UI Manager

Having problems organizing UI? Great news, the struggle is over! I will tell you all about the pattern I’m using in most of my projects.

In the majority of cases, we need to see a single UI window at a time, like the main menu, pause menu or in-game window. We might want to bring up a window from many different places in our code. And we want to disable other windows. So we need a method which would disable all windows and enable the one we want and we want to be able to access it from anywhere in the code. Let’s create a new class, call it UIManager and make it a Singleton, using this base class (use Singletons with caution though!).

To switch between different windows more easily, I will make use of enumerators. For now, let’s assume that we will have three windows: the main menu, in-game and pause. Also, we might want to have none. So here is how the UIManager looks:

public enum Window
{
    None,
    MainMenu,
    InGame,
    Pause
}

public class UIManager : Singleton<UIManager>
{
    public void SetWindow(Window window)
    {
        //switch windows here
    }
}

Now, let’s think about the windows. Each window will have different buttons and will behave slightly different than others. So it is a good idea to make a script for each of them.

using UnityEngine;

public class MainMenu : MonoBehaviour
{
}
using UnityEngine;

public class InGame : MonoBehaviour
{
}
using UnityEngine;

public class Pause : MonoBehaviour
{
}

Now let’s declare them in the UIManager like so:

[SerializeField]
private MainMenu mainMenu;
[SerializeField]
private InGame inGame;
[SerializeField]
private Pause pause;

We don’t want other scripts to know about these windows so I made them private. But we still want to assign them through the inspector and that is what the [SerializeField] attribute does: it exposes the field in the inspector.

Activate/deactivate them in the SetWindow(Window window) method and we are done with scripting!

using UnityEngine;

public enum Window
{
    None,
    MainMenu,
    InGame,
    Pause
}

public class UIManager : Singleton<UIManager>
{
    [SerializeField]
    private MainMenu mainMenu;
    [SerializeField]
    private InGame inGame;
    [SerializeField]
    private Pause pause;

    public void SetWindow(Window window)
    {
        mainMenu.gameObject.SetActive(window == Window.MainMenu);
        inGame.gameObject.SetActive(window == Window.InGame);
        pause.gameObject.SetActive(window == Window.Pause);
    }
}

In the Unity editor, create the UI elements, add the scripts, assign the variables and you are good to go!

Unity Hierarchy

Now anytime you need a specific window, you can open it up by simply writing:

UIManager.Instance.SetWindow(Window.MainMenu);

And that’s pretty much it. Know a different pattern or a better way? Anything to improve or fix? Some other suggestions? Perhaps a tip or a request for the next blog post? Let me know in the comments and I will make that happen! You can download the code from the Github and check out the rest of the Extra Tools project!

Resource Manager and Object Pooling

You know the Resources folder right? The place where you can put an asset and instantiate it whenever you need them with the Resources.Load<T>() function. Yeah, don’t use it. “But how should I instantiate the prefabs?” I hear you ask. Don’t worry! I will tell you about a very simple pattern I usually use for such purposes. And you will have a simple object pooling system as a bonus.

The first thing we are gonna need is the ResourceManager class. I will make it a Singleton, using this base class (use Singletons with caution though!).

namespace ExtraTools
{
    public class ResourceManager : Singleton<ResourceManager>
    {
    }
}

Next, we need a class to hold the prefab and a list of instantiated objects.

using System;
using System.Collections.Generic;
using UnityEngine;

namespace ExtraTools
{
    public class ResourceManager : Singleton<ResourceManager>
    {
        [Serializable]
        private class ObjectHolder
        {
            [SerializeField]
            private GameObject prefab;
            [SerializeField]
            private List<GameObject> instantiated = new List<GameObject>();
        }
    }
}

We won’t need the ObjectHolder class outside of the ResourceManager, so I made it a private class. The [Serializable] part is important because we want to see what’s in it from the editor. The [SerializeField] attribute forces Unity to serialize private fields, making them viewable in the inspector. We want to set the prefab from the editor but don’t want anyone to actually access it so it is a perfect use case. Serializing the instantiated list is not mandatory but I will keep it for now. So let’s add a method to actually instantiate the prefab. I will call it Get().

using System;
using System.Collections.Generic;
using UnityEngine;

namespace ExtraTools
{
    public class ResourceManager : Singleton<ResourceManager>
    {
        [Serializable]
        private class ObjectHolder
        {
            [Tooltip("The object to instantiate"), SerializeField]
            private GameObject prefab;
            [Tooltip("The pool of instantated objects"), SerializeField]
            private List<GameObject> pool = new List<GameObject>();

