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
相关产品推荐
相关产品推荐

