4

I use the following pattern to make my singleton in Unity

public class BlobManager : MonoBehaviour  
{
    public static BlobManager instance {get; private set;}
    void Awake () 
    {
        if(instance != null && instance != this)
           Destroy(gameObject);

        instance = this;
        DontDestroyOnLoad(gameObject);
    }

    public void SomeFunction()
    {
       if (this != instance)
           Debug.Log("They're Different!")
    }
}

They always end up being different as shown in SomeFunction(). When i set a value locally for the class BlobManager and another for using the static var "instance" like so:

foo = "bar"; 
BlobManager.instance.foo = "foo";

the value of foo a seen in the debugger on a breakpoint within the class, will always be "bar". But when other classes try to access the same variable it will be "foo".

I'm not sure how to look at memory addresses in Monodevelop, but im sure this and this.instance would have different memory addresses. Is there something wrong with my singleton pattern? I've tried may other patterns as well with the same result.

aggsol
  • 2,343
  • 1
  • 32
  • 49
  • 1
    How are you "calling" SomeFunction to check if its same / different – potatopeelings May 13 '15 at 04:33
  • Singletons typically have a private constructor to restrict instantiation by code other than the singleton itself. The class you have looks closer to the monostate pattern than the singleton pattern. – Matthew May 13 '15 at 04:45
  • And where/how is the instance of `BlobManager` created? – Ripple May 13 '15 at 04:55
  • If you don't care about the memory address and are just looking to see if the two instances are different you can use their hash code: `instance.GetHashCode().ToString()`. – Nic Foster May 13 '15 at 04:56
  • 1
    @NicFoster that is not at all the intended use of `GetHashCode`. `object.ReferenceEquals` is the idiomatic way to determine whether two variables contain the same reference. – Matthew May 13 '15 at 04:59
  • @Matthew: Yes I should've been more clear, I didn't mean to use hash codes to compare equality in code, I meant it more for if you were looking at both in a watch window or wanted to print out the hash codes, you could see that they were different by looking at the codes. – Nic Foster May 13 '15 at 05:18
  • You are trying multiple things: ensure a single instance, global access and persistance when loading scenes. Is your singelton required to ab a `MonoBehaviour`? – aggsol May 13 '15 at 06:40
  • Do you have game objects with these mono behaviours in the scene? How many? – Max Yankov May 13 '15 at 06:47
  • 3
    @dbc @Matthew since this is a `MonoBehaviour`, which can not be created using a constructor, advice describing generic C# singleton patterns is useless here. – Max Yankov May 13 '15 at 06:48
  • @MaxYankov I'm not familiar with Unity, so I can see how my comment is probably not at all useful. Do you know what manages the lifetime of a `MonoBehavior` object? Perhaps a solution may be achieved by doing both encapsulation and using the monostate pattern instead. – Matthew May 13 '15 at 13:53
  • `MonoBehaviour` instances are usually created by designers in the editor, and later serialised and deserialised, and if they are needed to be created in runtime, it is done with `AddComponent`. The lifetime of `MonoBehaviour` object is complicated; even comparison with `null` is overriden. – Max Yankov May 13 '15 at 14:04

5 Answers5

1

In your example the BlobManager instantiation occur many times, but only first instance saved in property Instance.

Deriving from MonoBehaviour means that you can attach your script to many objects coursing many instances. Singleton must have private constructor, no MonoBehaviour derivation and you have to not attach this script to any object. If you need to attach the script to object, you should create another script that derive from MonoBehaviour and controls singleton

Example (not tested):

public class BlobManager
{
    static BlobManager _inst;
    public static BlobManager Instance {
    get
    {
        if (_inst == null)
            _inst = new BlobManager();
        return _inst;
    }
    }

    private BlobManager() 
    {

    }
}
Stas BZ
  • 1,184
  • 1
  • 17
  • 36
1

Here is really well formed Singleton pattern that is easily reusable for monobehaviours

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    public static T Instance
    {
        get
        {
            if(mInstance == null && !isQuitting)
            {
                mInstance = FindObjectOfType(typeof(T)) as T;
                if(mInstance == null)
                {
                    GameObject go = new GameObject(typeof(T).Name);
                    mInstance = go.AddComponent<T>();
                    Logger.LogWarning("Creating " + go.name + " since one does not exist in the scene currently");
                }
            }
            return mInstance;
        }
    }

    public static bool HasInstance
    {
        get
        {
            //if we dont have one we try to find one
            if(mInstance == null)
            {
                mInstance = FindObjectOfType(typeof(T)) as T;
            }
            return mInstance != null;
        }
    }

    static T mInstance;

    protected static bool isQuitting = false;
    protected virtual void OnApplicationQuit()
    {
        isQuitting = true;
    }

}

The reason that your singleton pattern is failing is because Awake is not guaranteed to be called before you are able to access it via the Singleton interface, despite the documentations claim that Awake is always called first.

The above allows you to reference the singleton at any point and it will always return a valid instance, unless the game is quitting, in which case creating a new instance will throw uncleaned up errors and leave an extra instance stranded in the scene after stopping.

Colton White
  • 976
  • 5
  • 17
0

I would advise against trying to use the classic Singleton pattern with Monobehaviour. Instead perhaps have a container object for these type of things, like an empty gameobject, and retrieve the required behaviours like so:

GameObject.Find("ContainerName").GetComponent<BlobManager>();

This is globally accessible like the Singleton pattern. There will also always just be this one instance that you manually put on your container object. This also allows for the niceties of the editor inspector.

Reasurria
  • 1,808
  • 16
  • 14
  • I agree as a facade pattern keeps the code way more maintainable. The drawback is `GameObject.Find`: Calling this from a few FixedUpdate methods in a scene full of objects might affect perfomance. Making _ContainerName_ as the one and only singleton and let it manage access to other components helps avoiding this. – Kay May 13 '15 at 11:21
  • 1
    True. BUT in OnStart() you can just cache the instance as in BlobManager cached = GameObject.Find("ContainerName").GetComponent(); – Reasurria May 13 '15 at 11:23
  • Exactly. Or you can use a get property with a private variable that performs the lookup on demand. But again we need fast access to _ContainerName_ and that is where I pleed for a static var or property - IMO :) – Kay May 13 '15 at 11:30
0

The best way in Unity would be to avoid creating BlobManager more than once. You could achieve this for example with a Special boot scene that is responsible for setting up scene independent objects i.e. having application scope. If initialisation is done this scene is never loaded again and that's it.

The problem with your implementation of Awake is that instance is always set to the currently instantiated BlobManager component, regardless is there is one already or not. This is because Destroy does not perform a return and the code afterwards is executed anyway. Even worse it is not executed immediately. That said instance will always be updated with the most recent component even if it will be destroyed at the end of the frame and will be set to null then.

I suggest that you modify the Awake method:

void Awake () 
{
    if(instance == null) {
        instance = this;
        DontDestroyOnLoad(gameObject);
    } else {
        Destroy(gameObject);
    }
}

Note that singletons are useful if they are used sparingly. Having a bunch of them is considered as bad practice and might turn your project into a nightmare one day (s. the Links in Wikipedia for more about this).

To at least have them attached to one main game object you can use an approach dscribed in this answer: Unity singleton manager classes

Community
  • 1
  • 1
Kay
  • 12,918
  • 4
  • 55
  • 77
0

a singleton pattern need to have a private constructor otherwise it s not a singleton

by default a class has a public default constructor so to avoid this behavior as default we break it by a private default constructor

Eidivandi
  • 1
  • 1