You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加while循环后Unity持续冻结问题排查与解决求助

问题

我正在开发一款玩家猜测字母的游戏,生成玩家可选的字母选项时,需要检查该字母既未在场景中存在,也未被玩家使用过。原本一切正常,但添加以下while循环后,Unity开始持续冻结:

for (int i = 0; i < button4b_value.Length;)
{
    while (true)
    {
        int randomNum = UnityEngine.Random.Range(0, fourPointsLetters.Length);
        string currentRandomString = fourPointsLetters[randomNum];
        if (!usedLetters.Contains(currentRandomString))
        {
            if (!button4b_value.Contains(currentRandomString))
            {
                button4b_value[i] = currentRandomString;
                i++;
                break;
            }
        }
        else break;
    }
}

可以确定button4_value不可能包含fourPointsLetters的所有元素,因此这并非问题所在。我还添加了else语句进行测试(虽非预期逻辑),但Unity仍然冻结。请问可能的原因是什么?该如何解决?

相关代码如下:

1. 设置按钮值(从列表中随机选取值分配给按钮)

private void SetUpPyramidButtonsValues()
{
    selectedButton_value = null;
    string currentRandomFivePointString = fivePointsLetters[UnityEngine.Random.Range(0, fivePointsLetters.Length)];
    if (!usedLetters.Contains(currentRandomFivePointString))
    {
        button5b_value = currentRandomFivePointString;
    }

    for (int i = 0; i < button4b_value.Length;)
    {
        while (true)
        {
            int randomNum = UnityEngine.Random.Range(0, fourPointsLetters.Length);
            string currentRandomString = fourPointsLetters[randomNum];
            if (!usedLetters.Contains(currentRandomString))
            {
                if (!button4b_value.Contains(currentRandomString))
                {
                    button4b_value[i] = currentRandomString;
                    i++;
                    break;
                }
            }
            else break;
        }
    }

    for (int i = 0; i < button3b_value.Length;)
    {
        while (true)
        {
            int randomNum = UnityEngine.Random.Range(0, threePointsLetters.Length);
            string currentRandomString = threePointsLetters[randomNum];
            if (!usedLetters.Contains(currentRandomString))
            {
                if (!button3b_value.Contains(currentRandomString))
                {
                    button3b_value[i] = currentRandomString;
                    i++;
                    break;
                }
            }
        }
    }

    for (int i = 0; i < button2b_value.Length;)
    {
        while (true)
        {
            int randomNum = UnityEngine.Random.Range(0, twoPointsLetters.Length);
            string currentRandomString = twoPointsLetters[randomNum];
            if (!usedLetters.Contains(currentRandomString))
            {
                if (!button2b_value.Contains(currentRandomString))
                {
                    button2b_value[i] = currentRandomString;
                    i++;
                    break;
                }
            }
        }
    }
    Context.uIManager.UpdateAllPyramidaButtons();
}

2. 确认按钮(点击后重新生成值)

public void OnAcceptButton()
{
    CheckIfPlayerSelectRightButton();
    selectedButton_value = null;
    SetUpPyramidButtonsValues();
}

3. 检查玩家选中的值是否正确

if (answer_value.Contains(selectedButton_value))
{
    //Check if choosen letter is in the secretary
    string[] character = new string[answer_value.Length];
    for (int i = 0; i < answer_value.Length; i++)
    {
        character[i] = answer_value[i].ToString();
        if (character[i] == selectedButton_value)
        {
            AddItemToUsedLetters(selectedButton_value);
            skuska[i].enabled = true;
            //HERE ADD POINTS
            timeRemaining = 30f;
            timerIsRunning = true;
        }
    }
}
else
{
    timeRemaining = 30f;
    timerIsRunning = true;
}

分析与解决方案

冻结原因

  1. 4分按钮循环的死循环:当随机到的字母存在于usedLetters中时,代码执行else break;跳出内层while,但外层for循环的i并未递增,导致外层循环一直卡在当前索引,内层while重复执行,陷入死循环。
  2. 3/2分按钮循环的死循环:这两个循环没有处理字母在usedLetters中的情况,一旦随机到已使用的字母,内层while没有任何跳出条件,会无限循环直至Unity卡死。

