如何修复Unity中List的ArgumentOutOfRangeException异常?
问题根源分析
你怀疑的点完全正确:协程bubbleSignal执行时,先等待了0.1-1.5秒,这段时间内目标气泡可能已经移出碰撞体,被OnTriggerExit2D从bubbleOnes列表中移除。同时原代码的随机索引计算还有错误:
- Unity的
Random.Range(int min, int max)是左闭右开区间,Random.Range(0, bubbleOnes.Count -1)会漏掉最后一个元素的索引;当列表长度变为1时,bubbleOnes.Count -1等于0,此时调用Random.Range(0,0)会触发参数错误,直接导致索引越界。
修复方案
针对这两个问题,我们可以通过以下步骤解决:
- 修正随机索引逻辑:使用
Random.Range(0, bubbleOnes.Count)来覆盖所有有效索引。 - 避免索引失效:在等待前先选中目标对象(而非索引),等待完成后检查该对象是否仍在列表中,确保操作的对象是有效的。
- 空列表防护:在协程开始和等待后都检查列表状态,避免对空列表进行操作。
修复后的完整代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class BubbleScriptNecroGrab : MonoBehaviour { [SerializeField] private List<GameObject> bubbleOnes = new List<GameObject>(); [SerializeField] private List<GameObject> necroAcolytes = new List<GameObject>(); [SerializeField] private GameObject activeBubble; [SerializeField] private GameObject necroAcolyte; private bool necroReady = true; void Update() { if (bubbleOnes.Count > 0 && necroReady) { StartCoroutine(bubbleSignal()); } } private void OnTriggerEnter2D(Collider2D collision) { if (collision.gameObject.CompareTag("Bubble1")) { GameObject targetBubble = collision.gameObject; // 避免重复添加同一个对象 if (!bubbleOnes.Contains(targetBubble)) { bubbleOnes.Add(targetBubble); } } } private void OnTriggerExit2D(Collider2D collision) { if (collision.gameObject.CompareTag("Bubble1")) { GameObject targetBubble = collision.gameObject; bubbleOnes.Remove(targetBubble); } } IEnumerator bubbleSignal() { necroReady = false; // 启动协程时先检查列表是否为空 if (bubbleOnes.Count == 0) { necroReady = true; yield break; } // 等待前先锁定目标对象,而非索引 GameObject targetBubble = bubbleOnes[Random.Range(0, bubbleOnes.Count)]; yield return new WaitForSeconds(Random.Range(0.1f, 1.5f)); // 等待后检查目标是否仍在列表中(未被移除) if (bubbleOnes.Contains(targetBubble)) { Vector3 targetPos = targetBubble.transform.position; Destroy(targetBubble); // 主动从列表移除已销毁的对象,避免残留无效引用 bubbleOnes.Remove(targetBubble); GameObject acolyteClone = Instantiate(necroAcolyte, targetPos, Quaternion.identity); acolyteClone.GetComponent<AcolyteScript>().activeTarget = transform.parent.gameObject; } necroReady = true; } }
额外优化点:
- 使用
CompareTag替代直接比较tag,效率更高且避免拼写错误。 - 添加重复添加防护,防止同一个气泡多次进入触发器时被重复加入列表。
- 销毁对象后主动从列表移除,避免列表中残留已销毁的无效引用。
内容的提问来源于stack exchange,提问作者ssnssnss
相关产品推荐
相关产品推荐

