Mobile games optimization on Unity is going on: overviewing the Unity Profiler and areas you shouldn’t get into
I constantly review hundreds of new projects and see new performance gaps even in hypercasual games, almost all of them. In the last article, I described four simple steps on how to optimize any simple Unity game for weak devices at minimum resources.
Today we’ll look at a few more nuances that can make your project’s development and review easier to bring the release date closer and avoid disappointing players from poor performance.
We’ll also talk about what you shouldn’t pay attention to in small projects so as not to waste extra time for a little better performance.
Let’s start with the main tool that can make life much easier when looking for weak points.
1. Unity Profiler
I always use Unity’s built-in profiler at the beginning. Everything here is intuitive, even if you use this tool for the first time, so you shouldn’t ignore it. The profiler clearly shows the code parts that slow down.
Problems usually arise when the code is written by someone else — you’ll have to puzzle out what the code does and how you can fix it.
If the Unity profiler doesn’t give clear answers, we move on to native profilers — Android Studio or Xcode, depending on the platform.
In more complex situations, we use the specialized profilers: Snapdragon Profiler, Arm Mobile Studio and so on, depending on the devices at hand. The functionality of such profilers is more or less the same, they’re just intended for different hardware.
2. Banner ads
Not so long ago, there was a case when I used the profiler incorrectly. At the team’s request, the game analysis was carried out. The condition was: consistently low FPS. Since it all was about FPS, I took measurements not from the game start, but after a while, so I carefully studied the project when it hit the plateau. I noticed several problems and spent about two days making optimization recommendations. I wrote plenty of tips, but productivity increased only by 10%, which is really small.
Further analysis showed that the problem existed for 15–20 seconds after the game started due to loading the banner ads. Once the banner ad loaded, the game lags stopped.
In fact, banner ads represent a browser running inside the game. The browser is quite a heavy application that is used just to show a small picture or animation. While it’s loading, the game can lag. This approach is used by many advertising networks. So, if someone wanted to get rid of this problem, they should have to write their own plugin and create the company selling ads.
There are only two ways out of this situation:
- Disable banner ads on weak devices and lose some monetization.
- Put up with some lags for the first 30 seconds (in an ideal scenario, camouflage them somehow) while the banner ad is loading.
Basically, all advertising plugins cause lags, and the general tip to deal with them is loading ad placements in turn, slightly “smearing” the lag in time.
3. Cutting off what’s behind the scenes
Sometimes it happens when the camera looks down at two game characters, and around there are a dozen more invisible extra characters taking away many resources. It’s usually not critical, because small projects have few objects: all of them get in frame, and there are no large levels.
If the level is large enough and there are many objects, don’t forget to disable everything that isn’t visible. For example, the Unity’s AnimatorCullingMode has three options:
- AlwaysAnimate — it’s clear from the name what this option does.
- CullCompletely — animates objects only when players see them.
- AnimatePhysics — animates only physics if players don’t see the objects.
By default, Unity offers the Always Animate option, so this checkmark should be disabled in most cases.
4. 3D models & textures
For small projects, this point isn’t so relevant. Situations can be two types:
- Colossal mistakes. For example, the developer made a particle system from spheres, which had 1,500 polygons in each. Please, don’t do this.
- Everything is within normal limits, and optimization isn’t worth the time.
5. Project organization
Clutter in files’ organization, duplicating assets and textures, unreadable names — all these things slow down the development process. Although, it’s the last thing you should worry about in casual projects from third-party studios.
Everything is also complicated by the fact that each case is individual — different teams have their strengths and weaknesses. If you get used to the project’s specific order and hierarchy, then you should spend much time and effort to remake yourself, and it doesn’t mean the situation will get any better.
I don’t give advice here and don’t meddle with other people’s processes unless absolutely necessary. It’s great when the project is well organized from the very beginning, but still — in this article, we talk about technical errors.
Example of midcore project optimization
Now let’s look at a specific case with examples of tips on optimizing one project.
It was a midcore multiplayer game with ships intended for 10 players. But the game’s problem was that FPS dropped to 5 on average performance devices. And there were several reasons for this.
Skinning 3D models took huge resources on the project: there were many sails and sailors on the deck. Unity is able to do GPU skinning to make the process faster, but even this doesn’t always help, especially on weak devices where GPUs are quite poor.
In this case, skinning took 60 ms, and this led to a limit of 12 FPS, excluding frame rendering and other things.
What to do?
- To start with, you should set CullingMode to CullCompletely (see para. 3 above) on all animators that don’t affect the simulation but only the visuals. It also would be great to disable them at a certain distance from the camera, even if it’s visible.
- Reduce the number of skinned meshes.
- Reduce the number of bones per vertex. Sometimes it’s better to make 1–2 bones in each triangle if it doesn’t critically affect the visual beauty.
- Reduce the number of vertices on models.
- You can redo animations and so on. For example, reduce the bones’ number.
Clearing the framebuffer
When you render the first camera (or start rendering via SRP), you need to clear the entire framebuffer, not just stencil and Z-buffers. If you forget something, the frame marker at the start breaks, which is crucial for mobile GPUs, and some things done at the hardware level may not function as expected.
Good and trendy texture formats may not be supported on specific devices. In such cases, Unity shows that textures should weigh 0.7 MB, and on the device in fact — 2.7 MB.
The point is — if some original texture format isn’t supported on some devices, Unity unpacks it into another format and uses it uncompressed. Thus, textures should weigh little (as Unity shows), and on the device itself, they weigh several times more. This is because the built-in profiler only shows the estimated texture size if the mobile device supports this format.
To find out the texture’s real size, you need the native profilers I mentioned above.
A large number of allocations — memory assignment.
It’s usually calculated how many allocations are made per frame. If there are more allocations then, let’s say 5–10 KB per frame, then you should think about finding and eliminating these places.
We achieved 20–30 FPS on weak devices instead of 5.