Vi vários vídeos e tutoriais para criar objetos singleton no Unity, principalmente para um GameManager
, que parecem usar abordagens diferentes para instanciar e validar um singleton.
Existe uma abordagem correta, ou melhor, preferida para isso?
Os dois exemplos principais que encontrei são:
Primeiro
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;
}
}
A principal diferença que posso ver entre os dois é:
A primeira abordagem tentará navegar na pilha de objetos do jogo para encontrar uma instância da GameManager
qual, mesmo que isso só aconteça (ou só aconteça), uma vez parece que pode ser muito não otimizado à medida que as cenas aumentam de tamanho durante o desenvolvimento.
Além disso, a primeira abordagem marca o objeto a não ser excluído quando o aplicativo muda de cena, o que garante que o objeto persista entre as cenas. A segunda abordagem não parece aderir a isso.
A segunda abordagem parece estranha, pois no caso em que a instância é nula no getter, ela cria um novo GameObject e atribui um componente GameManger a ele. No entanto, isso não pode ser executado sem que o componente GameManager já esteja anexado a um objeto na cena, então isso está me causando alguma confusão.
Existem outras abordagens recomendadas ou um híbrido dos dois acima? Existem muitos vídeos e tutoriais sobre singletons, mas todos diferem tanto que é difícil fazer comparações entre os dois e, portanto, uma conclusão sobre qual é a melhor / preferida abordagem.
fonte
GameManager
deve fazer, mas de como garantir que exista apenas uma instância do objeto e a melhor maneira de aplicá-lo.Respostas:
Depende, mas geralmente eu uso um terceiro método. O problema com os métodos que você usou é que, no início, o objeto é incluído, ele não os remove da árvore e ainda pode ser criado instanciando muitas chamadas, o que pode tornar as coisas realmente confusas.
O problema com as duas implementações é que elas não destroem um objeto criado posteriormente. Pode funcionar, mas é possível jogar uma chave de macaco nos trabalhos que podem resultar em um erro de depuração muito difícil na linha. Certifique-se de verificar em Despertar se já existe uma instância e, se houver, destruindo a nova instância.
fonte
OnDestroy() { if (this == _instance) { _instance = null; } }
, se quiser ter uma instância diferente em cada cena.Instance
propriedade estática é apagada. Um exemplo de um que não pode ser encontrado em uma das respostas abaixo , ou wiki.unity3d.com/index.php/Singleton (que pode estar desatualizado, mas parece funcionar com minhas experiências com ele)Aqui está um resumo rápido:
Portanto, se tudo o que importa é o acesso global, todos os três terão o que você precisa. O uso do padrão Singleton pode ser um pouco ambíguo sobre se queremos instanciação lenta, exclusividade forçada ou acesso global. Portanto, pense cuidadosamente sobre o motivo pelo qual você está alcançando o singleton e escolha uma implementação que acerte esses recursos. do que usar um padrão para todos os três quando você precisar apenas de um.
(por exemplo, se meu jogo sempre tiver um GameManager, talvez eu não me importe com instâncias preguiçosas - talvez seja apenas o acesso global com existência garantida e exclusividade -, nesse caso, uma classe estática obtém exatamente esses recursos de maneira concisa, sem considerações de carregamento de cena)
... mas definitivamente não use o Método 1 como está escrito. O Find pode ser pulado mais facilmente com a abordagem Awake () do Method2 / 3, e se mantivermos o gerente entre as cenas, é provável que desejemos matar duplicados, caso possamos carregar entre duas cenas com um gerente já nelas.
fonte
A melhor implementação de um
Singleton
padrão genérico para o Unity que conheço é (é claro) o meu.Ele pode fazer tudo , e fá-lo perfeitamente e de forma eficiente :
Outras vantagens:
OnApplicationQuit()
. (E o faz com uma única bandeira global, em vez de cada tipo de singleton ter o seu próprio)E porque compartilhar é cuidar , aqui está:
fonte
MonoBehaviour
, e acredito que qualquer classe usada pelo Unity diretamente em geral.SampleSingletonClass : Singleton
,SampleSingletonClass.Instance
volta comSampleSingletonClass does not contain a definition for Instance
.Singleton<>
classe genérica . É por isso que o genérico é filho daSingleton
classe base .Gostaria apenas de acrescentar que pode ser útil ligar
DontDestroyOnLoad
se você quiser que o seu singleton persista nas cenas.fonte
Outra opção pode ser dividir a classe em duas partes: uma classe estática regular para o componente Singleton e um MonoBehaviour que atua como um controlador para a instância singleton. Dessa forma, você tem total controle sobre a construção do singleton, e ele persistirá nas cenas. Isso também permite adicionar controladores a qualquer objeto que possa precisar dos dados do singleton, em vez de precisar percorrer a cena para encontrar um componente específico.
fonte
Aqui está minha implementação de uma classe abstrata singleton abaixo. Aqui está como ele se compara aos 4 critérios
Ele tem algumas outras vantagens em comparação com alguns dos outros métodos aqui:
FindObjectsOfType
que é um assassino de desempenhoÉ seguro para discussão
fonte
OnApplicationQuitCallback
eOnEnableCallback
como emabstract
vez de apenasvirtual
métodos vazios ? Pelo menos no meu caso, eu não tenho nenhuma lógica de encerrar / ativar e ter uma substituição vazia parece suja. Mas eu posso estar perdendo alguma coisa.OnApplicationQuitCallback
eOnEnableCallback
: tê-lo como métodos virtuais meio que o torna menos óbvio. Talvez um pensamento um pouco estranho, mas pelo que me lembro, esse era o meu racional.Na verdade, existe uma maneira pseudo-oficial de usar o Singleton no Unity. Aqui está a explicação: basicamente crie uma classe Singleton e faça com que seus scripts sejam herdados dessa classe.
fonte
Vou seguir minha implementação também para as gerações futuras.
Para mim, essa linha
Destroy(gameObject.GetComponent(instance.GetType()));
é muito importante porque uma vez deixei um script singleton em outro gameObject em uma cena e todo o objeto do jogo estava sendo excluído. Isso destruirá o componente apenas se ele já existir.fonte
Eu escrevi uma classe singleton que facilita a criação de objetos singleton. É um script MonoBehaviour, para que você possa usar as Coroutines. É baseado neste artigo do Unity Wiki , e adicionarei a opção de criá-lo no Prefab posteriormente.
Portanto, você não precisa escrever os códigos Singleton. Basta baixar esta classe base Singleton.cs , adicioná-la ao seu projeto e criar seu singleton estendendo-o:
Agora sua classe MySingleton é um singleton e você pode chamá-lo por Instância:
Aqui está um tutorial completo: http://www.bivis.com.br/2016/05/04/unity-reusable-singleton-tutorial/
fonte