The Components Registry


The good old issue of finding references to other Unity components, or objects. It’s sometimes amazes me how developers reach to awkwardly complex solutions to a really simple problem:

  • Assign the reference in the Inspector
  • Expose the field as a static property

If I need a reference to my NetcodeState, I just write this:

var netcodeState = Components.NetcodeState;

The Components Registry

In the scene, I have an object named Components Registry with a Components script on it:

The Components Registry object

Just drag & drop the NetcodeState component on that Inspector field. That’s it.

Need more component references? Just add more fields.

Components Code

The purpose of Components is to have all prominent references at a central location.

Component null checking method omitted

The Components code is not the least bit complex. There’s both a private SerializeField and a public static property for NetcodeState. The latter merely returns the serialized reference.

Find Or Register References

If in the future there may be a need to actually “find” a reference at runtime, I can extend Components accordingly. Be it via GetComponentsInChildren or GameObject.FindWithTag.

Likewise, if instantiated objects need to be available from a central location, they could simply register themselves with the Components registry. Or a similar script just for those kinds of objects to avoid blobbing unrelated references in the same place.

And with registering objects you also need to unregister them, during OnDestroy at the latest. Otherwise you will have stale references eventually, at the latest when loading another scene.

Execution Order

By following the rule of thumb to never call out to outside references in Awake (use them in Start instead), we won’t have any issues with this setup.

Even if there were, you could always add the Components script to the Script Execution Order list.

Null Checks On Launch

The ThrowIfComponentIsNull is simply a validation where each field is supposed to be checked for null. The moment you enter playmode you’ll know there’s an issue – not after minutes of testing!

Pro Tip: Check for potential errors as early as possible!

Singleton In Disguise

Components has a static instance field, making it a singleton. You could optionally also put it in DontDestroyOnLoad.

Components is meant specifically to provide access to components where you’d commonly make that component a singleton. But rather than having a dozen singletons, it’s better to have a single one giving access to the global components of your game, be it Netcode, GUI, Game Logic, Player(s), Audio, Services, and so on.

Note that the Components singleton is not exposed! From the outside it just looks like a static class because the indirection through the instance field is neatly hidden from the user.

Supporting Disabled Domain Reload

To be compatible with Disabled Domain Reload (I prefer: Instant Enter Playmode) we only need to make sure the instance gets reset in OnDestroy:

There’s no need to use a RuntimeInitializeOnLoadMethod to support Disabled Domain Reload, as the manual suggests. Not if you clear your statics upon shutdown. Register a static event in Start and unregister it in OnDestroy – that’s it! Likewise, reset static fields in OnDestroy.

I feel like the manual has this totally backwards, and made a report to that effect.

All Singletons Are Singles!

Note that if we ever get a second instance, I throw an exception!

I always do! Never .. any .. other .. way! By definition, a second singleton instance is an ERROR that needs to be fixed!

If you only Destroy() additional singleton objects like 99% of other developers do, you do run at risk of really weird issues.

Imagine .. it just needs another component on that same object that also runs code in Awake, and already did before the object got destroyed, and then … DooM!

I love DooM! But not in my code!

Potential Issues Of A Doubleton

Perhaps that other component’s Awake cleared the list of all enemies in the game. Or reset the player stats to defaults. Or … ๐Ÿค

Oh and there can be issues from the outside, too!

Say there’s a GetComponent or FindObject that runs during Awake and it does find that second singleton object, not the actual one. And next thing you know, NullReferenceException!

Even though the singleton object is right there in the scene hierarchy and you’re like ๐Ÿ˜•.

Oh and … if you do this often enough, you could quite literally call your singletons MEGATONS! ๐Ÿ˜œ

It’s a looming nuclear nightmare with the potential to go off at the worst possible point in time.

It’s Not A Manager!

If you really wanted to, you could also name this script ComponentsManager .. if tradition is very important to you.

But consider, does this class really manage components? What is management doing anyway, really – do you know?

Most simply never give it any thought. Slam Manager at the end, and it sounds way more meaningful. Right?

But if you struggle to come up with a list of things that every Manager script has in common, then Manager has no meaning to you.

Thus your Manager scripts could as well be called Game or Player or Scene. That hurts, right?

And so should the overuse of ThisManager and ThatManager!

Summary

Components is a real neat solution for global references. And a simple variation of this can be used for registering dynamic references.

The rest of this article was me ranting about seemingly never-ending cargo-cult programming crud and poking a little fun. ๐Ÿ™‚

3 responses

  1. […] I implemented all three in about 6 hours while also fixing some bugs and adding the Components Registry. […]

  2. […] the Components Registry? If not, click the […]

  3. […] that’s where InputUsers comes into play. I get it via the trusted Components Registry object. And this also tells you that InputUsers exists outside network […]

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

WordPress Cookie Notice by Real Cookie Banner