web-dev-qa-db-de.com

Unity-Singleton-Manager-Klassen

Was ist ein guter Weg, um in Unity einen Singleton-Spiel-Manager zu erstellen, auf den überall als globale Klasse mit statischen Variablen zugegriffen werden kann, der die gleichen konstanten Werte für jede Klasse spuckt, die diese Werte abruft? Und wie würde man es in Unity implementieren? Muss ich es an ein GameObject anhängen? Kann es einfach in einem Ordner vorhanden sein, ohne visuell in der Szene zu sein?

31
DeviArt

Wie immer: es kommt darauf an. Ich verwende Singletons beider Arten, an GameObject angehängte Komponenten und eigenständige Klassen, die nicht von MonoBehaviour abgeleitet sind. IMO die allgemeine Frage ist, wie Instanzen an den Lebenszyklus von Szenen, Spielobjekten, ... gebunden sind. Und nicht zu vergessen, manchmal ist es bequemer, wenn eine Komponente speziell auf andere MonoBehaviour-Objekte verweist, die einfacher und sicherer sind.

  1. Es gibt Klassen, die nur einige Werte angeben müssen, z. B. eine Konfigurationsklasse, die beim Aufruf Einstellungen aus der Persistenzschicht laden muss. Ich entwerfe diese Klassen als einfache Singletons.
  2. Auf der anderen Seite müssen einige Objekte wissen, wann eine Szene gestartet wird, d. H. Start wird aufgerufen oder müssen Aktionen in Update oder anderen Methoden ausführen. Dann implementiere ich sie als Komponente und füge sie einem Spielobjekt hinzu, das das Laden neuer Szenen überlebt.

Ich habe komponentenbasierte Singletons (Typ 2) mit zwei Teilen entworfen: eine permanente GameObject mit dem Namen Main, die alle Komponenten enthält, und ein flaches Singleton (Typ 1) mit dem Namen MainComponentManager zum Verwalten. Einige Demo-Code:

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

Nun sehen andere Singletons, die sich als Main-Komponente registrieren möchten, einfach so aus:

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

Ingenieure, die neu bei Unity sind, bemerken das oft nicht

sie können kein "Singleton" in einem ECS-System haben.

Es ist sinnlos.

Alles, was Sie in Unity haben, ist GameObjects, XYZ-Positionen. Sie können Komponenten angebracht haben.

Es wäre, als würde man versuchen, "ein Einzelstück" oder "Vererbung" in .... Photoshop oder Microsoft Word zu haben.

Photoshop -Datei - Pixel an XY-Positionen
Texteditor Datei - Buchstaben an X-Positionen
Unity file - GameObjects an XYZ-Positionen

Es ist "so einfach".

Natürlich müssen Sie in jedem Unity-Projekt unbedingt eine Vorladeszene haben.

Unglaublich einfache Anleitung: https://stackoverflow.com/a/35891919/294884

Es ist so einfach, es ist kein Thema.

Sobald Unity eine "eingebaute Preload-Szene" enthält (dh, um einen Klick beim Erstellen zu sparen), wird dies schließlich nie mehr diskutiert.

(Hinweis A - einige der Sprachen, die Sie zum Kompilieren von Komponenten verwenden für Unity haben natürlich Konzepte OO; Unity selbst hat überhaupt keine Verbindung zu OO.)

(Anmerkung B - In den frühen Tagen von Unity gab es Versuche, Code zu erzeugen, der "Ein Spielobjekt wird schnell erstellt - und es bleibt einzigartig - und es bindet sich daran". Abgesehen davon, dass es bizarr ist Nur bei FWIW ist es theoretisch nicht möglich, die Eindeutigkeit zu gewährleisten (tatsächlich nicht einmal innerhalb eines Frames). Wiederum ist es nur völlig irrelevant, da dies ein triviales Nicht-Thema ist.

4
Fattie

Ich habe eine Singleton-Klasse geschrieben, die das Erstellen von Einzelobjekten erleichtert. Es ist ein MonoBehaviour-Skript, daher können Sie die Coroutines verwenden. Es basiert auf diesem Unity Wiki-Artikel , und ich werde später eine Option hinzufügen, um es aus dem Prefab zu erstellen.

Sie müssen also die Singleton-Codes nicht schreiben. Laden Sie einfach diese Singleton.cs-Basisklasse herunter, fügen Sie sie Ihrem Projekt hinzu und erstellen Sie Ihr Singleton, um es zu erweitern:

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

  public string globalVar;

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

Jetzt ist Ihre MySingleton-Klasse ein Singleton, und Sie können es per Instanz aufrufen:

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

Hier ist ein komplettes Tutorial: http://www.bivis.com.br/2016/05/04/unity-reusable-singleton-tutorial/

1
Bivis

Wenn diese Klasse nur für den Zugriff auf globale Variablen gedacht ist, benötigen Sie hierfür kein Singleton-Muster oder verwenden ein GameObject. 

Erstellen Sie einfach eine Klasse mit öffentlichen statischen Mitgliedern.

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

Die anderen Lösungen sind in Ordnung, aber übertrieben, wenn Sie nur globalen Zugriff auf Variablen benötigen.

1
LITM

Anstatt für jede Klasse ein Singleton zu erstellen. Ich würde vorschlagen, dass Sie eine generische Klasse für Singleton erstellen. Ich folge dieser Methode, die mein Leben sehr einfach macht.

Für mehr Details besuchen Sie hier

Oder

Erstellen Sie die Unity C # -Klasse in Unity und verwenden Sie den folgenden Code

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

0

Dies ist das Setup, das ich erstellt habe.

Erstellen Sie zuerst dieses Skript:

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

}

Dann machen Sie in jeder Klasse, die Sie Singleton sein möchten, folgendes:

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

Hier ist ein einfacher Code aus Unity Tutorial. zum besseren verständnis öffne den 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()
    {

    }
0
Hassaan sohail