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

Unity“仍在尝试访问GameObject”报错修复 动物觅食饮水模拟场景问题

问题根因

报错核心是多只动物并发抢占同一个资源触发空引用异常:多只动物会同时选中最近的同一个果实/湖泊,当其中一只动物到达后销毁了目标对象,其他还在移动途中的动物访问已销毁的对象就会触发报错。此外原有逻辑还存在单只动物重复触发寻路、移动步长计算错误、函数名误用的问题,优化方案如下:

具体修复步骤

  1. 首先给果实、湖泊对象添加一个标记脚本ResourceObject.cs,用来标记资源是否被占用:
public class ResourceObject : MonoBehaviour
{
    // 标记资源是否已经被某只动物占用
    public bool isOccupied = false;
}
  1. 修改你原有的动物控制脚本,优化资源抢占逻辑和空判断:
public float hunger = 100;
public float speed;
public float thirst = 100;

// 存储当前正在运行的寻路协程,方便终止旧的寻路任务
private Coroutine currentMoveCoroutine;

// Start is called before the first frame update
void Start()
{
    StartCoroutine(hungerdrop());
}

// Update is called once per frame
void Update()
{
    // 只有当前没有在执行寻路任务的时候才判断是否需要找食物/水
    if (currentMoveCoroutine == null)
    {
        if (hunger <= 20 && hunger < thirst)
        {
            gotoBush();
        }
        if (thirst <= 20 && thirst < hunger)
        {
            gotoLake();
        }
    }
    
    if (hunger <= 0)
    {
        Destroy(gameObject);
    }
}

IEnumerator hungerdrop()
{
    while (true)
    {
        yield return new WaitForSeconds(0.2f);
        hunger -= 1;
        thirst -= 2;
    }
}

public void gotoBush()
{
    GetClosestFruit();
}

public void gotoLake()
{
    GetClosestLake();
}

GameObject GetClosestFruit()
{
    GameObject tMin = null;
    float minDist = Mathf.Infinity;
    Vector3 currentPos = transform.position;
    foreach (GameObject t in GameObject.FindGameObjectsWithTag("fruit"))
    {
        // 跳过已经被其他动物占用的果实
        ResourceObject res = t.GetComponent<ResourceObject>();
        if (res == null || res.isOccupied) continue;
        
        float dist = Vector3.Distance(t.transform.position, currentPos);
        if (dist < minDist)
        {
            tMin = t;
            minDist = dist;
        }
    }
    if (tMin != null)
    {
        // 占用当前选中的果实
        tMin.GetComponent<ResourceObject>().isOccupied = true;
        // 启动寻路协程并存储引用
        currentMoveCoroutine = StartCoroutine(walkToFruit(tMin));
    }
    return tMin;
}

IEnumerator walkToFruit(GameObject targetFruit)
{
    while (targetFruit != null && Vector3.Distance(transform.position, targetFruit.transform.position) > 0.1f)
    {
        yield return new WaitForSeconds(0.1f);
        // 每轮移动重新计算步长,适配speed动态变化的情况
        float step = speed * Time.deltaTime * 10; // 乘以10是适配0.1秒的等待间隔
        if (targetFruit != null)
        {
            transform.position = Vector3.MoveTowards(transform.position, targetFruit.transform.position, step);
        }
        else
        {
            // 目标被意外销毁,直接终止寻路
            currentMoveCoroutine = null;
            yield break;
        }
    }

    if (targetFruit != null)
    {
        hunger = 100;
        yield return new WaitForSeconds(0.1f);
        Destroy(targetFruit);
    }
    // 寻路结束,清空协程标记
    currentMoveCoroutine = null;
}

GameObject GetClosestLake()
{
    GameObject tMin = null;
    float minDist = Mathf.Infinity;
    Vector3 currentPos = transform.position;
    foreach (GameObject t in GameObject.FindGameObjectsWithTag("lake"))
    {
        // 跳过已经被其他动物占用的湖泊
        ResourceObject res = t.GetComponent<ResourceObject>();
        if (res == null || res.isOccupied) continue;
        
        float dist = Vector3.Distance(t.transform.position, currentPos);
        if (dist < minDist)
        {
            tMin = t;
            minDist = dist;
        }
    }
    if (tMin != null)
    {
        // 占用当前选中的湖泊
        tMin.GetComponent<ResourceObject>().isOccupied = true;
        // 启动寻路协程并存储引用
        currentMoveCoroutine = StartCoroutine(walkToLake(tMin));
    }
    return tMin;
}

IEnumerator walkToLake(GameObject targetLake)
{
    while (targetLake != null && Vector3.Distance(transform.position, targetLake.transform.position) > 0.1f)
    {
        yield return new WaitForSeconds(0.1f);
        float step = speed * Time.deltaTime * 10;
        if (targetLake != null)
        {
            transform.position = Vector3.MoveTowards(transform.position, targetLake.transform.position, step);
        }
        else
        {
            currentMoveCoroutine = null;
            yield break;
        }
    }

    if (targetLake != null)
    {
        thirst = 100;
        yield return new WaitForSeconds(0.1f);
        // 如果湖泊是不可销毁的公共资源,删掉下面这行Destroy,同时把isOccupied改回false即可支持轮流饮用
        Destroy(targetLake);
    }
    currentMoveCoroutine = null;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 14:48:01