Classi manager Unity singleton

In Unity, che cosa è un buon modo per creare un gestore di giochi Singleton accessibile ovunque come una class globale con variabili statiche che sputeranno gli stessi valori costanti per ogni class che estrae quei valori? E quale sarebbe il modo di implementarlo in Unity? Devo collegarlo a un GameObject? Può essere solo lì in una cartella senza essere visivamente nella scena?

Come sempre: dipende. Uso singleton di entrambi i tipi, componenti collegati a GameObject e classi indipendenti non derivate da MonoBehaviour . IMO la domanda generale è come sono le istanze legate al ciclo di vita di scene, oggetti di gioco, … E non dimenticare che a volte è più conveniente avere un componente che fa riferimento in particolare ad altri oggetti MonoBehaviour è più facile e più sicuro.

  1. Ci sono classi che devono solo fornire alcuni valori come ad esempio una class di configurazione che deve caricare le impostazioni dal livello di persistenza quando viene chiamata. Io disegno queste classi come semplici singleton.
  2. D’altra parte alcuni oggetti devono sapere quando una scena viene avviata, ovvero Start viene chiamato o deve eseguire azioni in Update o altri metodi. Poi li implemento come componente e li attengo a un object di gioco che sopravvive caricando nuove scene.

Ho progettato singleton basati su componenti (tipo 2) con due parti: un GameObject persistente chiamato Main , che contiene tutti i componenti e un singleton piatto (tipo 1) chiamato MainComponentManager per MainComponentManager . Qualche codice demo:

 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  () where T : UnityEngine.Component { T t = SharedInstance.main.GetComponent (); if (t != null) { return t; } return SharedInstance.main.AddComponent  (); } 

Ora altri singleton che vogliono registrarsi come componente Main sono simili a:

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

Gli ingegneri che sono nuovi a Unity spesso non se ne accorgono

non puoi avere un “singleton” in un sistema ECS.

Non ha senso

Tutto ciò che hai in Unity è GameObjects, in, posizioni XYZ. Possono avere componenti collegati.

Sarebbe come cercare di avere “un singleton” o “ereditarietà” in …. Photoshop o Microsoft Word.

File Photoshop : pixel nelle posizioni XY
File di editor di testo – lettere in posizione X
Unity file – GameObjects alle posizioni XYZ

È “solo così semplice”.

Devi assolutamente avere una scena di precarico comunque, ovviamente, in ogni progetto Unity.

How-to incredibilmente semplice: https://stackoverflow.com/a/35891919/294884

È così semplice, è un non-problema.

Una volta Unity include una “scena precaristica incorporata” (cioè, per risparmiare un clic nella creazione di uno), questo non verrà mai più discusso.

(Nota A – alcune delle lingue che usi per compilare Components for Unity hanno ovviamente concetti OO: Unity stesso non ha alcuna connessione con OO).

(Nota B – nei primi giorni di Unity vedresti tentativi di creare codice che “crea al volo un object di gioco – e lo mantiene unico – e si attacca ad esso” . Oltre ad essere bizzarro, solo FWIW non è teoricamente è ansible garantire l’unicità (in realtà nemmeno all’interno di una cornice). Ancora una volta, è solo del tutto discutibile perché è un banale non-problema, in Unity i comportamenti generali vanno semplicemente nella scena del precarico.)

Se questa class serve solo per accedere a variabili globali, non hai realmente bisogno di un modello singleton o di un GameObject.

Basta creare una class con membri statici pubblici.

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

Le altre soluzioni vanno bene, ma sono eccessive se tutto ciò che serve è un accesso globale alle variabili.

Ho scritto una class singleton che semplifica la creazione di oggetti singleton. È uno script MonoBehaviour, quindi puoi usare le Coroutine. Si basa su questo articolo Unity Wiki , e aggiungerò l’opzione per crearlo da Prefab in seguito.

Quindi non è necessario scrivere i codici Singleton. Basta scaricare questa class base Singleton.cs , aggiungerla al progetto e creare il singleton estendendolo:

 public class MySingleton : Singleton { protected MySingleton () {} // Protect the constructor! public string globalVar; void Awake () { Debug.Log("Awoke Singleton Instance: " + gameObject.GetInstanceID()); } } 

Ora la tua class MySingleton è un singleton e puoi chiamarla per istanza:

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

Ecco un tutorial completo: http://www.bivis.com.br/2016/05/04/unity-reusable-singleton-tutorial/

Questa è la configurazione che ho creato.

Per prima cosa crea questo script:

MonoBehaviourUtility.cs

 using UnityEngine; using System.Collections; using System.Collections.Generic; using System.IO; static public class MonoBehaviourUtility { static public T GetManager( 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; } } 

Quindi in qualsiasi class vuoi essere un singleton fai questo:

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

Un modo per farlo è creare una scena solo per inizializzare il tuo game manager in questo modo:

 public class GameManager : MonoBehaviour { GameManager static instance; //other codes void Awake() { DontDestroyOnLoad(transform.gameObject); instance = this; } //other codes } 

È tutto, è tutto ciò che devi fare. E subito dopo aver inizializzato il game manager, caricare la scena successiva e non tornare più su questa scena.

Dai un’occhiata a questo tutorial: https://youtu.be/64uOVmQ5R1k?list=WL