He visto varios videos y tutoriales para crear objetos singleton en Unity, principalmente para a GameManager
, que parecen usar diferentes enfoques para crear instancias y validar un singleton.
¿Existe un enfoque correcto, o más bien preferido, para esto?
Los dos ejemplos principales que he encontrado son:
primero
public class GameManager
{
private static GameManager _instance;
public static GameManager Instance
{
get
{
if(_instance == null)
{
_instance = GameObject.FindObjectOfType<GameManager>();
}
return _instance;
}
}
void Awake()
{
DontDestroyOnLoad(gameObject);
}
}
Segundo
public class GameManager
{
private static GameManager _instance;
public static GameManager Instance
{
get
{
if(_instance == null)
{
instance = new GameObject("Game Manager");
instance.AddComponent<GameManager>();
}
return _instance;
}
}
void Awake()
{
_instance = this;
}
}
La principal diferencia que puedo ver entre los dos es:
El primer enfoque intentará navegar por la pila de objetos del juego para encontrar una instancia de la GameManager
cual, aunque esto solo suceda (o solo suceda), una vez parezca que podría no ser optimizada a medida que las escenas crecen en tamaño durante el desarrollo.
Además, el primer enfoque marca el objeto para que no se elimine cuando la aplicación cambia de escena, lo que garantiza que el objeto persista entre escenas. El segundo enfoque no parece adherirse a esto.
El segundo enfoque parece extraño, ya que en el caso de que la instancia sea nula en el captador, creará un nuevo GameObject y le asignará un componente GameManger. Sin embargo, esto no puede ejecutarse sin tener este componente GameManager ya conectado a un objeto en la escena, por lo que esto me está causando cierta confusión.
¿Hay otros enfoques que se recomendarían, o un híbrido de los dos anteriores? Hay muchos videos y tutoriales sobre singletons, pero todos difieren tanto que es difícil establecer comparaciones entre los dos y, por lo tanto, llegar a una conclusión sobre cuál es el enfoque mejor / preferido.
fuente
GameManager
deberían hacer, sino cómo asegurarse de que solo haya una instancia del objeto y la mejor manera de hacer cumplir eso.Respuestas:
Depende, pero generalmente uso un tercer método. El problema con los métodos que usó es que, en el caso de que el objeto esté incluido, para empezar, no los eliminará del árbol, y aún pueden crearse instanciando demasiadas llamadas, lo que podría hacer que las cosas sean realmente confusas.
El problema con ambas implementaciones es que no destruyen un objeto que se crea más tarde. Podría funcionar, pero uno podría lanzar una llave inglesa a las obras que podría resultar en un error muy difícil de depurar en el futuro. Asegúrese de verificar en Despertar si ya hay una instancia y, de ser así, destruir la nueva instancia.
fuente
OnDestroy() { if (this == _instance) { _instance = null; } }
, si desea tener una instancia diferente en cada escena.Instance
propiedad estática se borra. Un ejemplo de uno que no se puede encontrar en una de las respuestas a continuación , o wiki.unity3d.com/index.php/Singleton (que podría estar desactualizado, pero parece funcionar a partir de mi experimentación con él)Aquí hay un resumen rápido:
Entonces, si todo lo que le importa es el acceso global, los tres le dan lo que necesita. El uso del patrón Singleton puede ser un poco ambiguo sobre si queremos una instanciación perezosa, unicidad forzada o acceso global, así que asegúrese de pensar cuidadosamente por qué está buscando el singleton y elija una implementación que obtenga esas características correctas, en lugar de que usar un estándar para los tres cuando solo necesitas uno.
(por ejemplo, si mi juego siempre tendrá un GameManager, tal vez no me preocupe la instanciación perezosa, tal vez sea solo el acceso global con existencia garantizada y singularidad lo que me importa), en cuyo caso una clase estática me ofrece exactamente esas características de manera muy concisa, sin consideraciones de carga de escena)
... pero definitivamente no use el Método 1 como está escrito. El Find se puede omitir más fácilmente con el enfoque Awake () del Método 2/3, y si mantenemos al administrador en varias escenas, es muy probable que deseemos matar duplicados, en caso de que alguna vez carguemos entre dos escenas con un administrador ya en ellas.
fuente
La mejor implementación de un
Singleton
patrón genérico para Unity que conozco es (por supuesto) el mío.Puede hacer todo , y lo hace de manera ordenada y eficiente :
Otras ventajas:
OnApplicationQuit()
. (Y lo hace con una sola bandera global, en lugar de que cada tipo de singleton tenga la suya propia)Y porque compartir es cariñoso , aquí está:
fuente
MonoBehaviour
, y creo que cualquier clase utilizada por Unity directamente en general.SampleSingletonClass : Singleton
,SampleSingletonClass.Instance
vuelve conSampleSingletonClass does not contain a definition for Instance
.Singleton<>
clase genérica . Es por eso que el genérico es un hijo de laSingleton
clase base .Solo me gustaría agregar que puede ser útil llamar
DontDestroyOnLoad
si desea que su singleton persista en las escenas.fuente
Otra opción podría ser dividir la clase en dos partes: una clase estática regular para el componente Singleton y un MonoBehaviour que actúa como controlador para la instancia de singleton. De esta manera, tiene control total sobre la construcción del singleton, y persistirá en todas las escenas. Esto también le permite agregar controladores a cualquier objeto que pueda necesitar los datos del singleton, en lugar de tener que buscar en la escena para encontrar un componente en particular.
fuente
Aquí está mi implementación de una clase abstracta singleton a continuación. Así es como se compara con los 4 criterios
Tiene un par de otras ventajas en comparación con algunos de los otros métodos aquí:
FindObjectsOfType
cuál es un asesino de rendimientoEs hilo seguro
fuente
OnApplicationQuitCallback
yOnEnableCallback
como enabstract
lugar de solovirtual
métodos vacíos ? Al menos en mi caso, no tengo ninguna lógica para salir / habilitar y tener una anulación vacía se siente sucio. Pero podría estar perdiendo algo.OnApplicationQuitCallback
yOnEnableCallback
: tenerlo como métodos virtuales lo hace menos obvio. Tal vez un pensamiento un poco extraño, pero por lo que recuerdo, ese era mi racional.En realidad, hay una forma pseudo oficial de usar Singleton en Unity. Aquí está la explicación, básicamente crea una clase Singleton y haz que tus scripts hereden de esa clase.
fuente
Voy a cambiar mi implementación también para las generaciones futuras.
Para mí, esta línea
Destroy(gameObject.GetComponent(instance.GetType()));
es muy importante porque una vez que había dejado un script singleton en otro GameObject en una escena y todo el objeto del juego estaba siendo eliminado. Esto solo destruirá el componente si ya existe.fuente
Escribí una clase singleton que facilita la creación de objetos singleton. Es un script MonoBehaviour, por lo que puede usar las Coroutines. Se basa en esto artículo de Unity Wiki , y agregaré la opción para crearlo desde Prefab más adelante.
Por lo tanto, no necesita escribir los códigos Singleton. Simplemente descargue esta clase base Singleton.cs , agréguela a su proyecto y cree su singleton extendiéndola:
Ahora su clase MySingleton es singleton, y puede llamarla por Instancia:
Aquí hay un tutorial completo: http://www.bivis.com.br/2016/05/04/unity-reusable-singleton-tutorial/
fuente