            /// <summary>
            /// Returns a game object from the pool. Instantiate a new one if none is available
            /// </summary>
            /// <param name="position">Position of the object</param>
            /// <param name="rotation">Rotation of the object</param>
            public GameObject Get(Vector3 position = default(Vector3), Vector3 rotation = default(Vector3))
            {
                for (int i = 0; i < pool.Count; i++)
                {
                    if(!pool[i].activeInHierarchy)
                    {
                        pool[i].transform.position = position;
                        pool[i].transform.rotation = Quaternion.Euler(rotation);
                        pool[i].SetActive(true);
                        return pool[i];
                    }
                }

                pool.Add(Instantiate(prefab, position, Quaternion.Euler(rotation)));
                return pool[pool.Count - 1];
            }
        }
    }
}

Finally, define an ObjectHolder field to the ReourceManager folder like this:

public ObjectHolder testPrefab;

Set the prefab from the inspector and you are good to go!

Instead of instantiating object like this:

Instantiate(prefab);

Instantiate it like this:

ResourceManager.Instance.testPrefab.Get();

Disable the game object whenever you are done with it and it will be ready to be reused.

This pattern will make managing prefabs much easier. In fact, if you drop the Singleton, you can have multiple resource managers for different scenes or states. I will add a couple more functions but this is the core of this pattern. Here is the final version:

using System;
using System.Collections.Generic;
using UnityEngine;

namespace ExtraTools
{
    public class ResourceManager : Singleton<ResourceManager>
    {
        [Serializable]
        public class ObjectHolder
        {
            [Tooltip("The object to instantiate"), SerializeField]
            private GameObject prefab;
            [Tooltip("The pool of instantated objects"), SerializeField]
            private List<GameObject> pool = new List<GameObject>();

            /// <summary>
            /// Returns a game object from the pool. Instantiate a new one if none is available
            /// </summary>
            /// <param name="position">Position of the object</param>
            /// <param name="rotation">Rotation of the object</param>
            public GameObject Get(Vector3 position = default(Vector3), Vector3 rotation = default(Vector3), Transform parent = null)
            {
                for (int i = 0; i < pool.Count; i++)
                {
                    if (!pool[i].activeInHierarchy)
                    {
                        pool[i].transform.SetParent(parent);
                        pool[i].transform.position = position;
                        pool[i].transform.rotation = Quaternion.Euler(rotation);
                        pool[i].SetActive(true);
                        return pool[i];
                    }
                }

                pool.Add(Instantiate(prefab, position, Quaternion.Euler(rotation)));
                pool[pool.Count - 1].transform.SetParent(parent);
                return pool[pool.Count - 1];
            }

            /// <summary>
            /// Destroyes currently disabled objects
            /// </summary>
            public void DestroyUnused()
            {
                for (int i = 0; i < pool.Count; i++)
                {
                    if(!pool[i].activeInHierarchy)
                    {
                        Destroy(pool[i]);
                        pool.Remove(pool[i]);
                    }
                }
            }

            /// <summary>
            /// Destroyes all objects
            /// </summary>
            public void DestroyAll()
            {
                for (int i = 0; i < pool.Count; i++)
                    Destroy(pool[i]);

                pool.Clear();
            }

            /// <summary>
            /// Instantiates new objects to the pool
            /// </summary>
            /// <param name="count">Number of objects to prepare</param>
            public void Prepare(int count = 0)
            {
                for (int i = 0; i < count; i++)
                {
                    pool.Add(Instantiate(prefab));
                    pool[pool.Count - 1].SetActive(false);
                }
            }
        }

        //A transform to store inactive objects
        private static Transform pool;
        private static Transform Pool
        {
            get
            {
                if (!pool)
                    pool = new GameObject("Pool").transform;

                return pool;
            }
        }

        public ObjectHolder testPrefab;

        /// <summary>
        /// Disables the object and moves it under the Pool objecg
        /// </summary>
        /// <param name="obj">Object to disable</param>
        public static void Remove(GameObject obj)
        {
            obj.SetActive(false);
            obj.transform.SetParent(Pool);
        }
    }
}

You can download this project from Github.

So what do you think? Is it useful? Is there an alternative solution? Any suggestions? Optimizations? Let me know!

Quick Tip for Constants

Here is a really quick and useful tip: use a static class for your constants! An Example:

namespace Constants
{
    public static class Paths
    {
        public static readonly string settings = Application.streamingAssetsPath + "/Settings.json";
        public static readonly string playerSettings = Application.streamingAssetsPath + "/Player.json";
    }

