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

Unity3D脚本列表越界问题:无法实现类Minecraft方块销毁逻辑

方块销毁功能的索引越界问题修复

问题背景

需求:实现类似Minecraft的方块销毁逻辑——碰撞体触碰到目标物体且点击左键时,销毁列表首位的物体;但触碰到WorldMap、Water、Berries三类物体时,不执行销毁操作。

报错情况:当碰撞体触碰到上述三类物体时,触发索引越界错误:

Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index

原脚本代码:

using System.Collections.Generic;
using System.Collections;
using UnityEngine;

public class GetTarget : MonoBehaviour
{
    [Header("Координаты блока")] // block positions
    public int positionX;
    public int positionY;
    public int positionZ;

    [Header("Получение цели")] // set list for block to destroy
    public static List<GameObject> target = new List<GameObject>();

    private void OnTriggerStay(Collider collider)
    {
        target.Insert(0, collider.gameObject);

        if(target[0].name == "WorldMap") // can't destroy world map
        {
            if(target.Count != 0)
            {
                Debug.Log("Вы не можете получить " + target[0]); // you can't get (test message)
                target.Remove(collider.gameObject);  
            }
        }
        if(target[0].name == "Water") // you can't destroy water in structure
        {
            if(target.Count != 0)
            {
                Debug.Log("Вы не можете получить " + target[0]); // you can't get (test message)
                target.Remove(collider.gameObject);  
            }
        }
        if(target[0].name == "Berries") // you can't destroy berries
        {
            if(target.Count != 0)
            {
                Debug.Log("Вы не можете получить " + target[0]); // you can't get (test message)
                target.Remove(collider.gameObject);  
            }
        }
    }

    private void Update() // i get position for collider
    {
        Vector3 positions = transform.position;

        positionX = (int)Mathf.Round(gameObject.transform.position.x);
        positionY = (int)Mathf.Round(gameObject.transform.position.y);
        positionZ = (int)Mathf.Round(gameObject.transform.position.z);

// set position for collider - positions = transfrom.position

        positions.x = positionX;
        positions.y = positionY;
        positions.z = positionZ;
    }
}

问题分析与修复方案

核心问题点

  1. 索引越界根源:OnTriggerStay每帧调用,先将碰撞体插入列表首位,判断后移除该物体。如果移除后列表为空,后续的if(target[0].name)判断会直接访问空列表的索引0,触发错误。
  2. 列表逻辑混乱:未处理重复插入同一物体的情况,且没有在碰撞体离开时移除对应物体,导致列表数据不准确。

修复步骤

  1. 提前过滤禁止销毁的物体:在插入列表前先判断当前碰撞体是否属于禁止类型,直接跳过插入,避免后续无效的列表操作。
  2. 简化判断逻辑:用HashSet存储禁止销毁的物体名称,提升判断效率,减少重复代码。
  3. 添加空列表检查:所有访问target[0]的操作前,先确认列表不为空。
  4. 补充OnTriggerExit逻辑:当碰撞体离开时,从列表中移除对应物体,保证列表数据的准确性。

修复后的完整代码

using System.Collections.Generic;
using UnityEngine;

public class GetTarget : MonoBehaviour
{
    [Header("方块坐标")]
    public int positionX;
    public int positionY;
    public int positionZ;

    [Header("销毁目标列表")]
    public static List<GameObject> target = new List<GameObject>();
    
    // 存储禁止销毁的物体名称集合
    private readonly HashSet<string> _forbiddenObjects = new HashSet<string> {"WorldMap", "Water", "Berries"};

    private void OnTriggerStay(Collider collider)
    {
        GameObject currentObj = collider.gameObject;
        
        // 如果是禁止销毁的物体,直接提示并返回
        if (_forbiddenObjects.Contains(currentObj.name))
        {
            Debug.Log("无法选中该物体:" + currentObj.name);
            return;
        }
        
        // 避免重复插入同一物体
        if (!target.Contains(currentObj))
        {
            target.Insert(0, currentObj);
        }
    }

    private void OnTriggerExit(Collider collider)
    {
        // 碰撞体离开时,从列表中移除对应物体
        if (target.Contains(collider.gameObject))
        {
            target.Remove(collider.gameObject);
        }
    }

    private void Update()
    {
        Vector3 positions = transform.position;

        positionX = (int)Mathf.Round(transform.position.x);
        positionY = (int)Mathf.Round(transform.position.y);
        positionZ = (int)Mathf.Round(transform.position.z);

        positions.x = positionX;
        positions.y = positionY;
        positions.z = positionZ;
        
        // 处理左键点击销毁逻辑
        if (Input.GetMouseButtonDown(0) && target.Count > 0)
        {
            Destroy(target[0]);
            target.RemoveAt(0);
        }
    }
}

额外说明

  • 补充了原需求中缺失的左键点击销毁逻辑,确保点击时先检查列表不为空再操作。
  • 使用HashSet存储禁止名称,比多次字符串相等判断更高效。
  • 添加OnTriggerExit后,列表只会保留当前处于碰撞范围内的有效物体,避免无效数据积累。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 03:01:08