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

Unity数组初始长度7运行时变0触发索引越界异常排查

问题根因
  • 核心逻辑错误:PlayerMovement 继承了GameRules,但两个脚本作为独立组件挂载在场景不同对象上时,会各自持有一套独立的序列化字段(生成点数组、预制体引用等),数据完全不互通。
    • 挂载在全局管理器对象上的GameRules组件会正常执行自身Awake逻辑,通过FindGameObjectsWithTag获取到7个生成点,首次Start调用生成逻辑时日志正常输出数组长度为7。
    • 挂载在玩家对象上的PlayerMovement组件重写了Awake方法且没有调用base.Awake(),自身持有的两个生成点数组从未执行查找赋值逻辑,且被Unity序列化机制覆盖为Inspector中默认的空数组(长度0)。后续碰撞回调、死亡逻辑调用RandomLeft()/RandomRight()时,实际调用的是玩家身上PlayerMovement组件的方法,访问长度为0的数组直接触发越界报错。
  • 附带逻辑bug1:生成开关时将Instantiate返回的场景对象直接赋值给了预制体引用字段,会覆盖原有预制体引用,且仅单个字段无法存储3个生成的开关对象,调用销毁方法时只会删除最后生成的1个开关,剩余2个开关会永久残留在场景中。
  • 附带逻辑bug2:随机索引写法错误,Unity中Random.Range(int min, int max)为左闭右开区间,传入array.Length -1作为最大值时永远无法取到数组最后一个点位。
