3

I'm currently attempting to use a Singleton as a global data structure for Task organization in a game I'm making in Unity.

My Singleton class code is as follows:

public class TaskManager : MonoBehaviour 
{
    private List<Task> Tasks;

    private static TaskManager instance;
    private TaskManager() 
    {
        Tasks = new List<Task>();
    }

    public static TaskManager Instance
    {
        get
        {
            if(instance == null)
            {
                instance = new TaskManager();
            }
            return instance;
        }
    }
}

I used this example as a basis for my class: https://msdn.microsoft.com/en-us/library/ff650316.aspx

However, the problem is, when I try to access the TaskManager in different scripts, the values don't get saved.

For example, in one script I do:

TaskManager tm = TaskManager.Instance;

Task newTask = new Task();
tm.PushTask(newTask);

print(tm.GetTaskList().Count);

Through this I can see that the TaskManager has a count of 1, showing the new task.

But then my other script attempts to read from the TaskManager:

TaskManager tm = TaskManager.Instance;
List<Task> l = tm.GetTaskList();
print(l.Count);

When I do this, the Count is returned as 0, showing that the above task from the world has not been saved to the TaskManager.

I'm pretty sure the error is resulting from me misunderstanding how to use Singletons. Do I need to implement a set property for TaskManager Instance? Or is there another mistake I'm making?

Thanks!

Edit:

The PushTask() code is as follows:

public void PushTask(Task t)
{
    Tasks.Add(t);
}
Jestus
  • 623
  • 2
  • 9
  • 25
  • Can you provide `PushTask()` method code? – Bartłomiej Zieliński Feb 28 '15 at 22:30
  • Sure thing. I added it in at the bottom. – Jestus Feb 28 '15 at 22:31
  • Your singleton is ok, so the problem must be somewhere else. According to [this problem solution](http://answers.unity3d.com/questions/569694/unity-3d-singleton-problem.html), you should mark your singleton class with `DontDestroyOnLoad` if you're changing scenes and want to keep any data in static instance. – Bartłomiej Zieliński Feb 28 '15 at 22:52
  • Hmm I'm not changing scenes, though. It's the same single scene, just access via different scripts. – Jestus Feb 28 '15 at 22:53
  • Does it still occur if you directly access `Instance` in both cases, instead of assigning it to a new variable? – Dion V. Feb 28 '15 at 23:13
  • 2
    Actually if you just want a singleton you shouldn't inherit from a `MonoBehaviour` if you need it to be an object in the Scene (to for example run on Update), you need to create a `GameObject`, set it to `DontDestroyOnLoad` and add your instance as component to it. You shouldn't `new SomeComponent()`, Unity handles components (and `UnityEngine.Object` in general) differently and it'll take over your memory management. – Aidiakapi Feb 28 '15 at 23:24
  • I found this tutorial http://www.unitygeek.com/unity_c_singleton/, that explained very well about how to implement singleton in unity3d . I hope it is useful – Rahul Lalit Aug 05 '16 at 13:22

1 Answers1

6

Personally, I don't think that solution that you chose is a perfect one. For some time, I tried using "abstract" classes that didn't inherit from MonoBehaviours, to decouple logic from Unity mechanics — but found that this makes code bloated and unnecessary complicated. In fact, every class that doesn't only contain data but has some logic of its own ends up being a MonoBehaviour sooner or later, in my experience.

So, instead of removing inheritance from MonoBehaviour, I'd solve it by implementing the usual MonoBehaviour singleton pattern:

using UnityEngine;

public abstract class Singleton<T> : MonoBehaviour where T : Singleton<T>
{
    public static T Instance { get; private set; }

    protected virtual void Awake()
    {
        if (Instance == null)
        {
            Instance = (T) this;
        }
        else
        {
            Debug.LogError("Got a second instance of the class " + this.GetType());
        }
    }
}

And then just inheriting your class from it.

Max Yankov
  • 12,551
  • 12
  • 67
  • 135
  • I guess I'm a bit confused, what does this do that simply removing the Monobehavior doesn't fix? Why is this a better implementation? – Jestus Mar 01 '15 at 14:34
  • 1
    If you're only fixing this particular problem, your original answer is better. However, if you're building a potentially big Unity project, you'll find that (1) you'll have to make this script a MonoBehaviour anyway, for many different reasons that will come your way, (2) you don't want to implement singleton every time (DRY principle) and would rather use a generic solution. Both points are subjective and come from my personal experience, so I understand if you decide not to follow this advice. – Max Yankov Mar 01 '15 at 16:05
  • I see what you're saying. Then in this implementation, do I need to attach the script to a GameObject in the scene? I'm assuming yes. – Jestus Mar 01 '15 at 18:08
  • Yes, because that's the only way MonoBehaviours can be used. – Max Yankov Mar 01 '15 at 20:02