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

Unity法术系统Update函数触发IndexOutOfRangeException错误求助

问题:法术槽自动填充时触发数组越界错误

开发带有4个法术槽的UI系统,设定玩家与法术书GameObject碰撞时,法术会被添加到SpellSystem脚本的unequippedSpells数组中。实现空法术槽自动填充未装备法术的功能时,触发如下错误:

IndexOutOfRangeException: Index was outside the bounds of the array.
SpellSystem.Update () (at Assets/Scripts/Spells/SpellSystem.cs:24)

错误指向Update函数中调用EquipSpell时访问unequippedSpells数组的代码行:

EquipSpell(unequippedSpells[unequippedSpells.Length - 1], spellSlots[equippedSpells.Length]);

核心错误原因

1. AddSpell方法未正确添加法术到数组

该方法仅扩容了unequippedSpells数组,但没有将传入的spell赋值到数组的新位置,导致数组元素为空,后续访问时可能触发异常:

public void AddSpell(Spell spell) {
    Array.Resize(ref unequippedSpells, unequippedSpells.Length + 1);
    // 缺失:将spell赋值到数组的最后一个索引位置
    spellEquipped = false;
}

2. EquipSpell方法逻辑完全错误

  • 条件判断反向:Array.IndexOf(equippedSpells, spell) != -1是检查法术是否已装备,但我们需要处理的是未装备法术,逻辑完全颠倒。
  • 数组索引越界:扩容equippedSpells后,最大索引是equippedSpells.Length - 1,但代码中使用equippedSpells.Length + 1赋值,直接触发越界。
  • 未正确移除未装备法术:仅通过Array.Resize截断数组,没有移除目标法术元素,导致数组数据混乱。

3. Update方法缺少数组安全检查

当unequippedSpells为空(长度为0)时,unequippedSpells.Length - 1等于-1,访问该索引直接触发越界错误,未提前判断数组是否有元素。

4. UnequipSpell方法存在同样的索引错误

与EquipSpell类似,赋值时使用unequippedSpells.Length + 1导致越界,且条件判断逻辑反向。


修复方案

1. 修复AddSpell方法

确保扩容后将法术赋值到数组的最后一个位置,同时处理数组初始为null的情况:

public void AddSpell(Spell spell) {
    if (unequippedSpells == null) {
        unequippedSpells = new Spell[1];
        unequippedSpells[0] = spell;
    } else {
        Array.Resize(ref unequippedSpells, unequippedSpells.Length + 1);
        unequippedSpells[unequippedSpells.Length - 1] = spell;
    }
    spellEquipped = false;
}

2. 修复Update方法

增加数组非空、长度检查,避免越界:

private void Update() {
    // 确保未装备列表有元素、还有空槽、且未在装备流程中
    if (unequippedSpells != null && unequippedSpells.Length > 0 
        && equippedSpells.Length < spellSlots.Length 
        && !spellEquipped) {
        
        int targetSlotIndex = equippedSpells.Length;
        // 检查目标槽是否存在且为空
        if (targetSlotIndex < spellSlots.Length 
            && !spellSlots[targetSlotIndex].GetComponent<SlotStat>().hasSpell) {
            
            EquipSpell(unequippedSpells[unequippedSpells.Length - 1], spellSlots[targetSlotIndex]);
            spellEquipped = true;
        }
    }
}

3. 修复EquipSpell方法

修正逻辑,将法术从未装备列表移到已装备列表,并更新UI:

public void EquipSpell(Spell spell, GameObject spellSlot) {
    // 找到法术在未装备列表中的索引
    int unequippedIndex = Array.IndexOf(unequippedSpells, spell);
    if (unequippedIndex == -1) return; // 法术不在未装备列表,直接返回

    // 添加到已装备列表
    if (equippedSpells == null) {
        equippedSpells = new Spell[1];
        equippedSpells[0] = spell;
    } else {
        Array.Resize(ref equippedSpells, equippedSpells.Length + 1);
        equippedSpells[equippedSpells.Length - 1] = spell;
    }

    // 从未装备列表移除目标法术
    Spell[] newUnequipped = new Spell[unequippedSpells.Length - 1];
    int newIndex = 0;
    for (int i = 0; i < unequippedSpells.Length; i++) {
        if (i != unequippedIndex) {
            newUnequipped[newIndex] = unequippedSpells[i];
            newIndex++;
        }
    }
    unequippedSpells = newUnequipped;

    // 更新法术槽UI
    GameObject labelObject = spellSlot.transform.GetChild(1).gameObject;
    TextMeshProUGUI label = labelObject.GetComponentInChildren<TextMeshProUGUI>();
    label.text = spell.spellData.spellName; // 建议使用ScriptableObject中的名字,而非GameObject名称
    spellSlot.GetComponent<SlotStat>().hasSpell = true;
}

4. 修复UnequipSpell方法

修正逻辑,将法术从已装备列表移到未装备列表:

public void UnequipSpell(Spell spell) {
    // 找到法术在已装备列表中的索引
    int equippedIndex = Array.IndexOf(equippedSpells, spell);
    if (equippedIndex == -1) return;

    // 添加到未装备列表
    if (unequippedSpells == null) {
        unequippedSpells = new Spell[1];
        unequippedSpells[0] = spell;
    } else {
        Array.Resize(ref unequippedSpells, unequippedSpells.Length + 1);
        unequippedSpells[unequippedSpells.Length - 1] = spell;
    }

    // 从已装备列表移除目标法术
    Spell[] newEquipped = new Spell[equippedSpells.Length - 1];
    int newIndex = 0;
    for (int i = 0; i < equippedSpells.Length; i++) {
        if (i != equippedIndex) {
            newEquipped[newIndex] = equippedSpells[i];
            newIndex++;
        }
    }
    equippedSpells = newEquipped;

    // 可添加清空对应法术槽UI的逻辑,比如设置hasSpell为false、清空标签
}

额外优化建议

  • 替换数组为List<Spell>:List的Add、Remove操作更便捷,无需手动处理扩容和索引,大幅降低越界风险,例如:public List<Spell> unequippedSpells = new List<Spell>();。
  • 在Unity Inspector中初始化数组/List:避免数组初始为null导致的空引用异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 05:12:38