Best code pattern for keeping references to Unity GameObjects that may be Destroyed

What is the best code pattern to use when keeping references to Unity GameObjects that may be Destroyed elsewhere in the code?

I’ve read that you should always destroy a GameObject then set any references to null at the same time but the problem is that a class may have a reference to a GameObject and be unaware that is has been destroyed.

The equality operator for a GameObject is overridden so that a Destroyed GameObject will return true when compared to null, but it is a bit cumbersome when iterating through a collection of GameObjects since it doesn’t seem possible to remove the deleted objects easily.

An example scenario: Whenever a Enemy with a collider comes into range of the player and causes OnTriggerEnter to be called on the player object, the player object keeps a reference to the Enemy to know how many Enemies are nearby so it doesn’t have to query the world every time it needs to perform some sort of logic involving them.

The problem is that one of these Enemies may have been Destroyed (by another player perhaps) which means the Enemy GameObject in the collection held by the player is now null.

It does not seem that it is possible to filter them out using an approach like this:

        ArrayList deletedObjects = new ArrayList();
        foreach (GameObject enemy in enemiesInRange)
        {
            if (enemy == null)
            {
                deletedObjects.Add(gameObject);
            }
        }
        foreach (GameObject deleted in deletedObjects)
        {
            enemiesInRange.Remove(deleted);
        }

C Sharp has WeakReference and maybe that could be used to implement something like .net - Is there a way to do a WeakList or WeakCollection (like WeakReference) in CLR? - Stack Overflow but it seems cumbersome compared to other languages.

I feel like I am missing something obvious. Any help is appreciated!

It is totally possible to filter them like that… but the trick is they are not destroyed until end of frame.

So if you do the Destroy(), then run your remove above, they won’t be removed.

Next frame they will be however.

I actually just use:

Enemies.RemoveAll( x => !x);

to purge dead enemies.

To see the timing, put this on a blank GameObject and press run:

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

public class Destroys : MonoBehaviour
{
    IEnumerator Start()
    {
        var L = new List<GameObject>();

        var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);

        L.Add(cube);

        Debug.Log(L.Count.ToString());

        yield return null;

        Destroy(cube);

        L.RemoveAll(x => !x);

        Debug.Log(L.Count.ToString());  // didn't remove it; it's not dead yet!

        yield return null;

        L.RemoveAll(x => !x);

        Debug.Log(L.Count.ToString());
    }
}

6447306--721752--Screen Shot 2020-10-22 at 2.36.22 PM.png

1 Like

Ah thanks for that! Super helpful. I was unsure if it was considered a good pattern to always sanitize collections of GameObjects which may have been Destroyed during the last frame before their use in the next frame.

Appreciate the quick reply and the illustrative example!

It’s a little piggish but I’ve been getting away with doing that MyList.RemoveAll( x => !x); every frame, usually at the start of any loop that iterates the collection. Seems to work fine on iOS and Android and every other target I’ve tried. I’m sure there’s some extra GC going on, but meh, not obviously noticeable.

My proximity buttons demo twin stick game does it… proximity_buttons is presently hosted at these locations:

https://bitbucket.org/kurtdekker/proximity_buttons

https://github.com/kurtdekker/proximity_buttons

Look for the DemoTwinStickShooter scene and check the TwinStickGameManager.cs file.