Pros/Cons of "Game manager" scripts in terms of performance?

So recently I’ve been using two different methods of creating “Game manager” scripts (Meaning, MonoBehaviours which exist purely for managing other objects, and aren’t strictly tied to any particular gameObject), and I have my opinions on which I prefer for various reasons, but one area I’m not as knowledgable about in Unity is performance.

Method 1: One empty GameObject which just holds a bunch of scripts. I would use this by having a public static References class, which each script adds themself to in their Awake method;

public class MyManager : MonoBehaviour
{
    private void Awake()
    {
        References.MyManager = this;
    }
}

Pro: Only one game object, so might be so less effort spent instantiating upon launch.
Con: Messy. Could quickly build up to 10-20+ completely unrelated scripts on one object, which is obviously not ideal.

Method 2: Singleton objects with their own dedicated script, with a static Instance property which is instantiated upon first access. Instances being created;

public class MyManager : MonoBehaviour
{
    private static MyManager instance;
    public static MyManager Instance
    {
        get
        {
            if (instance == null)
            {
                instance = Instantiate(Resources.Load<MyManager>("Prefabs/Game
                 /MyManager"), GameObject.Find("GameControllers").transform);
            }
            return instance;
        }
    }
}

Pro: Can group the gameObjects in ways that make them easier to manage.
Con: Those 10-20+ scripts on one object, have now become 10-20+ objects with one script each.

My gut instinct is that Method 2 has to be much less performant, but in my opinion it’s nicer to work with. Am I wrong?
I’d love some extra opinions on which of these is better within the engine itself, like with performance.

In the end you’re describing very nearly the same thing. 10-20 scripts in existence. The only difference is if each script gets its own GameObject to attach itself too, or they all get attached to the same GameObject.

Performance wise the difference is going to be negligible.

Really all you’re talking about is the memory overhead of 10-20 GameObejcts/Transforms vs 1 GameObject/Transform. And considering that a GameObject and its Transform doesn’t really consume very much memory on its own. This difference will barely be seen (we’re talking in what… a few hundred bytes?)

Also note the Singleton has nothing to do with either of these either. It’s only incidental option to is a Singleton. You could easily have made your scripts Singletons in option 1 as well.

As for personal taste, Option 1 is closest to what I generally do. I always have a ‘Game’ asset that is loaded on start of my game and contains various global/singleton type scripts.

With that said, I will have other singleton’s separate from this ‘Game’ asset. But those are for singleton’s that aren’t specifically global to the entire game. Like maybe a multiplayer manager that only loads when you go into multiplayer. Or a camera manager that varies from scene to scene (it’s life time is scene long, not entire game long).

2 Likes

Usually when using a Singleton you put the script on 1 gameobject you want. That way you can add values in the inspector to it, which is great. This way it doesn’t instantiate anything at all (I personally use this method and simply throw an error when the script is not found. All my singletons are usually on 1 gameobject)

1 Like

Thank you, this is exactly the type of response I was looking for.
The overhead of instantiating an empty GameObject with just a transform was what I was curious about, as I really had no reasonable idea if that was worth worrying about in terms of performance.

On your note about the Singleton however, the reason I mentioned it is because I couldn’t find a way of creating the singleton reference on first access without instantiating a GameObject like in my example. How would you make the scripts singletons using method 1, out of curiosity?

I don’t think you’ll see any meaningful difference between these two methods. This is the kind of question that only you can answer for sure by profiling, though.

Some Notes:

You should call DontDestroyOnLoad in your manager GameObjects. It avoids needing to create them every time you change scene, and it makes multi-scene strategies a lot easier.

I’d recommend avoiding prefabs and Resources.Load in the Method 2. It’s messy, bug prone, and a bit slow. I think you should instead just create a new GameObject and add your Manager component to it.

I’d probably avoid nesting the individual GameObjects in a “GameControllers” object in Method 2. If you are using DontDestroyOnLoad, they will already be put under a separate header in the hierarchy window, which is often enough for organization. This will make your workflow easier, and less bug-prone.

Finally, I’d recommend avoiding so many singletons. This is a bit controversial in the community. I won’t say that you should never ever use them, but 20 singletons sounds like too many. Singletons can make code harder to change later on, they make things harder to test, and they can introduce a code architecture that is very bug-prone when there are too many of them.

1 Like

I’d say that the Method 1 also uses singletons. If you are storing them in a static variable, it means that there can only really be one of them in use.

1 Like

I suppose technically, but I’m talking about Singletons in the classic sense, where each script holds it’s own static reference to the singleton instance.
For me, calling MyManager.Instance is more appealing than calling References.MyManager. One reason I prefer this, is since all the References need to be instianted in their Awake method, I have to remember to reference them in at least Start, but that has caused some complications in the past.

Then, just assign the script instance to MyManager.Instance instead of assigning it to References.MyManager.
EDIT
I should also say that I prefer Method 2, too, though. It’s cleaner.

1 Like

I might be mis-understanding you, but I don’t think this solves the issue? Where are you assigning the script instance to MyManager.Instance?

My main issue is that the References.MyManager has to be done in Awake, meaning I cannot access this script in any other Awake method. Whereas MyManager.Instance is created upon access, so it can be accessed any time without issue.

The idea of it loading the singleton on access isn’t what makes it a singleton. There being only 1 instance is what makes it a Singleton.

Option 1 could just have the ‘instance’ on the script itself rather than this References class and it’d be a singleton accessed the same way most standard singleton’s are (via the static instance property). But even then… even with the References it’s still sort of technically a singleton since again, you only have 1 instance (though neither of your options actually have code that restricts it to 1 instance… meaning technically neither are true singletons… but I don’t want to get to pedantic).

public class MyManager : MonoBehaviour
{

    public static MyManager Instance { get; private set; }

    private void Awake()
    {
        Instance = this;
    }
}

But if you want the functionality of Option 2 where it’s loaded on access of ‘Instance’. You can do that. Just put all the scripts on the same GameObject asset.

Then your resource that you load will be a GameObject with all the scripts on it. They still set their ‘Instance’ in awake, but once ANY one of the singleton’s is accessed ALL of them are loaded simultaneously.

public class SingeltonX : MonoBehaviour
{
    private static SingeltonX instance;
    public static SingeltonX Instance
    {
        get
        {
            if (instance == null)
            {
                instance = Instantiate(Resources.Load<SingeltonX>("Prefabs/Game
                /SingletonAssetOfAllGameManagers"));
            }
            return instance;
        }
    }
 
    void Awake()
    {
        instance = this;
    }
}

public class SingeltonY : MonoBehaviour
{
    private static SingeltonY instance;
    public static SingeltonY Instance
    {
        get
        {
            if (instance == null)
            {
                instance = Instantiate(Resources.Load<SingeltonY>("Prefabs/Game
                /SingletonAssetOfAllGameManagers"));
            }
            return instance;
        }
    }
 
    void Awake()
    {
        instance = this;
    }
}

If you access SingletonX.Instance OR SingletonY.Instance, both will be loaded and the respective Instance property set.

1 Like

Right ok, this looks more like what I was looking for. For this to work, is it safe to assume the Awake is called after Instantiate() but before the instance is returned?

There’s actually a discussion of this going on today in this thread:
https://discussions.unity.com/t/904929

But in short… the answer is yes.

1 Like

Alright, I think I have all the information I need on this issue.
Thanks to everyone who helped out, you’ve made me a marginally better developer :sunglasses: