Unity3D: Automatic Script Manager Aggregator

Introduction


This article will focus on one type of organization of interaction between script managers (called singletones), and specifically, the use of a separate aggregator class, which contains links to all instance managers. The idea to create an aggregator class came to my mind after reading this article .

Tasks


I came to the conclusion that to hammer managers into the aggregator class with their own pens every time is wildly inconvenient. When creating a new manager, you will have to open the aggregator class and make changes. With enough experience, you can automate such tedious processes. Thus, the tasks were set: automatic creation of singletones and their automatic collector.

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>();
	}
}

The classic singleton implementation in Unity uses the instance static variable . The Awake () method checks to see if instance is defined . If not, we get a reference to the class instance using the this keyword . But since in a particular implementation, a “template” variable of class T is used, this could not be used. But we can easily get the T component from the object on which the script hangs. If instance is defined, then it must be destroyed. Thus, the object will always be on stage in a single copy.

In the Awake () methoda new element is added to the aggregator class, and in the OnDestroy () method, the element is removed from the aggregator class.

Creating a singleton of the MyClass class is as follows:

public class MyClass: Singleton<MyClass>

If you need to use the Awake () method in the MyClass class , then you need to use the base keyword ( more about base and inheritance ):

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

Class aggregator


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}-   .");
		}
	}
}

The class is static, i.e. he cannot have an instance on stage. Thus, it will be available from any scene at any time.

All script managers (singletones) are stored in the Managers dictionary . The key for each manager is the class name of that manager. Perhaps newcomers to programming will ask the question: “Bah, but what does this dictionary store MonoBehaviour , and all classes inherit from Singleton ?”. This is a good question, the answer to which is the key to the implementation of an automatic aggregator of managers of any classes.

In programming, there is the concept of upcasting - type conversion to a base class. Due to the fact that all classes in Unity inherit from MonoBehaviour ,they can be applied to MonoBehabiour . Therefore, the Managers dictionary contains only objects of the MonoBehaviour class .

Consider the methods of the aggregator class:

void addManager<T>(T newManager)

This method is called in the Awake () method of the Singleton class . The argument is a static variable of the instance class . Next, a key is created by the name of the class to which instance belongs , and the manager is added to the dictionary.

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

The function takes as an argument a string with the name of the class from where the method is called. This is done exclusively for convenient debugging (the console displays the class from where the method is called). An example of using this method in the AnotherMyClass class :

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

A message will hang in the console: " Hu ** I, redo [AnotherMyClass] Manager -MyClass- received."

void removeManager<T>()

Deletes a manager of type T from the dictionary if it is contained in the dictionary.

Summary


In fact, of the three functions of the aggregator class, a developer only needs to use the getManager method . A special plus is the good visibility of debug messages for all occasions. Optionally, of course, you can turn them off. I believe that it will be very convenient to see at what point in time, which class is trying to get something and what it is trying to get.

I hope this article was useful to you and you found out something useful for yourself!

All Articles