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

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);
    }
}
补充说明

数组不为空
数组非空截图


问题根源分析

  1. Template列表引用重复
    MazeLogic构造函数中,反复修改同一个Baze实例并添加到Template,导致列表所有元素指向同一对象,最终所有Baze的Start/End都被覆盖为最后一次循环的值(3和4),迷宫生成逻辑完全失效。

  2. Groups集合引用失效
    SetGroups方法中复用同一SortedSet实例,添加到Groups后立即Clear。由于List存储的是引用,Groups中所有集合都会变为空,后续SetVjoints处理空集合时会引发异常,或导致Vjoints数组被错误修改。

  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 00:45:52