2
\$\begingroup\$

I would like to have a single "Game Manager" game object or class that contains numerous parameters and tweens or other functions that would be necessary to access throughout my app across various scenes.

I see this is a common need people voice, and an apparently good script was posted here:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using JetBrains.Annotations;

public abstract class Singleton<T> : Singleton where T : MonoBehaviour {
    #region  Fields
    [CanBeNull]
    private static T _instance;

    [NotNull]
    // ReSharper disable once StaticMemberInGenericType
    private static readonly object Lock = new object();

    [SerializeField]
    private bool _persistent = true;
    #endregion

    #region  Properties
    [NotNull]
    public static T Instance {
        get {
            if (Quitting) {
                Debug.LogWarning($"[{nameof(Singleton)}<{typeof(T)}>] Instance will not be returned because the application is quitting.");
                // ReSharper disable once AssignNullToNotNullAttribute
                return null;
            }
            lock (Lock) {
                if (_instance != null)
                    return _instance;
                var instances = FindObjectsOfType<T>();
                var count = instances.Length;
                if (count > 0) {
                    if (count == 1)
                        return _instance = instances[0];
                    Debug.LogWarning($"[{nameof(Singleton)}<{typeof(T)}>] There should never be more than one {nameof(Singleton)} of type {typeof(T)} in the scene, but {count} were found. The first instance found will be used, and all others will be destroyed.");
                    for (var i = 1; i < instances.Length; i++)
                        Destroy(instances[i]);
                    return _instance = instances[0];
                }

                Debug.Log($"[{nameof(Singleton)}<{typeof(T)}>] An instance is needed in the scene and no existing instances were found, so a new instance will be created.");
                return _instance = new GameObject($"({nameof(Singleton)}){typeof(T)}")
                           .AddComponent<T>();
            }
        }
    }
    #endregion

    #region  Methods
    private void Awake() {
        if (_persistent)
            DontDestroyOnLoad(gameObject);
        OnAwake();
    }

    protected virtual void OnAwake() { }
    #endregion
}

public abstract class Singleton : MonoBehaviour {
    #region  Properties
    public static bool Quitting { get; private set; }
    #endregion

    #region  Methods
    private void OnApplicationQuit() {
        Quitting = true;
    }
    #endregion
}

In Unity, how do I correctly implement the singleton pattern?

However, I don't actually understand how to use this or what to do with it.

Do I attach this script to a GameObject? Do I need to attach it to one in each scene? Is the idea to attach it to one GameObject in every scene and then the script will automatically kill any duplicates that accidentally get made? I presume if I just attach it to one GameObject in one scene, then if that scene doesn't load it won't be loaded either.

But if I add it to one GameObject in a dummy scene that only loads once on starting the application and never again, this should work right?

I tried attaching it to a GameObject and it says I can't because it's abstract. So I have no idea what to do.

Further, if I wanted to add to that script public float exampleWidth = 1000.0f; and then be able to access that from anywhere in my game, where in the script would I be meaning to put it?

Then let's say I'm writing in another script attached to another GameObject and want to access something from this like exampleWidth how do I write that? How do I get the gameObject holding this script from another scene? Should I find it on Awake/Start in every scene that needs it somehow?

Ideally I want:

  • This singleton object/script should always be running.
  • If there is none currently in existence, I need one made.
  • It should not be destroyed on switching scenes

The other problem I wonder about with this script is when it checks for duplicates and tries to preserve instances[0], how do we know if instances[0] is the original or the new duplicate? It seems it might be random whether the new duplicate or the old original survives.

If you wanted the old original to always survive, could this be possible? For example, if I started a tween loop in the singleton and I want this tween loop to constantly survive so I can use it smoothly and seamlessly even across scene changes, would this be possible? Or would it be best to like I said attach this to just one GameObject in a dummy scene and load that at start up only once?

Thanks for any ideas.

\$\endgroup\$
1
  • \$\begingroup\$ This singleton implementation uses a lot of advanced features of the C# programming language. I would really urge you to pick a singleton implementation you actually understand. \$\endgroup\$
    – Philipp
    Commented Dec 3, 2021 at 13:50

2 Answers 2

3
\$\begingroup\$

This here is an abstract class. Which means you can not use it directly. You have to write a class which inherits from this class. Further, the class is a generic. Which means it is "a class for other classes". This is not newbie-stuff. This is C# for adults!

If I understand this contraption correctly, then it seems to allow you to create an attachable singleton MonoBehaviour class like this:

public class GameManager : Singleton<GameManager> {
    public float exampleWidth = 1000.0f;
}

You can also have any Unity event methods like Update in this class. With one exception, though: If you want to use the Awake event method, you have to declare it like this instead: protected override void OnAwake().

This is the component you need to attach to an empty game object.

When you don't want it to get deleted on scene switches, check the "persistent" checkbox in its inspector.

Now other classes can access it like this: GameManager.Instance.exampleWidth .

The other problem I wonder about with this script is when it checks for duplicates and tries to preserve instances[0], how do we know if instances[0] is the original or the new duplicate? It seems it might be random whether the new duplicate or the old original survives.

It's only going to do that the very first time another script requests the .Instance. At that point it's undefined which one of the objects in the scene is "the original". But this is not supposed happen anyway. When you receive that warning, then you screwed up.

When you create a second GameManager later, then the .Instance property is going to ignore it. It is just going to return the object cached in the private variable _instance. It won't search for other instances again (which is good, because FindObjectsOfType is a very slow method and thus shouldn't be called more often than strictly necessary).

\$\endgroup\$
0
0
\$\begingroup\$

Add this Awake() to your GameManager script, Attach this Script to a Empty GameObject at the starting scene of your game, it will not be destroyed till the end of the Application Cycle.

public class GameManager:MonoBehaviour
{
    public static GameManager Instance {get;private set;}
    void Awake()
    {
        if(Instance==null)
        {
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
            Destroy(gameObject);
    }

    public int variable1 = 0;
}

You can call variable using

GameManager.Instance.variable1
\$\endgroup\$
0

You must log in to answer this question.

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