修复方案
  1. 取消不合理的继承关系:PlayerMovement不需要继承GameRules,将GameRules做成单例全局管理类,所有生成、销毁开关的逻辑统一通过单例调用,避免多实例数据不互通的问题。
    改造后的GameRules示例代码:
    using UnityEngine;
    using System.Collections.Generic;
    
    public class GameRules : MonoBehaviour
    {
        public static GameRules Instance;
        public GameObject[] rightSwitchSpawns;
        public GameObject[] leftSwitchSpawns;
    
        public GameObject rightSwitchPrefab;
        public GameObject leftSwitchPrefab;
    
        // 存储生成的开关,方便后续统一销毁
        private List<GameObject> _spawnedRightSwitches = new List<GameObject>();
        private List<GameObject> _spawnedLeftSwitches = new List<GameObject>();
        
        void Awake(){
            // 单例初始化
            Instance = this;
            rightSwitchSpawns =  GameObject.FindGameObjectsWithTag("RightSpawns");
            leftSwitchSpawns = GameObject.FindGameObjectsWithTag("LeftSpawns");
        }
    
        void Start()
        {
            RandomLeft();
            RandomRight();
        }
    
        public void RandomLeft()
        {   
            // 生成前先清理上一轮的开关
            DestroyLeftSwitches();
            Debug.Log("This Left array length is " + leftSwitchSpawns.Length);
            // 修正随机范围,直接传Length即可覆盖所有索引
            int leftRandom1 = Random.Range(0, leftSwitchSpawns.Length);
            Debug.Log("left 1 index is " + leftRandom1);
            Vector2 leftOne = leftSwitchSpawns[leftRandom1].transform.position;
            _spawnedLeftSwitches.Add(Instantiate(leftSwitchPrefab, leftOne, Quaternion.identity));
    
            int leftRandom2 = Random.Range(0, leftSwitchSpawns.Length);
            while(leftRandom2 == leftRandom1)
            {
                leftRandom2 = Random.Range(0, leftSwitchSpawns.Length);
            }
            Vector2 leftTwo = leftSwitchSpawns[leftRandom2].transform.position;
            _spawnedLeftSwitches.Add(Instantiate(leftSwitchPrefab, leftTwo, Quaternion.identity));
            Debug.Log("Left 2 index is: " + leftRandom2);
    
            int leftRandom3 = Random.Range(0, leftSwitchSpawns.Length);
            while(leftRandom3 == leftRandom1 || leftRandom3 == leftRandom2)
            {
                leftRandom3 = Random.Range(0, leftSwitchSpawns.Length);
            }
            Vector2 leftThree = leftSwitchSpawns[leftRandom3].transform.position;
            _spawnedLeftSwitches.Add(Instantiate(leftSwitchPrefab, leftThree, Quaternion.identity));
            Debug.Log("Left 3 index is: " + leftRandom3);
        }
    
        public void RandomRight()
        {
            // 生成前先清理上一轮的开关
            DestroyRightSwitches();
            Debug.Log("This Right array length is " + rightSwitchSpawns.Length);
            int rightRandom1 = Random.Range(0, rightSwitchSpawns.Length);
            Vector2 rightOne = rightSwitchSpawns[rightRandom1].transform.position;
            _spawnedRightSwitches.Add(Instantiate(rightSwitchPrefab, rightOne, Quaternion.identity));
            Debug.Log("Right 1 index is: " + rightRandom1);
    
            int rightRandom2 = Random.Range(0, rightSwitchSpawns.Length);
            while (rightRandom2 == rightRandom1)
            {
                rightRandom2 = Random.Range(0, rightSwitchSpawns.Length);
            }
            Vector2 rightTwo = rightSwitchSpawns[rightRandom2].transform.position;
            _spawnedRightSwitches.Add(Instantiate(rightSwitchPrefab, rightTwo, Quaternion.identity));
            Debug.Log("Right 2 index is: " + rightRandom2);
    
            int rightRandom3 = Random.Range(0, rightSwitchSpawns.Length);
            while (rightRandom3 == rightRandom1 || rightRandom3 == rightRandom2)
            {
                rightRandom3 = Random.Range(0, rightSwitchSpawns.Length);
            }
            Vector2 rightThree = rightSwitchSpawns[rightRandom3].transform.position;
            _spawnedRightSwitches.Add(Instantiate(rightSwitchPrefab, rightThree, Quaternion.identity));
            Debug.Log("Right 3 index is: " + rightRandom3);
        }
    
        public void DestroyRightSwitches(){
            Debug.Log("Right Switches Destroyed");
            foreach(var sw in _spawnedRightSwitches){
                Destroy(sw);
            }
            _spawnedRightSwitches.Clear();
        }
    
        public void DestroyLeftSwitches(){
            Debug.Log("Left Switches Destroyed");
            foreach(var sw in _spawnedLeftSwitches){
                Destroy(sw);
            }
            _spawnedLeftSwitches.Clear();
        }
    }
    
  2. 改造PlayerMovement脚本,去掉对GameRules的继承,所有逻辑调用走GameRules.Instance单例,修改后的代码如下:
    using UnityEngine;
    
    public class PlayerMovement : MonoBehaviour
    {
        private float speed = 5f;
        private float jump = 5f;
        private Rigidbody2D rb;
        private bool isPlaying = false;
        private Transform trans;
    
        void Awake()
        {
            trans = gameObject.GetComponent<Transform>();
            rb = gameObject.GetComponent<Rigidbody2D>();
            rb.simulated = false;
        }
    
        void Update()
        {
            if(Input.GetMouseButtonDown(0)){
                if(isPlaying == false){
                    isPlaying = true;
                    rb.simulated = true;
                    rb.velocity = new Vector2(speed,0f); 
                }
                else{
                    rb.velocity = new Vector2(speed,jump);
                }
            }
        }
        void OnCollisionEnter2D(Collision2D collision)
        {
            if (collision.gameObject.tag == "Wall")
            {
               OnDeath();
            }
    
            if(collision.gameObject.tag == "RightSwitch"){
                speed = -speed;
                rb.velocity = new Vector2(speed,0f); 
                GameRules.Instance.RandomRight();
            }
             if(collision.gameObject.tag == "LeftSwitch"){
                speed = -speed;
                rb.velocity = new Vector2(speed,0f); 
                GameRules.Instance.RandomLeft();
            }
        }
    
        void OnDeath(){
            isPlaying = false;
            rb.simulated = false;
            trans.position = new Vector2(0,0);
            GameRules.Instance.RandomLeft();
            GameRules.Instance.RandomRight();
        }
    }
    
  3. 检查场景中静态生成点的标签设置,确保所有左右生成点分别挂载LeftSpawns、RightSpawns标签,且不会被运行时逻辑误销毁。

内容的提问来源于stack exchange,提问作者Damion Russell

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 18:48:54