修复方案

方案1:优化随机逻辑(推荐)

先筛选出所有未被使用的字母,再从中随机选取,彻底避免无效循环:

private void SetUpPyramidButtonsValues()
{
    selectedButton_value = null;

    // 处理5分按钮:先筛选可用字母再随机
    var availableFiveLetters = fivePointsLetters.Where(letter => !usedLetters.Contains(letter)).ToList();
    if (availableFiveLetters.Count > 0)
    {
        button5b_value = availableFiveLetters[UnityEngine.Random.Range(0, availableFiveLetters.Count)];
    }

    // 处理4分按钮
    var availableFourLetters = fourPointsLetters.Where(letter => !usedLetters.Contains(letter)).ToList();
    for (int i = 0; i < button4b_value.Length; i++)
    {
        if (availableFourLetters.Count == 0) break; // 极端情况兜底
        int randomIndex = UnityEngine.Random.Range(0, availableFourLetters.Count);
        string selectedLetter = availableFourLetters[randomIndex];
        button4b_value[i] = selectedLetter;
        availableFourLetters.RemoveAt(randomIndex); // 避免重复选取
    }

    // 处理3分按钮
    var availableThreeLetters = threePointsLetters.Where(letter => !usedLetters.Contains(letter)).ToList();
    for (int i = 0; i < button3b_value.Length; i++)
    {
        if (availableThreeLetters.Count == 0) break;
        int randomIndex = UnityEngine.Random.Range(0, availableThreeLetters.Count);
        string selectedLetter = availableThreeLetters[randomIndex];
        button3b_value[i] = selectedLetter;
        availableThreeLetters.RemoveAt(randomIndex);
    }

    // 处理2分按钮
    var availableTwoLetters = twoPointsLetters.Where(letter => !usedLetters.Contains(letter)).ToList();
    for (int i = 0; i < button2b_value.Length; i++)
    {
        if (availableTwoLetters.Count == 0) break;
        int randomIndex = UnityEngine.Random.Range(0, availableTwoLetters.Count);
        string selectedLetter = availableTwoLetters[randomIndex];
        button2b_value[i] = selectedLetter;
        availableTwoLetters.RemoveAt(randomIndex);
    }

    Context.uIManager.UpdateAllPyramidaButtons();
}

方案2:修复原循环结构

如果不想改动整体逻辑,针对每个循环的死循环问题逐个修复:

// 修复后的4分按钮循环
for (int i = 0; i < button4b_value.Length;)
{
    bool foundValid = false;
    while (!foundValid)
    {
        int randomNum = UnityEngine.Random.Range(0, fourPointsLetters.Length);
        string currentRandomString = fourPointsLetters[randomNum];
        if (!usedLetters.Contains(currentRandomString))
        {
            if (!button4b_value.Contains(currentRandomString))
            {
                button4b_value[i] = currentRandomString;
                i++;
                foundValid = true;
            }
        }
        // 字母已被使用时继续循环,不直接break
    }
}

// 修复后的3分按钮循环
for (int i = 0; i < button3b_value.Length;)
{
    bool foundValid = false;
    while (!foundValid)
    {
        int randomNum = UnityEngine.Random.Range(0, threePointsLetters.Length);
        string currentRandomString = threePointsLetters[randomNum];
        if (!usedLetters.Contains(currentRandomString))
        {
            if (!button3b_value.Contains(currentRandomString))
            {
                button3b_value[i] = currentRandomString;
                i++;
                foundValid = true;
            }
        }
    }
}

// 2分按钮循环修复逻辑与3分一致

额外优化建议

  • 将usedLetters改为HashSet<string>,Contains方法的查询效率会从O(n)降至O(1),尤其当字母数量较多时提升明显。
  • 为所有循环添加最大尝试次数限制(比如循环100次未找到有效字母就跳出),防止极端情况下的死循环。

内容的提问来源于stack exchange,提问作者V1lko

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 00:45:36