Single Entry Point in Unity

(TLDR at the bottom)

The Idea

For a game project within Unity I had the idea to create a single class that has an Awake, Start, Update and FixedUpdate function. The reason for that was to have a single entry point into these functions, a central place that kicks off the logic. This is of course the GameManager. That has for example a reference to a PlayerManager, and so the PlayerManager’s Update function (named “Tick” in the code) is called by the GameManager’s Update function. This is the case for all other Managers as well.

Reduction of MonoBehaviours

The GameManger, of course, has to be a MonoBehaviour to have the Unity functions Awake, Start, etc. called. However, all other managers are supposed to be pure C# classes that are instantiated on runtime. The advantage of that is to have a distance from Unity. The state of the game, the core logic should be handled in these managers. They receive input from MonoBehaviours and send events to update the GameObjects to MonoBehaviours, but in general, the state of the game is handled with native C# classes.

Reference Handling

One problem to be solved is how to give the PlayerManager a reference to the MonoBehaviours of the Player. The PlayerManager is a pure C# class and only instantiated on runtime, so there is no way to use a public field or SerializeField. I chose to make use of ScriptableObjects and the fact that you can hold a reference to a prefab via an attached MonoBehaviour.

The GameManager has a reference to a ScriptableObject called “PrefabReferences”. And that Object has a reference to for example a script called “PlayerSetupHelper”. That’s a MonoBehaviour which is attached to the Player Prefab. It’s a little known fact that you can reference a whole prefab by referencing a script attached to it.

Logic Rundown

So what happens when for example spawning the player?

  • The GameManger’s Awake is called
  • The PlayerManager is created
  • The save data is loaded
  • The PlayerManager is initialized with the save data
  • The PlayerManager initializes its own managers (e.g. PlayerStatsManager, PlayerInventoryManager, etc.)
  • The PlayerManager gets a reference to the PrefabReferences ScriptableObject (which is a Singleton, but since it has no logic whatsoever I deemed that okay. It could also be passed to the PlayerManager by the GameManager)
  • The PlayerManager spawns the player prefab and assigns MonoBehaviours to the managers (e.g. PlayerAnimationHandler to the PlayerAnimationManager)

Conclusion

Here are what I see as the advantages of this technique:

  • You can handle order of execution of different scripts easily yourself
  • There is a slight performance advantage since there aren’t tons of calls to different Update and FixedUpdate functions
  • There is one central place where everything kicks off from. This can make debugging a lot easier.
  • The codebase is generally divided into native C# classes that handle the logic, and MonoBehaviours that are only supposed to “present” the logic and handle input

Now for the disadvantages:

  • There is a fair chance that the GameManager class will become a godclass
  • A lot more classes are needed this way

TLDR

I’ve created a GameManager class which is the only class in the entire game that has an Awake, Start, Update and FixedUpdate function. This is to follow loosely the principle of having a single entry point. While it has slight performance advantages, the big advantage is having a central place to manage the “script execution order” yourself, and a distance from Unity by using a lot of native C# classes, which would make it less fragile and safer to update Unity.

Leave a comment