Performance Gains in Unity. How I went from 15 to 60 FPS
A facsinating journey to better performance on mobile phones.

Search for a command to run...
A facsinating journey to better performance on mobile phones.

No comments yet. Be the first to comment.
Ось уже понад рік, як я працюю над своєю грою Дике Поле: Слобода 2. Почав її, бо більше не можна так жити, кругом якась...Мобільних ігор з дійсно цікавим гейм-плеєм і не туалетним терміном гри (приблизно 2 хвилини) дуже мало. Крім Kingdom of Two Cro...
Build. Craft. Explore.

Заселяємо південь України!

Hey! We just published the second content update for our game Sloboda 2. This update is free for anyone who already purchased our game. If you want to purchase it, then pick your platforms: Apple App Store: https://apps.apple.com/us/app/loca-deserta-...

Привіт, компана! Ось і настав час Січневого оновлення для нашої гри Слобода 2! Це оновлення безкоштовне для всіх, хто придбав гру. Якщо ж ще не придбали, то можете це зробити по цих посиланнях: Apple App Store: https://apps.apple.com/ua/app/loca-dese...

Статистика запуску гри Трохи більше двох тижнів тому ми випустили гру на всіх магазинах. І одразу ж попали в Топ платних ігор 🇺🇦українського App Store! Купити можна на iOS по посиланню: https://apps.apple.com/ua/app/loca-deserta-sloboda-2/id164250...

I am doing a remake of my old game Loca Deserta: Sloboda The first version was done with Flutter but this time I picked Unity as the game engine.
I started from scratch and implemented lots of new functionality but I have noticed that even my Galaxy S21 Ultra had lagging issues. The FPS was smooth but sometimes I had a feeling it dropped from 60 to 30 FPS.
I took a very old Nokia 6.1 with Android, launched my game and was shocked. It was a complete garbage. FPS was in a 0-15 range. Completely unusable.
Of course I launched a Profiler and checked what was choking in my Nokia 6.1/Galaxy Note 8
You can see that the game ran at max 30 FPS, even fewer.

Also when I have to show building/production requirements with 3D resource models:

it spiked to 40-60ms per frame and the bottleneck was my OnTriggerEnter logic (more on this later). Also pay attention to that blue scripting timings that appear when the new resources are shown on the screen. Definitely something was wrong with my code.

At first I thought it was all about shadows. I switched them off...and nothing changed. The game choked itself.
I tried to run it on low/very low. Better but still at 15-30 FPS for such a simple game. No!
I decided to use a scientific method of divide, conqueror and guess what's wrong. Something in the scene definitely chocked the GPU (as 80% of my CPU frame time was spent on waiting on GPU results).
I started with the biggest asset: I removed Terrain layer.
BOOM ! Steady 60 FPS on Galaxy Note 8! Haha! It was it:

I returned back - again 15-30...It was definitely Terrain that caused me the performance issues.
So I read through the TerrainLayer official docs and noticed this:
you can use four Terrain Layers per Texture pass, with no limit on the number of passes.
This means that although you are allowed to use as many Terrain Layers as you want,
each pass increases the time spent rendering the Terrain.
For maximum performance, limit each of your Terrain tiles to four Terrain Layers.
And I had like 8 layers all mixed on the floor with different alpha and masks!
And for real, once I have only 4 layers like on this screen:

Then the game runs very smooth even on ancient Android like Nokia 6.1:

Once I add even one more layer (without using it for painting!) then the performance drops.
Don't set more than 4 terrain layers even if you paint only 4 still the Unity will choke processing your Terrain.
This was easy to fix, it turned out I used DateTime.now to get the current time in order to process task timers in game. It seems this can be a little painfull on mobile phones so I rewrote the logic and used Time.deltaTime in order to check how much time has passed since the Upgrade/Production task started:
void Update()
{
if (actionStarted)
{
elapsedActionTime += Time.deltaTime;
if (elapsedActionTime > actionDuration)
{
actionStarted = false;
elapsedActionTime = 0.0f;
finalizeAction();
}
}
}
Check this awesome video on how to implement timers in Unity:
4 Easy ways to create Timers in Unity - in 2 minutes - Jason Weimann
After fix, notice that the blue scripting timings are much shorter!

When I have to show materials for build/produce requirements I instantiated prefabs and rendered them on screen. Then when the player is not near the building I destroyed them.
It turns out...Instantiate and Destroy are VERY resource consuming procedures.
See these spikes? These are Instantiate/Destroy calls. Some of them take 40-70 ms:

I knew how to fix this with Object Pooling: Object Pooling (in depth) - Game Programming Patterns in Unity & C# - Jason Weimann
I reimplemented the logic to create all the objects beforehand when the scene loads. And then just pull them from Object Pool and place where needed.
Check the same game scenario to show new Resource objects on screen but with Object Pooling:

My game now runs at 60FPS on Samsung S21 Ultra and 30-40 FPS on Galaxy Note 8 and Nokia 6.1.
I can finally continue on adding more buildings to the game :)
This is my game running on Nokia 6.1: https://twitter.com/DmytroGladkyi/status/1481540410098991104
The first version of my game has features from a 'real' full-sized game you used to have in a city building genre:
Manufacturing lines
Create compose materials out of simpler materials
Research influences production
Building and Upgrading buildings (each type of building has 3 level with all unique voxel art!)
Local Map with random events
Global huge map
Conquering new areas
Weather events
You can learn more and download my old Sloboda game here: Loca Deserta: Sloboda

You can join my Telegram Channel: https://t.me/locadesertachumaki. Or https://t.me/locadesertachumaki/467.
Or follow me on Twitter: https://twitter.com/DmytroGladkyi