bleat interteiment
bleat interteiment

Reputation: 1467

MissingReferenceException after destroying an object using a coroutine in Unity

void OnCollisionEnter(Collision collision)
{
    if (collision.gameObject.layer == layer)
    {
        StopAllCoroutines();
        Destroy(gameObject, 1f);
    }
}

//Coroutine is called from another script
public IEnumerator MoveRb(Vector3 destination)
{
    yield return new WaitUntil(() => rb.velocity == Vector3.zero);

    destination.y = rb.position.y;
    rb.velocity = transform.right;

    //this is where i get an error 
    yield return new WaitUntil(() => Vector3.Distance(rb.position, destination) < 0.1f); 

    rb.velocity = Vector3.zero;
}

Basically, getting "MissingReferenceException" when trying to destroy an object while running coroutine. Delay of 1 second, nor replacing "WaitUntil" with while loop and "yield return null" doesn't fix this issue. The only place where object gets destroyed is inside of "OnCollisionEnter" inside of the same gameObject script. What am I missing here?

Full exception message:

MissingReferenceException: The object of type 'Rigidbody' has been destroyed but you are still trying to access it. Your script should either check if it is null or you should not destroy the object.

Upvotes: 0

Views: 3674

Answers (2)

derHugo
derHugo

Reputation: 90852

As you posted on the other answer you actually run this routine from another script

public class ConveyourBelt : MonoBehaviour
{
    void OnCollisionEnter(Collision other)
    {
        if (other.gameObject.TryGetComponent(out MovingPart movingPart))
        {
            // runs this Coroutine on THIS component
            StartCoroutine(movingPart.MoveRb(transform.position + transform.right));
        }
    }
}

The issue here is that this ConveyourBelt component is running the Coroutine, not the MovingPart component attached to the other object

=> The call to

StopAllCoroutines();

in the MovingPart component has no effect at all since it is never running that routine!

So when you destroy the object after 1 second the routine could still be running on the ConveyourBelt component.


As a solution you should rather make the routine running on the other component like

public class ConveyourBelt : MonoBehaviour
{
    void OnCollisionEnter(Collision other)
    {
        if (other.gameObject.TryGetComponent<MovingPart>(out var movingPart))
        {
            // rather runs the Coroutine on the movingPart component
            movingPart.StartCoroutine(movingPart.MoveRb(transform.position + transform.right));
        }
    }
}

Upvotes: 1

anon
anon

Reputation:

The code should work in theory. I think the problem is you are trying to access the coroutine again during this 1 second destroy delay. This results in the coroutine running while the gameobject is destroyed.

If you want this 1s delay then either make sure that the coroutine can't get called again after you have collided with something or the easier way is to make the coroutine error proof. Means you check if the rigidbody is null before you use it.

Upvotes: 0

Related Questions