C# Unity 单例管理器类

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/13730112/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 09:36:32  来源:igfitidea点击:

Unity singleton manager classes

c#design-patternsunity3dsingleton

提问by DeviArt

In Unity, whats a good way to create a singleton game manager that can be accessed everywhere as a global class with static variables that will spit the same constant values to every class that pulls those values? And what would be the way to implement it in Unity? Do I have to attach it to a GameObject? Can it just be there in a folder without being in the scene visually?

在 Unity 中,创建一个可以作为全局类随处访问的单例游戏管理器的好方法是什么?在 Unity 中实现它的方法是什么?我必须将它附加到游戏对象吗?它可以只存在于文件夹中而不在视觉中出现在场景中吗?

采纳答案by Kay

Like always: it depends. I use singletons of both kinds, components attached to GameObjectand standalone classes not derived from MonoBehaviour. IMO the overall question is how are instances bound to the lifcycle of scenes, game objects, ... And not to forget sometimes it is more convenient to have a component especially referencing other MonoBehaviourobjects is easier and safer.

像往常一样:这取决于。我使用两种类型的单例,附加到的组件GameObject和不是从MonoBehaviour. IMO 的总体问题是实例如何绑定到场景、游戏对象等的生命周期......而且不要忘记有时拥有一个组件更方便,尤其是引用其他MonoBehaviour对象更容易和更安全。

  1. There are classes that just need to provide some values like for example a config class that needs to load settings from persistence layer when called. I design theese classes as simple singletons.
  2. On the other hand some objects need to know when a scene is started i.e. Startis called or have to perform actions in Updateor other methods. Then I implement them as component and attach them to a game object that survives loading new scenes.
  1. 有些类只需要提供一些值,例如需要在调用时从持久层加载设置的配置类。我将这些类设计为简单的单例。
  2. 另一方面,一些对象需要知道场景何时开始,即被Start调用或必须在Update或其他方法中执行操作。然后我将它们实现为组件并将它们附加到一个在加载新场景后仍然存在的游戏对象。

I designed component based singletons (type 2) with two parts: a persistent GameObjectcalled Main, which holds all components and a flat singleton (type 1) called MainComponentManagerfor managing it. Some demo code:

我设计了基于组件的单例(类型 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> ();
    }

Now other singletons that want to register as Maincomponent just look like:

现在其他想要注册为Main组件的单例看起来像:

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

回答by rygo6

This is the setup I have created.

这是我创建的设置。

First create this script:

首先创建这个脚本:

MonoBehaviourUtility.cs

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

}

Then in any class you want to be a singleton do this:

然后在任何你想成为单身人士的课程中这样做:

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

回答by Behnam Rasooli

One way to do it is to make a scene just to initialize your game manager like this:

一种方法是制作一个场景来初始化您的游戏管理器,如下所示:

public class GameManager : MonoBehaviour {
    static GameManager instance;

    //other codes

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

    //other codes
}

That's it, that's all you need to do. And then immediately after initializing the game manager, load the next scene and never come back to this scene again.

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

Have a look at this tutorial: https://youtu.be/64uOVmQ5R1k?list=WL

看看这个教程:https: //youtu.be/64uOVmQ5R1k?list =WL

Edit:Changed GameManager static instance;to static GameManager instance;

编辑:更改GameManager static instance;static GameManager instance;

回答by Fattie

Engineers who are new to Unity often don't notice that

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

you can't have a "singleton" in an ECS system.

在 ECS 系统中不能有“单身”。

It is meaningless.

这是毫无意义的。

All you have in Unity is GameObjects, at, XYZ positions. They can have components attached.

您在 Unity 中拥有的只是游戏对象,位于 XYZ 位置。它们可以附加组件。

It would be like trying to have "a singleton" or "inheritance" in .... Photoshop or Microsoft Word.

这就像在 Photoshop 或 Microsoft Word 中尝试“单例”或“继承”一样。

Photoshopfile - pixels at XY positions
Text editorfile - letters at X positions
Unityfile - GameObjects at XYZ positions

Photoshop文件 - XY 位置的像素
文本编辑器文件 - X 位置的字母
Unity文件 - XYZ 位置的游戏对象

It is "just that simple".

就是“就这么简单”。

You absolutely have to have a preload scene anyway, of course, in every Unity project.

无论如何,您绝对必须有一个预加载场景,当然,在每个 Unity 项目中。

Incredibly simple how-to: https://stackoverflow.com/a/35891919/294884

非常简单的操作方法:https: //stackoverflow.com/a/35891919/294884

It's so simple, it is a non-issue.

这很简单,这不是问题。

Once Unity includes a "built-in preload scene" (ie, to save you one click in creating one) this will finally never be discussed again.

一旦 Unity 包含“内置预加载场景”(即,为您节省一键创建场景),这将永远不会再被讨论。

(Note A - some of the languages you use to compile Componentsfor Unity of course have OO concepts; Unity itself has no connection to OO at all.)

(注意 A -您用于为 Unity编译组件的一些语言当然具有 OO 概念;Unity 本身与 OO 完全没有联系。)

(Note B - in the early days of Unity you'd see attempts at making code which "creates a game object on the fly - and keeps it unique - and attaches itself to it". Apart from being bizarre, just FWIW it's theoretically not possible to ensure uniqueness (actually not even within a frame). Again, it's just completely moot because it's a trivial non-issue, in Unity general behaviors just go on the preload scene.)

(注意 B - 在 Unity 的早期,您会看到尝试编写“即时创建游戏对象 - 并保持其唯一性 - 并将自身附加到它”的代码。除了奇怪之外,只是 FWIW 理论上不是可能确保唯一性(实际上甚至不在一个框架内)。同样,这完全没有实际意义,因为它是一个微不足道的非问题,在 Unity 中,一般行为只是在预加载场景中进行。)

回答by LITM

If this class is just for accessing global variables then you don't really need a singleton pattern for this, or use a GameObject.

如果此类仅用于访问全局变量,那么您实际上不需要单例模式,也不需要使用 GameObject。

Simply create a class with public static members.

只需创建一个具有公共静态成员的类。

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

The other solutions are fine but overkill if all you need is global access to variables.

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

回答by Bivis

I wrote a singleton class that makes easy to create singleton objects. Its is a MonoBehaviour script, so you can use the Coroutines. Its based on this Unity Wiki article, and I will add option to create it from Prefab later.

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

So you don't need to write the Singleton codes. Just download this Singleton.cs Base Class, add it to your project, and create your singleton extending it:

所以你不需要编写单例代码。只需下载这个 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());
  }
}

Now your MySingleton class is a singleton, and you can call it by Instance:

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

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

Here is a complete tutorial: http://www.bivis.com.br/2016/05/04/unity-reusable-singleton-tutorial/

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

回答by Muhammad Sulaiman

Instead of creating one singleton for each class. I would suggest you to create a generic class for singleton. i use to follow this method which make my life very easy.

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

For More detail visit here

欲了解更多详情,请访问这里

Or

或者

Create Unity C# class in unity and use following code

统一创建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;
  }
}

回答by Hassaan sohail

Here is a simple code taken from Unity Tutorial. for better understanding open the link

这是从 Unity 教程中获取的简单代码。为了更好地理解打开链接

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()
    {

    }