Displaying the map of Million Lords smoothly on mobile is a constant challenge. The game contains a very high number of hexagonal tiles, all of them with different color values, different sprites, etc…
On top of that, the world map is in constant extension, so it needs to be able to load the server data and to back them up while displaying them.
The initial solution
In the first versions of the game, the map was static and was loaded during the launch of the game. The map was saved locally and only the city data were refreshed. Each one of the 17.000 tiles contained a background sprite (desert, grass or snow) and either :
- A city sprite with a colored sprite outlining the tile
- Sprites with trees, mountains etc… on the tile.
In dezoomed mode, there were several thousand sprites on screen at the same time. The number of frames per second was low on mobile for several reasons:
- With Unity, sprites usually create a lot of triangles,
- The images aren’t regrouped and the device spends a lot of time to find the right images to display,
- The algorithm tasked with knowing what needs to be on screen or not in order to load faster was taking a lot of time.
Reduce the number of triangles
We can reduce the number of triangles per sprite to 1, which is at first glance the best idea performance-wise. But that’s only valid if the sprite has a triangular shape in the first place. The other cases also need a lot of transparency zones and it can get pretty costly in terms of calculation. You need to find the best ratio between the number of triangles and the empty surface.
For the tiles backgrounds, it’s quite simple. You only need 4 triangles and there is no transparency. However, for the decoration sprites, we create the shapes by hand in unity with the sprite editor, like with the gif below.
Reduce the drawCalls
Each time the GPU needs to change the texture, it spends time to search for it in its memory, then it loads it and displays it. This operation is very costly compared to just displaying a lot of triangles with the same texture. Everytime a GPU does this action, it’s called a drawCall. The lowest the number of drawCalls, the better the performances.
The solution is to regroup the textures often used together into a single, bigger texture. This way, the GPU won’t charge a texture for each object.
For sprites, Unity offers a partly automatized system.
The Frustrum Culling is the algorithm calculating what’s displayed on screen. It goes over all the objects in the scene and adds them to the list of objects to display. If you reduce the number of objects, it will consequently become much faster.
With the arrival of the infinite procedural map, we charge the tiles data per package. With each data package, we create an object that contains a group of 3D tiles, on which we add the props. The frustrum culling is mostly used on a small number of objects.
Thanks to these modifications, we managed to get a smooth display of the entire map on many devices, even mid-range. It’s important to think about the optimization during the entirety of the development, to avoid any big changes right at the end.
Article by Ralph Nicolas
Wait, you haven’t played the game yet? Sign up for the beta here!
Check out our other devlogs
- The finite-state machine with Unity
- API Load Testing with Gatling
- Unity Cloud Build
- Game assets creation process at Million Lords