在Unity3D中使用单例模式

4

我目前正尝试使用单例模式作为Unity游戏中任务组织的全局数据结构。

我的单例类代码如下:

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;
        }
    }
}

我将这个例子作为我的课程基础: https://msdn.microsoft.com/zh-cn/library/ff650316.aspx 然而,问题是,当我尝试在不同的脚本中访问TaskManager时,值并没有被保存。
例如,在一个脚本中,我做如下操作:
TaskManager tm = TaskManager.Instance;

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

print(tm.GetTaskList().Count);

通过这个,我可以看到TaskManager有一个计数为1,显示了新的任务。

但是我的另一个脚本试图从TaskManager中读取:

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

当我执行此操作时,Count返回为0,表明来自世界的上述任务未保存到TaskManager中。

我非常确定错误是由于我误解了如何使用单例模式导致的。我需要实现一个TaskManager Instance的set属性吗?还是我犯了其他错误?

谢谢!

编辑:

PushTask()代码如下:

public void PushTask(Task t)
{
    Tasks.Add(t);
}

你能提供 PushTask() 方法的代码吗? - Bartłomiej Zieliński
当然,我已经在底部添加了它。 - Jestus
如果您直接访问Instance,而不是将其分配给新变量,那么在这两种情况下仍然会发生吗? - Dion V.
2
实际上,如果您只想要一个单例,那么如果您需要它成为场景中的对象(例如在Update上运行),则不应该从MonoBehaviour继承,您需要创建一个GameObject,将其设置为DontDestroyOnLoad并将您的实例作为组件添加到其中。您不应该使用new SomeComponent(),Unity以不同的方式处理组件(和UnityEngine.Object),它会接管您的内存管理。 - Aidiakapi
我发现了这个教程http://www.unitygeek.com/unity_c_singleton/,它非常好地解释了如何在Unity3D中实现单例模式。希望对你有用。 - Rahul Lalit
显示剩余2条评论
1个回答

6
个人认为你选择的解决方案并不完美。 我曾经尝试使用未继承自MonoBehaviour的“抽象”类来将逻辑与Unity机制分离,但发现这使代码臃肿且不必要地复杂。 实际上,根据我的经验,除了仅包含数据而具有自己的某些逻辑的每个类最终都会成为MonoBehaviour。
因此,我会通过实现常规的MonoBehaviour单例模式来解决它,而不是从MonoBehaviour中删除继承。
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());
        }
    }
}

然后只需从中继承您的类即可。


我有点困惑,这样做与仅删除Monobehavior有什么区别?为什么这是更好的实现方式? - Jestus
1
如果你只是修复这个特定的问题,那么你原来的答案更好。然而,如果你正在构建一个可能很大的Unity项目,你会发现(1)无论如何你都必须将这个脚本变成MonoBehaviour,因为有许多不同的原因会出现,(2)你不想每次都实现单例模式(DRY原则),而是宁愿使用通用解决方案。这两点都是主观的,来自我的个人经验,所以如果你决定不遵循这个建议,我也理解。 - Max Yankov
我明白你的意思。那么在这个实现中,我需要将脚本附加到场景中的一个游戏对象上吗?我猜是的。 - Jestus
是的,因为这是使用MonoBehaviour的唯一方式。 - Max Yankov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接