1
\$\begingroup\$

We are developing an economic sandbox somewhat similar to a mixture of The Guild and Kenshi.

In this game, both the player and the NPCs will often create and destroy (consume) various objects.

The trouble is that destroying an object in Unity does not remove references to destroyed objects. For example, Lists containing the object will still contain an entry for it (that compares as equal to null).

The best solution I came up with is to use the observer pattern, notifying all classes where the object was stored about its destruction and removing references to it from there. But this method does not look optimal; are there better options?

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Assume your reader does not know The Guild or Kenshi like you do. Try to explain what kind of lists you need to maintain in your game, so we can suggest and judge proposed solutions by how well they serve that use case. \$\endgroup\$
    – DMGregory
    Commented Nov 29, 2023 at 18:25
  • \$\begingroup\$ Why doesn't it "look optimal" to you? What problems do you see with that architecture? \$\endgroup\$
    – Philipp
    Commented Nov 29, 2023 at 19:20
  • 1
    \$\begingroup\$ Also, note that the reference to the object isn't actually replaced with null — the whole managed object stays in memory until all references to it are deleted and the garbage collector reclaims it. Until then, it's just an overridden == operator that returns true when comparing the reference to null even though it's still pointing at a non-null object. \$\endgroup\$
    – DMGregory
    Commented Nov 29, 2023 at 21:12
  • \$\begingroup\$ Why not simply create a destructor for said objects to delete themselves as game objects, and you just eliminate the list entries? \$\endgroup\$ Commented Nov 30, 2023 at 13:06
  • \$\begingroup\$ "Assume your reader does not know The Guild or Kenshi like you do." Exactly this. When I hear "The Guild" I think of Felicia Day, not "economic sandbox" games. \$\endgroup\$ Commented Jan 3 at 13:55

1 Answer 1

4
\$\begingroup\$

The best way that I came up with is, according to the observer pattern, to notify all classes where the object was stored about its destruction and remove references to it from there.

That's what I always do.

[System.Serializable] public class DestroyableEvent : UnityEvent<Destroyable> {}
public class Destroyable : MonoBehaviour {
    [SerializeField] private DestroyableEvent destroyedEvent = new DestroyableEvent();
    public DestroyableEvent DestroyedEvent => destroyedEvent;

    void OnDestroy() {
        destroyedEvent.Invoke(this);
    }
}

Then in some other class you have something like this:

[SerializeField] private Destroyable prefab;
private List<Destroyable> objects = new List<Destroyable>();

public void SpawnObject() {
    var newObject = Instantiate(prefab);
    newObject.DestroyedEvent.AddListener(objects.Remove);
    objects.Add(newObject);
}

If objects are created and destroyed frequently, you should use a pooling system instead, but you still need events to track when an object is returned to the pool so you can remove it from any relevant lists.

Another solution you can use is to periodically scan lists for null entries and remove them, or remove them when you try to access an entry in the list and determine that it's null. I prefer to use an event-driven approach as shown above.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ @Phillip Your edit to this answer was unnecessary. Since the type of the prefab field is Destroyable, calling Instantiate(prefab) returns a reference to the new Destroyable component - calling GetComponent<Destroyable>() is not necessary. \$\endgroup\$
    – Kevin
    Commented Jan 15 at 19:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .