Introduction
When we make games, we often overlook one of the most important aspects of game development - optimization. As a result, we get lags and low FPS (sometimes even on High-end devices, if everything is really running). Most people will always see game optimization as the last step, and this is the first mistake - it should always be the first item on the list.
It is impossible to overestimate the habit with every build, every day when you add a new mechanic or a new lighting setup, or whatever physics-based systems to your games, always check the build to see if there are any performance issues due to these change is good game development practice.
If you don't take the time to optimize performance right from the start of the project, it can end up in trouble for you and your game in the final stages. Here I want to share some thoughts and tips on how you can improve your game's performance based on a few practical lessons I've learned from my mistakes over the past 5 years.
Profile your game
The Profiler is the first on the list and one of my all-time favorite tools in Unity for monitoring game performance to see what is really causing performance issues. It is very useful for getting a detailed understanding of how your game reacts to various changes in the editor.
You can find the Profiler in Window-> Analysis-> Profiler
It displays categories like CPU and GPU usage, rendering, physics, audio, and more. We cannot rely on profiling in the Editor (Editor Profiling), as the editor affects the performance of the project during testing, and this can affect the validity of the profiling information. To get accurate profiling data, it is better to create a separate build.
Remote profiling
To make it work, you need to install Android SDK and connect JDK and USB debugging. Remember, it's always a good idea to test how your game is performing when it comes to mechanics, UI scaling, and so on. Not to mention testing real-world in-game performance along with the above. To test the actual performance of the game, you need to create a custom profiling build.
To connect the Remote Profiler, go to Edit> Project Settings> Editor and in the Device section select Any Android Device.
Profiling build
To ensure that Unity has access to your build that can be profiled, you must enable “Development build or Deep Profiling Support” and “Auto-connect Profiler” in the build settings before creating it. This allows the Unity editor to automatically link your build.
When your bid is ready, open your game without closing the Unity Profiler window. Unity will now automatically display performance data for the current build of the game in the profiler window.
You can find out more about the profiler here .
Batching GameObjects
Batching is a very good technique to improve performance by reducing the number of Draw calls, which is grouping the rendering of several similar GameObjects in a single draw call. There are two types of batching methods: static and dynamic. There are some limitations for batching - we cannot batch process skinned meshes, fabrics and some render components.
Static batching
Static Batching is used whenever GameObjects are static. Such static GameObjects must not move, scale, or rotate, and must use the same material for all static GameObjects for batching to work.
If your GameObjects do not interact with your Player, or if you are not changing Transform, then it is best to use static batching for most of the environments in your game, such as buildings, roads, etc.
Dynamic batching
Dynamic Batching is similar to static in that GameObjects must use the same materials, but can group moving objects without having to make them static. Basically, Unity can automatically load GameObjects into the same draw call if they use the same material, but they have some of the dynamic batching restrictions imposed on them according to Unity:
- Batching dynamic GameObjects has a certain overhead per vertex, so batching is only applied to meshes with no more than 300 vertices and no more than 900 vertex attributes.
- Shader Vertex Position, Normal UV, 300 , Shader Vertex Position, Normal, UV0, UV1 Tangent, 180 .
- : .
- GameObject- , transform (, GameObject A +1 GameObject B –1 ).
- Material , GameObject- , . Shadow Caster.
- : / . , GameObject- .
- Multi-pass .
- Unity , . « » .
- Legacy Deferred ( ) , GameObject .
Dynamic batching works differently for particle systems, render lines, and trail rendering than for meshes.
- For each compatible renderer type, Unity collects all batch content into 1 large Vertex Buffer.
- The renderer sets the state of the batch material.
- Unity binds the Vertex Buffer to the Graphics Device.
- For each renderer in the batch, Unity updates the offset in the Vertex Buffer and then sends a new draw call.
There are other ways to improve batching using some assets from Assetstore like Simple Mesh Combine, Bakery, or we can also use
Mesh.CombineMeshes
to combine multiple meshes into one, which is ideal for optimizing performance.
Bake your lighting
Roughly speaking, there are three lighting modes: Realtime, Baked and Mixed.
Realtime is the best BUT you have to pay for it with performance. It brings direct light into the scene and updates every frame as lights and game objects move within the scene, updating the lighting immediately.
Whenever you have the option to bake lighting on a stage, be sure to use it because it's ideal for productivity gains, especially if you're targeting mobile devices. It is always best to use low lighting on stage to achieve the desired look.
This way, all of your lights will be pre-computed offline in a process called Lightmap Baking. When you set the GameObject Lightmap Static Flag, Unity bakes information about GameObject Light, shadows, specular light, and soft shadows in textures that touch your scene. However, Lightmaps has some limitations, the lighting cannot update dynamically for the objects you select with the Lightmap Static Flag.
I made a SCIFI scene a few months ago in my spare time and I think this is the best example of how baked lighting works. Of course I used Emission.
If anyone is interested in an overview of how this SCIFI scene was created and how I achieved this look, let me know, maybe I could devote a separate article to this.
, !
This is a very good way to improve the performance of your game with Unity occlusion. In general, Occlusion Culling means that Unity will not display GameObjects that are completely hidden from the camera's perspective (occluded) by other GameObjects.
To open the Occlusion window go to Window-> Rendering-> Occlusion Culling
If anyone is interested in how I achieved this look in the Mystery Forest scene, let me know, maybe I could write a separate article about it.
Here's a video if you're interested!
twitter.com/i/status/1096336962259021824
By default, Unity applies Frustum Culling, which means it only displays the camera's line of sight, showing all objects. For example, if the camera is looking at a wall, all objects behind that wall will also be rendered. We don't need this at all, so we need to add Occlusion Culling to our scene.
This is why we have to use Occlusion Culling, which means that the camera will only render the wall with no objects behind it.
Read more: