【问题标题】:Using a Singleton in Unity3D在 Unity3D 中使用单例
【发布时间】:2015-02-28 22:20:01
【问题描述】:

我目前正在尝试在 Unity 中制作的游戏中使用 Singleton 作为任务组织的全局数据结构。

我的Singleton类代码如下:

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/en-us/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中。

我很确定这个错误是由于我误解了如何使用 Singletons 造成的。我需要为 TaskManager Instance 实现一个 set 属性吗?还是我犯了另一个错误?

谢谢!

编辑:

PushTask() 代码如下:

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

【问题讨论】:

  • 能否提供PushTask()方法代码?
  • 当然可以。我在底部添加了它。
  • 你的单身没问题,所以问题肯定出在其他地方。根据this problem solution,如果您正在更改场景并希望将任何数据保留在静态实例中,则应使用DontDestroyOnLoad 标记您的单例类。
  • 嗯,不过,我不会改变场景。这是同一个场景,只是通过不同的脚本访问。
  • 实际上,如果你只想要一个单例,你不应该从 MonoBehaviour 继承,如果你需要它成为场景中的一个对象(例如在更新上运行),你需要创建一个GameObject,将其设置为 DontDestroyOnLoad 并将您的实例作为组件添加到其中。你不应该 new SomeComponent(),Unity 以不同的方式处理组件(和一般的 UnityEngine.Object),它将接管你的内存管理。

标签: c# unity3d singleton unity3d-2dtools


【解决方案1】:

就个人而言,我认为您选择的解决方案并不完美。有一段时间,我尝试使用不继承自 MonoBehaviours 的“抽象”类来将逻辑与 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 无法解决的问题是什么?为什么这是一个更好的实现方式?
  • 如果您只是解决这个特定问题,那么您的原始答案会更好。但是,如果您正在构建一个潜在的大型 Unity 项目,您会发现(1)无论如何您都必须将此脚本设置为 MonoBehaviour,因为您会遇到许多不同的原因,(2)您不需要每次都想实现单例(DRY 原则),宁愿使用通用解决方案。这两点都是主观的,来自我的个人经验,所以如果你决定不遵循这个建议,我理解。
  • 我明白你在说什么。那么在这个实现中,我是否需要将脚本附加到场景中的 GameObject 上?我假设是的。
  • 是的,因为只有这样才能使用 MonoBehaviours。
猜你喜欢
  • 2018-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多