添加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; }
分析与解决方案
冻结原因
- 4分按钮循环的死循环:当随机到的字母存在于
usedLetters中时,代码执行else break;跳出内层while,但外层for循环的i并未递增,导致外层循环一直卡在当前索引,内层while重复执行,陷入死循环。 - 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
相关产品推荐
相关产品推荐