    public static class Tags
    {
        public const string board = "Board";
        public const string metal = "Metal";
        public const string net = "Net";
        public const string stick = "Stick";
    }
    public static class Layers
    {
        public static readonly int player = LayerMask.NameToLayer("Player");
        public static readonly int enemy = LayerMask.NameToLayer("Enemy");
    }
}

This is especially useful when dealing with strings. It will reduce memory allocations and typos. Also if any of these constants is changed, you won’t have to hunt for them in the whole project.

PS: Bonus points for using

gameObject.CompareTag(Constants.Tags.board);

instead of

gameObject.tag == Constants.Tags.board;

Inspector Lock Toggle

You know that tiny “Lock” icon at the right top corner of the Inspector tab, right? Then you probably know that it is pretty much impossible to click on it with an average trackpad on an average laptop? Well, I do! So I was very excited when I saw the “EasyShortcutLockInspector” asset in the Asset Store and decided to add the same functionality to the ExtraTools. Here how it is done:

[MenuItem("Extra Tools/Toggle Inspector Lock %l")]
private static void ToggleInspectorLock()
{
    ActiveEditorTracker.sharedTracker.isLocked = !ActiveEditorTracker.sharedTracker.isLocked;
    ActiveEditorTracker.sharedTracker.ForceRebuild();
}

Just copy-paste this method anywhere in your project and use CTRL+L to toggle the lock of the Inspector window! Also, you can download it from the GitHub.

I will be adding some other stuff to the ExtraTools in the future. Let me know if you have something in mind!

A Better GetComponent() Method

At some point, I found myself writing the following code:

public class TestScript : MonoBehaviour
{
    private BoxCollider col_1;
    private BoxCollider col_2;
    private Text message;

    private void CheckComponents()
    {
        col_1 = GetComponent<BoxCollider>();
        if(!col_1)
        {
            Debug.LogWarning(string.Format("{0} does not have a component of type BoxCollider./nAdding one...", name));
            col_1 = gameObject.AddComponent<BoxCollider>();
        }

        col_2 = GetComponent<BoxCollider>();
        if (!col_2)
        {
            Debug.LogWarning(string.Format("{0} does not have a component of type BoxCollider./nAdding one...", name));
            col_2 = gameObject.AddComponent<BoxCollider>();
        }

        message = GetComponent<Text>();
        if (!message)
        {
            Debug.LogWarning(string.Format("{0} does not have a component of type Text./nAdding one...", name));
            message = gameObject.AddComponent<Text>();
        }
    }
}

See how quickly it can get very annoying and repetitive very fast? So, the obvious solution was to write a more functional and generic GetComponent<>() method. Here is what I did:

public static bool GetComponent<T>(this Transform t, out T component, bool needWarning = false, bool ensureComponent = false, bool addedMessage = false) where T : Component
{
    component = t.GetComponent<T>();
    if (!component)
    {
        if (needWarning)
            Debug.LogWarning(string.Format("{0} does not have a component of type {1}!", t.name, typeof(T)));

        if (ensureComponent)
        {
            component = t.gameObject.AddComponent<T>();
            if (addedMessage)
                Debug.Log(string.Format("Added a component of type {0} to {1}", typeof(T), t.name));
        }

    }

    return component;
}

And my ChceckComponents() method was quickly reduced to this:

public class TestScript : MonoBehaviour
{
    private BoxCollider col_1;
    private BoxCollider col_2;
    private Text message;

    private void CheckComponents()
    {
        transform.GetComponent(out col_1, true, true, true);
        transform.GetComponent(out col_2, false, true, false);
        transform.GetComponent(out message);
    }
}

Check out the GitHub repo and let me know if you have any suggestions or ideas for the next post!

Unity Console Clear Shortcut

Annoyed yet?

Are you compulsively spamming that “Clear” button on the console after every message? Well, then I have great news for you! Copy the following script, paste it anywhere you like and you will be able to compulsively spam CTRL+Q!

[MenuItem("Extra Tools/Clear Console %q")] // CTRL + Q
private static void ClearConsole()
{
    Assembly assembly = Assembly.GetAssembly(typeof(SceneView));
    Type type = assembly.GetType("UnityEditor.LogEntries");
    MethodInfo method = type.GetMethod("Clear");
    method.Invoke(new object(), null);
}

Or you can download it from GitHub.

I will be updating the project with some other useful stuff. Stay tuned and let me know if there is anything you would like me to add.

PS: As far as I can tell, there is no command for this in the new shortcut editor in 2019.1