Unity“仍在尝试访问GameObject”报错修复 动物觅食饮水模拟场景问题
问题根因
报错核心是多只动物并发抢占同一个资源触发空引用异常:多只动物会同时选中最近的同一个果实/湖泊,当其中一只动物到达后销毁了目标对象,其他还在移动途中的动物访问已销毁的对象就会触发报错。此外原有逻辑还存在单只动物重复触发寻路、移动步长计算错误、函数名误用的问题,优化方案如下:
具体修复步骤
- 首先给果实、湖泊对象添加一个标记脚本
ResourceObject.cs,用来标记资源是否被占用:
public class ResourceObject : MonoBehaviour { // 标记资源是否已经被某只动物占用 public bool isOccupied = false; }
- 修改你原有的动物控制脚本,优化资源抢占逻辑和空判断:
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
相关产品推荐
相关产品推荐

