Unity3D: Agregador automático de gerenciador de scripts

Introdução


Este artigo focará em um tipo de organização da interação entre gerenciadores de scripts (chamados singletones) e, especificamente, no uso de uma classe agregadora separada, que contém links para todos os gerenciadores de instância. A ideia de criar uma classe agregadora veio à minha mente depois de ler este artigo .

Tarefas


Cheguei à conclusão de que martelar gerentes na classe agregadora com suas próprias canetas toda vez é extremamente inconveniente. Ao criar um novo gerente, você terá que abrir a classe agregadora e fazer alterações. Com experiência suficiente, você pode automatizar esses processos tediosos. Assim, foram definidas as tarefas: criação automática de singletones e seu coletor automático.

Class-Singleton


using UnityEngine;
public class Singleton<T>: MonoBehaviour where T: MonoBehaviour
{
	public static T instance { get; private set; }

	public void Awake()
	{
		if (instance == null)
		{
			instance = GetComponent<T>();
			ManagersAregator.addManager(instance);
		}
		else
		{
			Debug.Log($"<color=red>   {typeof(T).ToString()}</color>");
			Destroy(gameObject);
		}
	}

	public void OnDestroy()
	{
		ManagersAregator.removeManager<T>();
	}
}

A implementação clássica de singleton no Unity usa a variável estática da instância . O método Awake () verifica se a instância está definida . Caso contrário, obteremos uma referência à instância da classe usando a palavra-chave this . Mas desde em uma implementação particular, uma variável “modelo” de classe T é usado, este não poderia ser usado. Mas podemos facilmente obter o componente T do objeto em que o script está pendurado. Se a instância for definida, ela deverá ser destruída. Assim, o objeto estará sempre no palco em uma única cópia.

No método Awake ()um novo elemento é adicionado à classe agregadora e, no método OnDestroy () , o elemento é removido da classe agregadora.

A criação de um singleton da classe MyClass é a seguinte:

public class MyClass: Singleton<MyClass>

Se você precisar usar o método Awake () na classe MyClass , precisará usar a palavra-chave base ( mais sobre base e herança ):

new void Awake()
	{
		base.Awake(); //  Awake() -
		// 
	}

Agregador de classe


using System.Collections.Generic;
using UnityEngine;

public static class ManagersAregator
{
	static Dictionary<string, MonoBehaviour> Managers = new Dictionary<string, MonoBehaviour>();

	public static void addManager<T>(T newManager)
	{
		string keyWord = typeof(T).ToString();

		if(Managers.ContainsKey(keyWord))
		{
			Debug.Log($"[ManagersAregator]  -{newManager}-   -{keyWord}-  ");
		}
		else
		{
			Managers.Add(keyWord, newManager as MonoBehaviour);
			Debug.Log($"<color=green>[ManagersAregator]    -{newManager}-   -{keyWord}-</color>");
		}
	}

	public static T getManager<T>(string callback) where T: Singleton<T>
	{
		string keyWord = typeof(T).ToString();

		if(Managers.ContainsKey(keyWord))
		{
			Debug.Log($"<color=yellow>[{callback}]   -{keyWord}-</color>");

			MonoBehaviour mbTemp = null;
			T manager = null;

			if(Managers.TryGetValue(keyWord, out mbTemp))
			{
				manager = (T)mbTemp;
				Debug.Log($"<color=green>[{callback}]  -{manager}- </color>");
			}
			else
			{
				Debug.Log($"<color=red>[{callback}]    -{keyWord}-</color>");
			}

			return manager;
		}

		Debug.Log($"<color=red>[ManagersAregator]    -{keyWord}-   .</color>");
		return null;
	}

	public static void removeManager<T>()
	{
		string keyWord = typeof(T).ToString();

		if(Managers.ContainsKey(keyWord))
		{
			Managers.Remove(keyWord);
			Debug.Log($"[ManagersAregator]    -{keyWord}-   ");
		}
		else
		{
			Debug.Log($"[ManagersAregator]    -{keyWord}-   .");
		}
	}
}

A classe é estática, ou seja, ele não pode ter uma instância no palco. Assim, estará disponível a partir de qualquer cena a qualquer momento.

Todos os gerenciadores de scripts (singletones) são armazenados no dicionário Gerenciadores . A chave para cada gerente é o nome da classe desse gerente. Talvez os novatos em programação façam a pergunta: "Bah, mas o que esse dicionário armazena o MonoBehaviour e todas as classes herdam de Singleton ?". Essa é uma boa pergunta, cuja resposta é a chave para a implementação de um agregador automático de gerentes de qualquer classe.

Na programação, existe o conceito de conversão do tipo upcasting para uma classe base. Devido ao fato de todas as classes no Unity herdarem do MonoBehaviour ,eles podem ser aplicados ao MonoBehabiour . Portanto, o dicionário Gerenciadores contém apenas objetos da classe MonoBehaviour .

Considere os métodos da classe agregadora:

void addManager<T>(T newManager)

Este método é chamado no método Awake () da classe Singleton . O argumento é uma variável estática da classe de instância . Em seguida, uma chave é criada pelo nome da classe à qual a instância pertence e o gerente é adicionado ao dicionário.

T getManager<T>(string callback) where T: Singleton<T>

A função usa como argumento uma string com o nome da classe de onde o método é chamado. Isso é feito exclusivamente para depuração conveniente (o console exibe a classe de onde o método é chamado). Um exemplo de uso desse método na classe AnotherMyClass :

public class AnotherMyClass: MonoBehaviour
	void Start()
	{
		string cb = GetType().ToString(); //     
		MyClass MC = ManagersAregator.getManager<MyClass >(cb);
	}

Uma mensagem ficará pendurada no console: " Hu ** I, refaça o [AnotherMyClass] Manager -MyClass- recebido".

void removeManager<T>()

Exclui um gerente do tipo T do dicionário, se estiver contido no dicionário.

Sumário


De fato, das três funções da classe agregadora, um desenvolvedor precisa usar apenas o método getManager . Uma vantagem especial é a boa visibilidade das mensagens de depuração para todas as ocasiões. Opcionalmente, é claro, você pode desativá-los. Acredito que será muito conveniente ver em que momento, qual classe está tentando obter algo e o que está tentando obter.

Espero que este artigo tenha sido útil para você e você tenha descoberto algo útil para si mesmo!

All Articles