Dans Unity, quel est le bon moyen de créer un gestionnaire de jeu singleton accessible partout en tant que classe globale avec des variables statiques qui cracheront les mêmes valeurs constantes dans chaque classe qui extrait ces valeurs? Et quel serait le moyen de le mettre en œuvre dans Unity? Dois-je l'attacher à un GameObject? Peut-il simplement être là dans un dossier sans être visuellement dans la scène?
Comme toujours: ça dépend. J'utilise des singletons des deux types, des composants attachés à GameObject
et des classes autonomes non dérivées de MonoBehaviour
. OMI, la question générale est de savoir comment les instances sont liées au cycle de vie de scènes, d’objets de jeu, ... Et pour ne pas oublier parfois, il est plus pratique d’avoir un composant faisant particulièrement référence à d’autres objets MonoBehaviour
qui est plus sûr et plus sûr.
Start
est appelé ou doivent exécuter des actions dans Update
ou d'autres méthodes. Ensuite, je les implémente en tant que composant et les attache à un objet de jeu survivant en chargeant de nouvelles scènes.J'ai conçu des singletons basés sur des composants (type 2) avec deux parties: une GameObject
persistante appelée Main
, qui contient tous les composants, et un singleton plat (type 1) appelé MainComponentManager
pour la gérer. Un code de démonstration:
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> ();
}
Maintenant, les autres singletons qui veulent s’inscrire en tant que composant Main
ressemblent à ceci:
public class AudioManager : MonoBehaviour {
private static AudioManager instance = null;
public static AudioManager SharedInstance {
get {
if (instance == null) {
instance = MainComponentManger.AddMainComponent<AudioManager> ();
}
return instance;
}
}
Les ingénieurs qui débutent dans Unity ne le remarquent souvent pas.
Cela n'a pas de sens.
Tout ce que vous avez dans Unity, c'est GameObjects, aux positions XYZ. Ils peuvent avoir des composants attachés.
Ce serait comme essayer d'avoir "un singleton" ou un "héritage" dans .... Photoshop ou Microsoft Word.
Fichier Photoshop - pixels aux positions XY
Fichier de l'éditeur de texte - lettres aux positions X
Fichier Unity - GameObjects aux positions XYZ
C'est "aussi simple que cela".
Incroyablement simple comment: https://stackoverflow.com/a/35891919/294884
C'est si simple, c'est un non-problème.
Une fois que Unity aura inclus une "scène de préchargement intégrée" (c.-à-d. Pour vous sauver d'un clic et en créer un), cela ne sera finalement plus jamais discuté.
(Remarque A - certains des langages que vous utilisez pour compiler les composants} pour Unity ont bien sûr les concepts OO; Unity en soi n'a aucun lien avec OO.)
(Remarque B - dans les premiers jours d'Unity, vous voyiez des tentatives de création de code qui "crée un objet de jeu à la volée - et le maintient unique - et s'y attache". En plus d'être bizarre En fait, il est théoriquement impossible d’assurer l’unicité (en fait même pas dans un cadre). Encore une fois, c’est tout à fait inutile, car c’est un non-problème trivial, dans Unity, les comportements généraux vont simplement dans la scène de préchargement.
J'ai écrit une classe singleton qui facilite la création d'objets singleton. C'est un script MonoBehaviour, vous pouvez donc utiliser les Coroutines. Elle est basée sur cet article Unity Wiki , et je vais ajouter une option pour le créer à partir de Prefab ultérieurement.
Donc, vous n'avez pas besoin d'écrire les codes Singleton. Il suffit de télécharger cette classe de base Singleton.cs , de l’ajouter à votre projet et de créer votre singleton en l’étendant:
public class MySingleton : Singleton<MySingleton> {
protected MySingleton () {} // Protect the constructor!
public string globalVar;
void Awake () {
Debug.Log("Awoke Singleton Instance: " + gameObject.GetInstanceID());
}
}
Maintenant, votre classe MySingleton est un singleton, et vous pouvez l'appeler par instance:
MySingleton.Instance.globalVar = "A";
Debug.Log ("globalVar: " + MySingleton.Instance.globalVar);
Voici un tutoriel complet: http://www.bivis.com.br/2016/05/04/unity-reusable-singleton-tutorial/
Si cette classe sert uniquement à accéder à des variables globales, vous n'avez pas vraiment besoin d'un modèle singleton, ni d'un objet GameObject.
Créez simplement une classe avec des membres statiques publics.
public class Globals
{
public static int mStatic1 = 0;
public static float mStatic2 = 0.0f;
// ....etc
}
Les autres solutions sont correctes mais excessives si tout ce dont vous avez besoin est un accès global aux variables.
Au lieu de créer un singleton pour chaque classe. Je vous suggère de créer une classe générique pour singleton. J'ai l'habitude de suivre cette méthode qui rend ma vie très facile.
Pour plus de détails, visitez ici
Ou
Créez la classe Unity C # dans l'unité et utilisez le code suivant
/// <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;
}
}
C'est la configuration que j'ai créée.
Commencez par créer ce script:
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;
}
}
Dans n'importe quelle classe, vous voulez être un singleton, procédez comme suit:
public class ExampleManager : MonoBehaviour
{
static public ExampleManager sharedManager
{
get
{
return MonoBehaviourUtility.GetManager<ExampleManager>( ref _sharedManager );
}
}
static private ExampleManager _sharedManager;
}