Unity List能否存储重复元素?AI空闲状态切换死循环排查
核心问题答疑
- C#
List<T>天然支持重复元素存储:枚举属于值类型,同值的枚举项重复存入列表完全符合语法规则,不会抛出任何异常,也不存在存储层面的限制。 - 不需要补充完整列表控制脚本:当前给出的代码片段已经可以100%定位死循环根因,问题和未贴出的其余状态分支逻辑无关。
死循环根因
原状态切换逻辑使用FindIndex查找当前状态位置,该方法永远返回列表中第一个匹配目标值的元素索引,一旦列表存在重复状态,就会触发索引回跳:
举个实际触发例子:假设配置列表顺序为
[BasicIdle, Curious, Bored, Curious, Shy]
- 执行到索引3的第二个
Curious,计时结束后FindIndex查找Curious只会返回第一个匹配项的索引1,+1后得到索引2,切换到Bored- 索引2的
Bored计时结束后,FindIndex查找Bored返回索引2,+1得到索引3,又切回第二个Curious- 两个状态反复跳转,永远无法执行到索引4的
Shy,形成无限循环。
修复方案
放弃每次切换时从头查找状态位置的逻辑,新增一个整型变量持久记录当前执行到的列表索引即可,所有原有状态逻辑、changeState方法都不需要改动:
- 类中新增索引记录字段:
// 记录当前执行到的状态列表索引,初始值对应列表第一个元素 private int currentStateIndex = 0;
- 替换
FixedUpdate中原有的索引查找逻辑:
private void FixedUpdate() { if (Time.timeSinceLevelLoad > prevIdleStateChangeTime + currentStateDuration) { // 直接递增索引,不需要重新查找匹配 currentStateIndex++; if (currentStateIndex >= availableIdleStates.Count) { currentStateIndex = 0; // 此处保留原有列表洗牌的TODO逻辑即可 } changeState(availableIdleStates[currentStateIndex]); } // 下方原有switch状态分支代码完全不需要修改 switch (currentIdleState) { case IdleState.BasicIdle: if (Time.timeSinceLevelLoad > subStateChangeTime + subStateDuration) { subStateDuration = Random.Range(20f, 30f); Debug.Log(subStateDuration); int randInt = Random.Range(0, 1); subStateChangeTime = Time.timeSinceLevelLoad; switch (randInt) { case 0: CurrentMovingState = Moving.MovingState.MoveAndRotateToTarget; return; case 1: CurrentMovingState = Moving.MovingState.MoveAndRotateToTarget; return; } } return; // 其余未贴出的状态分支保持原有写法即可 } }
- 该方案完全兼容Inspector中任意顺序、包含任意重复元素的状态配置,不会再出现索引回跳问题
- 如果需要运行时动态增删
availableIdleStates的元素,只需要在修改列表后校验currentStateIndex小于列表当前长度即可,避免索引越界。
内容的提问来源于stack exchange,提问作者chana banana
相关产品推荐
相关产品推荐

