Unity中IndexOutOfRangeException异常咨询:数组索引越界但逻辑正确?
错误信息
IndexOutOfRangeException: 索引超出数组范围。
RowManager.GenerateRow (System.Single hbias, System.Single vbias) (位于 Assets/Scripts/RowManager.cs:155) GameManager.Update () (位于 Assets/Scripts/GameManager.cs:38)
- 数组包含5个元素,使用0到4的索引访问
- Unity版本:2021.3.8f LTS
相关代码
using System.Collections; using System.Collections.Generic; using UnityEngine; class Baze { private byte Start,End; public Baze() { Start = 0; End= 0; } public void SetStart(byte s) { Start = s; } public void SetEnd(byte s) { End = s; } public byte GetStart() { return Start; } public byte GetEnd() { return End; } public void Correction() { if(Start>End) { byte temp = Start; Start = End; End = temp; } } public bool isSingular() { return Start == End; } } class MazeLogic { private List<SortedSet<byte>> Groups; private SortedSet<byte> TempSet; private Baze TempRange; private List<Baze> Template; private uint[] IDs; private bool[] Hjoints, Vjoints; public MazeLogic() { Groups = new List<SortedSet<byte>>(); TempRange = new Baze(); IDs = new uint[5] {1,2,3,4,5}; Hjoints = new bool[5] {false,false,false,false,false}; Vjoints = new bool[5] {true,true,true,true,true}; TempSet = new SortedSet<byte>(); Template = new List<Baze>(); for(byte i=0;i<4;i++) { TempRange.SetStart(i); TempRange.SetEnd((byte)(i + 1)); Template.Add(TempRange); } } private void SetGroups(float bias) { uint temp = 0; for(byte i=0;i<5;i++) { Hjoints[i]= false; if (Vjoints[i]) temp = IDs[i]; else IDs[i] = 0; } for (byte i = 0; i < 5; i++) if (IDs[i] == 0) IDs[i] = ++temp; TempRange.SetStart(0); for(byte i=0;i<4;i++) { if (Random.value > bias && IDs[Template[i].GetStart()] != IDs[Template[i].GetEnd()]) { TempSet.Add(Template[i].GetStart()); TempSet.Add(Template[i].GetEnd()); } else { TempSet.Add(Template[i].GetStart()); Groups.Add(TempSet); TempSet.Clear(); } } for (byte i = 0; i < Groups.Count; i++) for (byte j = Groups[i].Min; j < Groups[i].Max; j++) Hjoints[j] = true; } private void SetVjoints(float bias) { bool check; for (byte i = 0; i < Groups.Count; i++) { check = false; for (byte j = Groups[i].Min; j <= Groups[i].Max; j++) if(Random.value>bias) { check = true; Vjoints[j] = true; } if (!check) Vjoints[Random.Range(Groups[i].Min, Groups[i].Max + 1)] = true; } } public void GenerateMaze(float hbias,float vbias) { SetGroups(hbias); SetVjoints(vbias); } public bool[] GetHorizontalJoints() { return Hjoints; } public bool[] GetVerticalJoints() { return Vjoints; } } public class RowManager : MonoBehaviour { [SerializeField] GameObject[] Hwalls; [SerializeField] GameObject[] Vwalls; MazeLogic ml; void Awake() { ml = new MazeLogic(); } void Start() { Hwalls = new GameObject[4]; Vwalls = new GameObject[5]; for (byte i = 0; i < 4; i++) Hwalls[i] = transform.GetChild(i).gameObject; for (byte i = 0; i < 5; i++) Vwalls[i] = transform.GetChild((byte)(i+4)).gameObject; } public void GenerateRow(float hbias,float vbias) { ml.GenerateMaze(hbias,vbias); for (byte i = 0; i < 4; i++) if (ml.GetHorizontalJoints()[i]) Hwalls[i].SetActive(false); for(byte i=0;i<5;i++) if (ml.GetVerticalJoints()[i]) Vwalls[i].SetActive(false);//line 155 } public void Reset() { for (byte i = 0; i < 4; i++) Hwalls[i].SetActive(true); for (byte i = 0; i < 5; i++) Vwalls[i].SetActive(true); } }
补充说明
数组不为空
问题根源分析
Template列表引用重复
MazeLogic构造函数中,反复修改同一个Baze实例并添加到Template,导致列表所有元素指向同一对象,最终所有Baze的Start/End都被覆盖为最后一次循环的值(3和4),迷宫生成逻辑完全失效。Groups集合引用失效
SetGroups方法中复用同一SortedSet实例,添加到Groups后立即Clear。由于List存储的是引用,Groups中所有集合都会变为空,后续SetVjoints处理空集合时会引发异常,或导致Vjoints数组被错误修改。Vwalls初始化时机错误
若GenerateRow在RowManager.Start执行前被调用(比如动态实例化后立即调用),此时Vwalls仍是Inspector中设置的原始数组,若数组长度不足5,访问索引4时会触发IndexOutOfRangeException。
修复方案
1. 修复Template初始化
循环中创建新的Baze实例,避免引用重复:
public MazeLogic() { // ... 其他初始化代码 Template = new List<Baze>(); for(byte i=0;i<4;i++) { Baze tempBaze = new Baze(); tempBaze.SetStart(i); tempBaze.SetEnd((byte)(i + 1)); Template.Add(tempBaze); } }
2. 修复Groups集合引用问题
每次添加到Groups时创建新的SortedSet,避免复用导致的引用失效:
private void SetGroups(float bias) { // ... 其他代码 TempRange.SetStart(0); for(byte i=0;i<4;i++) { if (Random.value > bias && IDs[Template[i].GetStart()] != IDs[Template[i].GetEnd()]) { TempSet.Add(Template[i].GetStart()); TempSet.Add(Template[i].GetEnd()); } else { SortedSet<byte> newSet = new SortedSet<byte>(TempSet); newSet.Add(Template[i].GetStart()); Groups.Add(newSet); TempSet.Clear(); } } // 循环结束后添加剩余元素 if(TempSet.Count > 0) { Groups.Add(new SortedSet<byte>(TempSet)); TempSet.Clear(); } // ... 后续代码 }
3. 确保Vwalls初始化完成后调用GenerateRow
- 静态场景对象:将
GenerateRow调用移至RowManager.Start方法,或在GameManager中延迟一帧调用。 - 动态实例化对象:通过协程延迟调用:
// GameManager中实例化RowManager后 StartCoroutine(CallGenerateRowAfterFrame(rowManager, hbias, vbias)); IEnumerator CallGenerateRowAfterFrame(RowManager rm, float h, float v) { yield return null; // 等待一帧确保Start执行完毕 rm.GenerateRow(h, v); }
额外优化:用结构体替代Baze类
避免引用问题,简化代码:
struct Baze { public byte Start; public byte End; public Baze(byte start, byte end) { Start = start; End = end; if(Start > End) (Start, End) = (End, Start); } public bool IsSingular() => Start == End; } // 构造函数中初始化Template Template = new List<Baze>(); for(byte i=0;i<4;i++) { Template.Add(new Baze(i, (byte)(i+1))); }
内容的提问来源于stack exchange,提问作者user19938171
相关产品推荐
相关产品推荐

