【问题标题】:Unity cant figure out how to fix the "still trying to access gameobject" errorUnity 无法弄清楚如何修复“仍在尝试访问游戏对象”错误
【发布时间】:2021-10-25 16:45:59
【问题描述】:

所以我正在编写代码来模拟动物如何在地图周围吃食物和喝水,但我一直收到错误消息。我相信这是因为多种动物正试图进入一个湖泊或一种水果,但我不知道如何避免这种情况发生。 所以这是我的代码,请帮忙(我知道我的编码技能很糟糕,但我不在乎)

public float hunger = 100;
public float speed;
public float thirst = 100;

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

// Update is called once per frame
void Update()
{

    if (hunger <= 20 && hunger < thirst)
    {
        gotoBush();
    }
    if (thirst <= 20 && thirst < hunger)
    {
        gotoLake();
    }
    if (hunger <= 0)
    {
        Destroy(gameObject);
    }
}
IEnumerator hungerdrop()
{
    yield return new WaitForSeconds(0.2f);
    hunger -= 1;
    thirst -= 2;
    StartCoroutine(hungerdrop());
}
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"))
    {
        float dist = Vector3.Distance(t.transform.position, currentPos);
        if (dist < minDist)
        {
            tMin = t;
            minDist = dist;
        }
    }
    if (tMin != null)
    {
        float step = speed * Time.deltaTime;
        StartCoroutine(walkToBush());
        IEnumerator walkToBush()
        {
            if (tMin != null)
            {
                
                while (transform.position != tMin.transform.position && tMin != null)
                {
                    yield return new WaitForSeconds(0.1f);
                    transform.position = Vector3.MoveTowards(transform.position, tMin.transform.position, step);
                    


                }
                hunger = 100;
                StartCoroutine(bushEat());



            }
            IEnumerator bushEat()
            {
                yield return new WaitForSeconds(0.1f);
                destroyBush();

            }





        }
        void destroyBush()
        {
            Destroy(tMin);
        }






    }
    return tMin;







}
GameObject GetClosestLake()
{
    GameObject tMin = null;
    float minDist = Mathf.Infinity;
    Vector3 currentPos = transform.position;
    foreach (GameObject t in GameObject.FindGameObjectsWithTag("lake"))
    {
        float dist = Vector3.Distance(t.transform.position, currentPos);
        if (dist < minDist)
        {
            tMin = t;
            minDist = dist;
        }
    }
    if (tMin != null )
    {
        float step = speed * Time.deltaTime;
        StartCoroutine(walkToBush());
        IEnumerator walkToBush()
        
        {
            if (tMin != null)
            {
                
                while (transform.position != tMin.transform.position && tMin != null)
                {
                    yield return new WaitForSeconds(0.1f);
                    if(tMin != null)
                    {
                        transform.position = Vector3.MoveTowards(transform.position, tMin.transform.position, step);
                    }
                    
                    



                }
                thirst = 100;
                StartCoroutine(bushEat());



            }
        
            IEnumerator bushEat()
            {
                yield return new WaitForSeconds(0.1f);
                destroyBush();

            }





        }
        void destroyBush()
        {
            Destroy(tMin);
        }






    }
    return tMin;



}

}

【问题讨论】:

  • 在你已经销毁游戏对象之后,听起来public voids 之一被另一个脚本调用了......
  • 是的,但我不知道如何解决它
  • 好吧,不要在已经销毁的对象上调用该方法...
  • 所有的动物都使用相同的脚本,所以我认为这是不可能的。

标签: c# unity3d


【解决方案1】:

首先:您应该在协程中使用 while 循环,而不是像这样的递归:

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

第二:要检查已经被销毁的游戏对象,您可以简单地使用 if 语句,例如:

if(gameObject != null)
{
   //do something
}

【讨论】:

  • 我已经在代码中检查了很多次关于游戏对象已被销毁但不起作用的信息。
  • 哪一行代码抛出异常?双击统一控制台中的错误信息即可看到。
  • 异常在第 150 行
  • 我找不到问题,但我建议你清理你的代码(使用 reagions clear 变量名和 cmets)。当我被卡住时,它通常会有很大帮助。而且,你不必多次测试null,只需一次,也可以尝试使用Debug.Log("")-s
  • 真是烦人,我还是找不到问题。
【解决方案2】:

如果我理解正确,您有多个动物实例都在运行这个组件。

我在这里看到两个大问题:

  • 您可能有并发协程,因为您每帧开始一个新例程,而您的条件是true!这甚至可能意味着您尝试朝两个相反的方向移动 => 您永远无法达到目标。
  • 您的多只动物可以瞄准同一个资源 => 当一只仍在向它移动时,另一只可能已经吃掉并摧毁了它。

然后很可能会出现这个问题,您获得职位后检查资源的活动状态

while (transform.position != tMin.transform.position && tMin != null)

你应该检查的地方

while (tMin && transform.position != tMin.transform.position)

但是,即使tMin 不再存在,这仍然不会阻止执行while 之后的其余代码!


