If there is one thing to keep in mind when developing a video game, and which, above all, unfortunately, is only taken into account in the final stages of development, it is its optimization. Many titles that neglect this aspect receive a lot of complaints and criticism. One of the key points that can produce spikes in the performance of our game can be the user interface (or UI).
Understanding how Unity UI works
First of all, we are going to explain some small technical details about the operation of the UI rendering in Unity, with these concepts a little clearer it will be more obvious to understand the practical advice that we will present later.
First of all, it must be clear that when the UI unity is rendered it creates a mesh to be able to represent each of the elements that are on the canvas. When there is a change in the canvas it is marked as dirty and the system is informed that it has to be recalculated and redrawn.
Another thing to keep in mind is that the canvas elements are the last to be rendered, so even though our canvas is configured as World Space and there are other elements in the scene that cover it, the pixels corresponding to the canvas will be calculated anyway.
Practical tips to optimize our UI
As we have already seen, each change that is made to a canvas implies that the entire canvas is recalculated, so a good practice is to divide the UI into different canvas, the idea would be to group in the same canvas the elements that are updated at the same time, such as the energy of the attack that goes down every time we hit and the experience points that we gain by eliminating enemies. An interesting point to keep in mind is that we can create a canvas within another canvas. If the child canvas is modified it will not affect the entire parent canvas, since each canvas is isolated, so the elements of the child canvas should simply be recalculated, but in no case those of the parent.
Another point to take into account is the interactive elements of our canvas. The UI elements that we create in Unity have the Raycast Target property. This property makes the elements of the canvas susceptible to being clicked or touched, that is, they can receive an input. This means that every time we click or touch on the Unity screen, it checks if there is an element of the UI that has this property checked and that remains in the projection line of that interaction with the screen. If all the elements of our canvas have the raycast property activated, we will be making Unity perform more calculations than necessary, slowing down the system. That is why it is important to make sure that all the UI elements with which it will not be possible to interact have the Raycast Target option unchecked.
The use of cameras on canvases defined with a Render Mode of Screen Space or World Space is also important. If in these two modes we do not specify which camera we are going to use, Unity by default will search for the main Camera of our scene. But to carry out this search automatically, it will be forced to carry out a search using the Find method, that is, it will have to go through all the objects in our scene until it finds the one that responds to the MainCamera. The use of Find-type methods is totally discouraged at the performance level, so it is highly recommended that if we use any of these types of render mode we always specify the camera manually.
When we want to hide a canvas the best solution is to deactivate the canvas Component of the object. Disabling the component will stop the object from making GPU calls and the canvas will stop rendering. In addition, as we do not hide the object, we simply deactivate a component, certain Garbage Collection tasks that could be called automatically if we were to act on the object, such as the OnDisable functions, will not be executed.
One of the key points of UI optimization is to reduce the use of animations within it, animations cause the canvas to be marked as dirty in each frame and have to be recalculated, if we have to make very sporadic animations it is better to try face a solution that makes the same change through code being this much less costly at the performance level.
We have seen some tips to optimize the use of our UI in our games. We must remember again that although the optimization task can be somewhat heavy since we have to take many things into account, and that if we do not plan it well from the beginning it can lead us to make many changes later, it is a key task when it comes to making a successful title.
Layers & Collision Matrix
Any object created in Unity, if not configured, will have the default Layer assigned (Default). This means that, in the case of associating a collider to the object, it will collide with the entire environment since, by default, this Layer is configured like this. However, in many cases, we won’t need our objects to collide with every other object in our scene. For this, Unity offers us the “Layer Collision Matrix”, which is nothing more than a matrix of layers where we will configure what each of the layers can and cannot collide with.
Leaving all our GameObjects with the default layer, unless our game requires it, is not very efficient since, for each object, Unity has to calculate its collisions with the rest of the existing ones in the scene. Therefore, it is our responsibility to create our own layers, assign them to our objects and make a good configuration of the “Layer Collision Matrix” in order to achieve better optimization.
Avoid calls to Find … () methods
Although these types of methods are very powerful, they are quite expensive since they force Unity to go through all the GameObjects in memory until it finds the desired object. This is a little less worrisome in small projects, where there are a very small number of objects, but as the complexity of the project increases, the less advisable these calls to these functions become.
As a recommendation, use these methods as little as possible and try to access the objects you are looking for in other ways such as caching their references in code, through a reference in the inspector.
Level of Detail (LOD)
Level of Detail (better known as LOD) is another very common rendering optimization technique that is based on the following: objects close to the player are rendered with the maximum detail (using Maya and detailed textures), while objects far away pass to have a much less detailed rendering.
Using the “Profiler”
The Unity editor offers us the “Profiler” tool that is in charge of giving us, in real-time, all kinds of information about the performance of our game. For example, if we have problems regarding memory use, through this window we will be able to know which element (or elements) of our project is causing it.
Thanks to this tool we can take a look at the performance of our game. Details such as the memory usage we are using, how much CPU time is being used for a certain task, when physical calculations are being carried out, etc … we can analyze all of this from this window.
There is no need to talk about the importance of making use of this useful tool throughout the development process of our game, but… use it!