Interface organization in Unity with UI Canvas

Unity has a good system for creating a UI Canvas user interface. A lot of training material has been written on it, but most guides only tell you which buttons to press and which code to write to make everything work. As examples, a small interface usually consists of a pair of windows: the main menu, settings. However, in games there are much more windows and when there are already at least a dozen of them, the need for some kind of organization arises. As part of this article, I want to talk about how I solve this problem.



First you need to select individual windows in your interface. By window I mean a panel with some controls.



Window Examples












Each window should have a root object that will contain all controls. This object will represent the window as a whole. For example, there is a panel on which the controls are located, it is logical to make these elements child with respect to the panel. A component is attached to each window, which is the descendant of the abstract Window class.



Window



Window is written on the assumption that windows can have child windows, i.e. those windows that can be opened from the current window at the same time at the same time one child window can be opened, and all the rest should be closed. For this purpose, the class contains the CurrentWindow property in which the link to the window that is currently open is stored. And also there is an OnOpen event that reports the opening of a window. The ChangeCurrentWindow () method can be subscribed to this event in child windows, so that at any one time one child window is opened, it closes the open child window and changes the link to the current open window, below I will give an example implementation. Also in the class there are SelfClose () and SelfOpen () methods, these methods are responsible for how the window will open and close. In the Awake () method, the window is registered in the UIManager, i.e. a link to the window is added.



public abstract class Window : MonoBehaviour { public bool IsOpen { get; private set; } public Window CurrentWindow { get; protected set; } = null; public delegate void OpenEventHandler(Window sender); public event OpenEventHandler OnOpen; void Awake() { UIManager.Instance.Windows.Add(this.gameObject); } public void Open() { IsOpen = true; if (OnOpen != null) OnOpen(this); SelfOpen(); } protected abstract void SelfOpen(); public void Close() { IsOpen = false; if (CurrentWindow != null) CurrentWindow.Close(); SelfClose(); } protected abstract void SelfClose(); protected void ChangeCurrentWindow(Window sender) { if (CurrentWindow != null) CurrentWindow.Close(); CurrentWindow = sender; } }
      
      





UIManager



Next, we move on to the UIManager class, it is a sigleton class so that all windows can access it if necessary. As you can see, it has a list of Windows windows; links to all the windows on the stage are stored in it. InitUI () is needed to initialize something, for example, if you want to create windows from prefabs, then here you can do their instances.

In the Start () method, you can open windows that should be open from the very beginning or vice versa close unnecessary ones. The Get () method allows you to get a link to a specific window.



 using System.Collections; using System.Collections.Generic; using UnityEngine; using System; public class UIManager : MonoBehaviour { public static UIManager Instance = null; public List<GameObject> Windows; void Awake() { if (Instance == null) Instance = this; else if (Instance != this) Destroy(gameObject); InitUI(); } private void InitUI() { //to do } void Start() { foreach(var window in Windows) { var windowComponent = window.GetComponent<Window>(); if (windowComponent is StartWindow) windowComponent.Open(); else windowComponent.Close(); } } public Window Get<T> () where T : Window { foreach(var window in Windows) { var windowComponent = window.GetComponent<Window>(); if (windowComponent is T) return windowComponent; } return null; } }
      
      





SettingsWindow



As an example, I will give my implementation of the settings window:



 using System.Collections; using System.Collections.Generic; using UnityEngine; public class SettingsWindow : Window { private VideoSettingsWindow videoSettingsWindow; private LanguageSettingsWindow languageSettingsWindow; private AudioSettingsWindow audioSettingsWindow; private ControlSettingsWindow controlSettingsWindow; public void Start() { videoSettingsWindow = UIManager.Instance.GetWindow<VideoSettingsWindow>(); languageSettingsWindow = UIManager.Instance.GetWindow<LanguageSettingsWindow>(); audioSettingsWindow = UIManager.Instance.GetWindow<AudioSettingsWindow>(); controlSettingsWindow = UIManager.Instance.GetWindow<ControlSettingsWindow>(); videoSettingsWindow.OnOpen += ChangeCurrentWindow; languageSettingsWindow.OnOpen += ChangeCurrentWindow; audioSettingsWindow.OnOpen += ChangeCurrentWindow; controlSettingsWindow.OnOpen += ChangeCurrentWindow; } protected override void SelfOpen() { this.gameObject.SetActive(true); } protected override void SelfClose() { this.gameObject.SetActive(false); } public void Apply() { } public void VideoSettings() { videoSettingsWindow.Open(); } public void LanguageSettings() { languageSettingsWindow.Open(); } public void AudioSettings() { audioSettingsWindow.Open(); } public void ControlSettings() { controlSettingsWindow.Open(); } }
      
      





From the settings window I can open 4 other windows so that only one window is open, I subscribe the ChangeCurrentWindow () method of the settings window to the OnOpen events of the child windows, so open windows are closed when others open. The SelfOpen () and SelfClose () implementations simply activate or deactivate the window.



Thus, an easily extensible UI system is obtained; there is no need to manually add links to windows in the inspector or elsewhere; to add a new window, you just need to create the appropriate class and inherit it from Window. Such a system has a couple of minuses, that the more windows, the more classes it will be necessary to create and the fact that the search for a link to the window is done by sorting through the array of links, but in my opinion these shortcomings pay off with advantages.



Repository Link



All Articles