Unity单例管理器类

时间:2012-12-05 18:38:38

标签: c# design-patterns unity3d singleton

在Unity中,有什么方法可以创建一个单独的游戏管理器,可以作为全局类在任何地方访问,静态变量会将相同的常量值吐出到每个提取这些值的类中?在Unity中实现它的方法是什么?我必须将它附加到GameObject吗?可以只是在文件夹中,而不是在视觉上在场景中吗?

9 个答案:

答案 0 :(得分:44)

总是如此:它取决于。我使用两种类型的单例,附加到GameObject的组件和不是MonoBehaviour派生的独立类。 IMO的整体问题是如何将实例绑定到场景,游戏对象的生命周期......并且不要忘记有时候让组件特别引用其他MonoBehaviour对象更方便更安全。

  1. 有些类只需要提供一些值,例如需要在调用时从持久层加载设置的配置类。我将theese课程设计为简单的单身人士。
  2. 另一方面,一些对象需要知道场景何时开始,即调用Start或必须在Update或其他方法中执行操作。然后我将它们作为组件实现,并将它们附加到游戏对象中,该游戏对象可以在加载新场景后继续存在。
  3. 我设计了基于组件的单例(类型2),它有两个部分:一个名为GameObject的持久Main,它包含所有组件,一个名为MainComponentManager的扁平单例(类型1)用于管理它。一些演示代码:

    public class MainComponentManger {
        private static MainComponentManger instance;
        public static void CreateInstance () {
            if (instance == null) {
                instance = new MainComponentManger ();
                GameObject go = GameObject.Find ("Main");
                if (go == null) {
                    go = new GameObject ("Main");
                    instance.main = go;
                    // important: make game object persistent:
                    Object.DontDestroyOnLoad (go);
                }
                // trigger instantiation of other singletons
                Component c = MenuManager.SharedInstance;
                // ...
            }
        }
    
        GameObject main;
    
        public static MainComponentManger SharedInstance {
            get {
                if (instance == null) {
                    CreateInstance ();
                }
                return instance;
            }
        }
    
        public static T AddMainComponent <T> () where T : UnityEngine.Component {
            T t = SharedInstance.main.GetComponent<T> ();
            if (t != null) {
                return t;
            }
            return SharedInstance.main.AddComponent <T> ();
        }
    

    现在,想要注册为Main组件的其他单身人士看起来像:

    public class AudioManager : MonoBehaviour {
        private static AudioManager instance = null;
        public static AudioManager SharedInstance {
            get {
                if (instance == null) {
                    instance = MainComponentManger.AddMainComponent<AudioManager> ();
                }
                return instance;
            }
        }
    

答案 1 :(得分:4)

刚接触Unity的工程师通常不会注意到

你不能拥有&#34;单身&#34;在ECS系统中。

没有意义。

Unity中的所有内容都是GameObjects,位于XYZ位置。他们可以附加组件。

这就像试图拥有一个单身&#34;或者&#34;继承&#34;在.... Photoshop或Microsoft Word。

Photoshop 文件 - XY位置的像素
文本编辑器文件 - X位置的字母
Unity 文件 - 位于XYZ位置的GameObjects

就是那么简单&#34;。

当然,你必须在每个Unity项目中都有一个预加载场景。

难以置信的简单方法:https://stackoverflow.com/a/35891919/294884

这很简单,这不是问题。

一旦Unity包含一个&#34;内置的预加载场景&#34; (即,为了节省你一次点击创建一个),这将永远不会再讨论。

(注意A - 用于编译Unity的组件的一些语言当然具有OO概念; Unity本身根本没有与OO的连接。)

(注意B - 在团结的早期,你会看到尝试制作代码&#34;动态创建游戏对象 - 并保持其独特性 - 并将其自身附加到其中#&# 34; 。除了奇怪之外,只是FWIW理论上它不可能确保唯一性(实际上甚至不在一个框架内)。再一次,它完全没有实际意义,因为它是#sa;简单的非问题,在Unity中,一般行为只是进入预加载场景。)

答案 2 :(得分:1)

如果这个类只是用于访问全局变量,那么你真的不需要单例模式,或者使用GameObject。

只需创建一个包含公共静态成员的类。

public class Globals
{
    public static int mStatic1 = 0;
    public static float mStatic2 = 0.0f;
    // ....etc
}

其他解决方案很好,但如果您只需要对变量进行全局访问,那就太过分了。

答案 3 :(得分:1)

我编写了一个单例类,可以轻松创建单例对象。它是MonoBehaviour脚本,因此您可以使用Coroutines。它基于这个Unity Wiki article,我将添加选项,以便稍后从Prefab创建它。

所以你不需要编写Singleton代码。只需下载this Singleton.cs Base Class,将其添加到您的项目中,然后创建扩展它的单例:

public class MySingleton : Singleton<MySingleton> {
  protected MySingleton () {} // Protect the constructor!

  public string globalVar;

  void Awake () {
      Debug.Log("Awoke Singleton Instance: " + gameObject.GetInstanceID());
  }
}

现在你的MySingleton类是一个单例,你可以通过Instance调用它:

MySingleton.Instance.globalVar = "A";
Debug.Log ("globalVar: " + MySingleton.Instance.globalVar);

以下是完整的教程:http://www.bivis.com.br/2016/05/04/unity-reusable-singleton-tutorial/

答案 4 :(得分:0)

这是我创建的设置。

首先创建此脚本:

MonoBehaviourUtility.cs

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;

static public class MonoBehaviourUtility 
{

    static public T GetManager<T>( ref T manager ) where T : MonoBehaviour
    {
        if (manager == null)
        {
            manager = (T)GameObject.FindObjectOfType( typeof( T ) );
            if (manager == null)
            {
                GameObject gameObject = new GameObject( typeof( T ).ToString() );
                manager = (T)gameObject.AddComponent( typeof( T ) );
            }
        }
        return manager;
    }

}

然后在任何课程中你想成为一个单身人士:

public class ExampleManager : MonoBehaviour 
{   
    static public ExampleManager sharedManager 
    {
        get 
        {
            return MonoBehaviourUtility.GetManager<ExampleManager>( ref _sharedManager );
        }
    }   
    static private ExampleManager _sharedManager;       
}

答案 5 :(得分:0)

不是为每个类创建一个单例。我建议您为单例创建一个通用类。我习惯遵循这种方法,这使我的生活非常轻松。

有关更多详细信息,请访问here

统一创建Unity C#类并使用以下代码

/// <summary>
/// Inherit from this base class to create a singleton.
/// e.g. public class MyClassName : Singleton<MyClassName> {}
/// </summary>
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
    // Check to see if we're about to be destroyed.
    private static bool m_ShuttingDown = false;
    private static object m_Lock = new object();
    private static T m_Instance;

    /// <summary>
    /// Access singleton instance through this propriety.
    /// </summary>
    public static T Instance
    {
        get
        {
            if (m_ShuttingDown)
            {
                Debug.LogWarning("[Singleton] Instance '" + typeof(T) +
                    "' already destroyed. Returning null.");
                return null;
            }

            lock (m_Lock)
            {
                if (m_Instance == null)
                {
                    // Search for existing instance.
                    m_Instance = (T)FindObjectOfType(typeof(T));

                    // Create new instance if one doesn't already exist.
                    if (m_Instance == null)
                    {
                        // Need to create a new GameObject to attach the singleton to.
                        var singletonObject = new GameObject();
                        m_Instance = singletonObject.AddComponent<T>();
                        singletonObject.name = typeof(T).ToString() + " (Singleton)";

                        // Make instance persistent.
                        DontDestroyOnLoad(singletonObject);
                    }
                }

                return m_Instance;
         }
      }
  }

  private void OnApplicationQuit()
  {
     m_ShuttingDown = true;
  }

  private void OnDestroy()
  {
    m_ShuttingDown = true;
  }
}

答案 6 :(得分:0)

这是来自Unity教程的简单代码。为了更好地理解,请打开link

using System.Collections.Generic;       //Allows us to use Lists. 

public class GameManager : MonoBehaviour
{

    public static GameManager instance = null;              //Static instance of GameManager which allows it to be accessed by any other script.
    private BoardManager boardScript;                       //Store a reference to our BoardManager which will set up the level.
    private int level = 3;                                  //Current level number, expressed in game as "Day 1".

    //Awake is always called before any Start functions
    void Awake()
    {
        //Check if instance already exists
        if (instance == null)

            //if not, set instance to this
            instance = this;

        //If instance already exists and it's not this:
        else if (instance != this)

            //Then destroy this. This enforces our singleton pattern, meaning there can only ever be one instance of a GameManager.
            Destroy(gameObject);    

        //Sets this to not be destroyed when reloading scene
        DontDestroyOnLoad(gameObject);

        //Get a component reference to the attached BoardManager script
        boardScript = GetComponent<BoardManager>();

        //Call the InitGame function to initialize the first level 
        InitGame();
    }

    //Initializes the game for each level.
    void InitGame()
    {
        //Call the SetupScene function of the BoardManager script, pass it current level number.
        boardScript.SetupScene(level);

    }



    //Update is called every frame.
    void Update()
    {

    }

答案 7 :(得分:0)

 using UnityEngine;

 public 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;
        DontDestroyOnLoad(gameObject);
        OnInit();
    }
    else if (instance != this)
    {
        Destroy(gameObject);
    }
  }

  protected virtual void OnInit()
  {

  }


}

游戏管理:

class GameManager : Singleton<GameManager> {


}

答案 8 :(得分:-1)

一种方法是制作一个场景,只是为了初始化你的游戏管理器:

public class GameManager : MonoBehaviour {
    static GameManager instance;

    //other codes

    void Awake() {
        DontDestroyOnLoad(transform.gameObject);
        instance = this;
    }

    //other codes
}

那就是你需要做的一切。然后在初始化游戏管理器后立即加载下一个场景,再也不会再回到这个场景。

看一下本教程: https://youtu.be/64uOVmQ5R1k?list=WL

修改 已将GameManager static instance;更改为static GameManager instance;