我宁愿做的是

  • 确保一次只运行一个协程
  • 确保一次只有一只动物可以瞄准资源

因此,我宁愿将两个实际组件附加到具有公共基类的资源上,而不是使用标签:

public abstract class ConsumableResource : MonoBehaviour
{
    public bool isOccupied;
}

然后这两个进入相应的资源GameObjects:

public class Fruit : ConsumableResource
{ 
    // Doesn't have to do anything else .. but could
}

public class Lake : ConsumableResource 
{
    // Doesn't have to do anything else .. but could
    // You could e.g. consider to add a counter so that multiple animals can target this at a time
    // and/or add another counter so multiple animals can consume this resource before it is destroyed 
}

然后我会做

public class Animal : MonoBehaviour
{
    public float hunger = 100;
    public float speed;
    public float thirst = 100;

    // the currently executed coroutine
    private Coroutine _currentRoutine;

    // the currently targeted resource instance
    private ConsumableResource _currentTargetResource;

    // Update is called once per frame
    private void Update()
    {
        // first of all I would rather drop the hunger and thirst like this
        // This now means hunger drops 5 units per second and thirst drops 10 units per second
        // What you had before was a complex way for writing basically the same
        hunger -= 5f * Time.deltaTime;
        thirst -= 10f * Time.deltaTime;

        if (hunger <= 0)
        {
            Destroy(gameObject);
        }

        // In order to no end up with multiple concurrent routines check if there is already a routine running first
        if (_currentRoutine == null)
        {
            if (hunger <= 20 && hunger < thirst)
            {
                // start our resource routine targeting the type Fruit and if we succeed to run the entire routine to and 
                // then call WhenConsumedFruit afterwards to reset the hunger
                _currentRoutine = StartCoroutine(GetResourceRoutine<Fruit>(WhenConsumedFruit));
            }
            // make your cases exclusive!
            else if (thirst <= 20 && thirst < hunger)
            {
                _currentRoutine = StartCoroutine(GetResourceRoutine<Lake>(WhenConsumedLake));
            }
        }
    }

    private void WhenConsumedFruit()
    {
        hunger = 100;
    }

    private void WhenConsumedLake()
    {
        thirst = 100;
    }

    // Use a generic method to not repeat the same implementation 
    // This now can be used to get the closest of any inherited type from ConsumableResource
    private T FindClosestResource<T>() where T : ConsumableResource
    {
        // First get all instances in the scene of given resource type
        var allFruits = FindObjectsOfType<T>();
        // filter out those that are already occupied by another animal instance
        var onlyNotOccupiedFruits = allFruits.Where(fruit => !fruit.isOccupied);
        // sort the remaining instances by distance
        var sortedByDistance = onlyNotOccupiedFruits.OrderBy(fruit => (fruit.transform.position - transform.position).sqrMagnitude);
        // take the first one (= closest) or null if there was no remaining instance
        return sortedByDistance.FirstOrDefault();
    }

    // Again use the most generic base class so you can reuse the same code for any inherited type of ConsumableResource
    IEnumerator MoveTowardsResource(ConsumableResource target)
    {
        while (transform.position != target.transform.position)
        {
            transform.position = Vector3.MoveTowards(transform.position, target.transform.position, speed * Time.deltaTime);
            yield return null;
        }

        transform.position = target.transform.position;
    }

    // and again do his only once
    IEnumerator ConsumeResource(ConsumableResource target)
    {
        yield return new WaitForSeconds(0.1f);
        // You might want to consider to rather move this into the resource itself
        // so it could extend the behavior as said e.g. using counters
        // without the need that your animal class has to be aware of what exactly
        // this means for the resource. Maybe it doesn't destroy it but only disable
        // for some time so it can grow back => unlimmitted options ;) 
        Destroy(target.gameObject);
    }

    private IEnumerator GetResourceRoutine<T>(Action whenConsumed) where T : ConsumableResource
    {
        _currentTargetResource = FindClosestResource<T>();

        // did we find a closest fruit?
        if (!_currentTargetResource)
        {
            // if not terminate this routine and allow the next one to start
            _currentRoutine = null;
            yield break;
        }

        // set closest fruit occupied so no animal can take it anymore
        _currentTargetResource.isOccupied = true;

        // Move towards the closest fruit
        yield return MoveTowardsResource(_currentTargetResource);

        // "Eat" the fruit
        yield return ConsumeResource(_currentTargetResource);

        // when all is done without this object dying before simply invoked the passed callback action
        whenConsumed?.Invoke();

        // Allow the next routine to start
        _currentRoutine = null;
    }

    private void OnDestroy()
    {
        // in case we die for whatever reason make sure to release the occupied resources if there are any
        // so if you die on the way towards it at least from now on another animal can pick it again as target
        if (_currentTargetResource) _currentTargetResource.isOccupied = false;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-07
    • 1970-01-01
    • 2021-04-08
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    • 1970-01-01
    • 2018-08-15
    相关资源
    最近更新 更多