If your game contains at least some user interfaces, you will quickly run into the need of manage them in a central place. In our past games we developed a generic window manager that can be used in any project. It takes over the all the loading/unloading of windows and makes sure windows are not opened multiple times.
Basic considerations
The window manager can be used out of the box, but it makes some demands on how you setup your UIs.
One window, one scene
First of all the manager expects that each window is stored in a separate scene. This shouldn’t be a problematic demand as separating the windows in some way is a good idea anyway. The name of the scene is used as the window id by which you open/close a window.
Other than that you are free to setup your window scenes the way you like. Most often you will have a single canvas in it, so there is one root game object. Underneath you design the UI of your window with Unity UI or any other UI system as you are used to.

Window Manager Singleton
You can easily access the WindowManager by using its static property WindowManager.Instance . In general I don’t like singletons that much and in fact it doesn’t create a window manager itself. Instead it just stores the first initialized window manager via its Awake() method.
So you have to make sure to put a WindowManager script on one of your game objects before using the WindowManager . Additionally make sure that the game object holding the script isn’t destroyed, otherwise the window manager is lost as well.
Implementation
Now let’s dive right into the implementation. You might want to check out the Bitbucket repository and pull the sample project to follow my descriptions easier.

Window Manager
As you might have guessed most of the work is done by the WindowManager class. It has a slim interface to open and close windows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
public interface IWindowManager { #region Events /// <summary> /// Callback when a window was opened. /// </summary> event Action<Window> WindowOpened; /// <summary> /// Callback when a window was closed. /// </summary> event Action<Window> WindowClosed; #endregion #region Public Methods and Operators /// <summary> /// Closes the window with the specified window id, sending the specified return value to listeners. /// </summary> /// <param name="windowId">Id of window to close.</param> /// <param name="returnValue">Return value of the window.</param> /// <returns>True if the window was closed successfully; false, if it isn't open.</returns> bool CloseWindow(string windowId, object returnValue = null); /// <summary> /// Closes the specified window, sending the specified return value to listeners. /// </summary> /// <param name="window">Window to close.</param> /// <param name="returnValue">Return value of the window.</param> /// <returns>True if the window was closed successfully; false, if it isn't open.</returns> bool CloseWindow(Window window, object returnValue = null); /// <summary> /// Opens the window with the specified id. The id is equal to the scene name the window is stored. /// </summary> /// <param name="windowId">Id of window to open</param> /// <param name="context">Optional window data.</param> /// <param name="onClosedCallback">Callback when window was closed.</param> /// <param name="onOpenedCallback">Callback when window was opened.</param> /// <returns>Returns the object that holds the information about the window that should be opened.</returns> Window OpenWindow( string windowId, object context = null, Action<Window, object> onClosedCallback = null, Action<Window> onOpenedCallback = null); #endregion } |
Internally it contains a list of Window objects, each storing information about an open window:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
public class Window { #region Properties /// <summary> /// Data the window needs, e.g. for initialization. /// </summary> public object Context { get; set; } /// <summary> /// Indicates if the window is loaded loaded. /// </summary> public bool Loaded { get; set; } /// <summary> /// Callback when window was closed. /// Parameter 1: Closed window. /// Parameter 2: Return value of window. /// </summary> public Action<Window, object> OnClosed { get; set; } /// <summary> /// Callback when window was opened. /// Parameter 1: Opened window. /// </summary> public Action<Window> OnOpened { get; set; } /// <summary> /// Root transforms of loaded window scene. /// </summary> public Transform[] Roots { get; set; } /// <summary> /// Scene the window was loaded from. /// </summary> public Scene Scene { get; set; } /// <summary> /// Unique id of window. /// </summary> public string WindowId { get; set; } #endregion #region Public Methods and Operators public void Hide() { foreach (var root in this.Roots) { root.gameObject.SetActive(false); } } public void Show() { foreach (var root in this.Roots) { root.gameObject.SetActive(true); } } #endregion } |
Open a window
Let’s have a look now what happens when a new window is opened. The method gets up to 4 parameters, but only the first is mandatory:
- Window id
- Window data/context
- Callback when window was closed
- Callback when window was opened
The window id has to be unique and has two functions: First of all it acts as an identifier to close the window via its id later on and to avoid opening the same window twice. It is also used to load the window as the window id and the name of the scene the window is saved in have to be the same.
What the OpenWindow method does is:
- Creating a new Window object and adding it to the list of open windows
- Starting an asynchronous load of the scene with the name of the window id
- Checks if the scene was successfully loaded
- Collecting all root Transform s that belong to the loaded scene
- Invoking the event and callback
You can check out the steps one by one in the WindowManager.DoOpenWindow method.
The usage is a very simple one-liner (see the OpenWindow script in the sample project):
1 |
WindowManager.Instance.OpenWindow(this.WindowId); |
Opening a window before Unity 5.3
Before Unity 5.3 the logic and creation of a window was a bit more difficult as there was no chance to get the Transform s that belong to a specific scene. Version 5.3 introduced a new SceneManager though which makes multiscene editing possible and additive scene loading. Plus each game object has a property to indicate which scene it belongs to ( GameObject.Scene ). Check out the details at http://docs.unity3d.com/Manual/UpgradeGuide53.html.
In Unity 5.2 and lower it was necessary to put a WindowRoot script on the root game object of your window scenes. This had an identifier which had to be the same as the window id, so the window manager could find the root Transforms of a loaded window/scene.
Closing a window
To close a window, just call the CloseWindow method and pass the window id or the window object you got by the OpenWindow method. Additionally a return value could be passed which might be interesting for the listeners of the WindowClosed event.
After checking if the window is open, the method performs the following steps:
- Unloading the window scene
- Removing the window from the list of open windows
- Invoking the event and callback
As you can see the steps to close just invert the ones to open the window. Thanks to the SceneManager of Unity 5.3 it’s only some lines of code.
As opening a window, closing it is simple as well (see the CloseWindow script in the sample project):
1 |
WindowManager.Instance.CloseWindow(this.WindowId); |
Showing/Hiding a window
After you opened a window you can use it as you want. A common use case is to hide/show it. This just deactivates/activates the root game objects of a window and can be performed via the Hide() / Show() methods of the Window class.
Conclusion
The current version contains just the basic functionality to manage UI windows in a project. With it you can easily and cleanly open and close windows and manage them in separate scenes.
In a real project though there are a lot more features that have to be added:
- Animating the window when it opens/closes
- Set up the content of a window, using the specified window data/context
- Testing a window independent of the game it is created for
- Sharing one canvas for all windows, so you don’t have to do the Canvas settings in each window scene
- A generic way to open a modal window which can’t be clicked through
Those are just a few requirements for a window manager used in practice. I will cover some of them in future posts, so check back soon. If you have any questions, don’t hesitate to ask! And if you want to have a specific thing about window management covered in the next post about it, let me know 🙂