
Game optimization on Unity and dev story Tap Tap Builder
Each indie developer’s piggy bank should have its own city builder, maybe that’s why I decided to “construct” my bike. Of course, with square wheels and a rocking chair instead of a grandmother. I work alone, so there are no designers, artists, and even more modelers, in the project. In addition, in general, this is my first game with three-dimensional graphics. In order not to bother learning advanced tools for creating three-dimensional models, I decided to do everything with my own hands and the means of the Unity gaming environment. There are only primitives, like cubes and cylinders, as well as the ability to color them. Well, you should be patient and begin to "create", immersed in the role of an architect. My experience with a publisher, as well as ways to optimize the game, can be useful information for beginning indie developers.

The main feature of Tap Tap Builder is a symbiosis with a clicker, and this genre is firmly established among the general public. So, for the construction of buildings from the player will need, in addition to a certain amount of resources, pretty to put your fingers together, clicking on the screen. True, you can build a construction crane, which will build for you, but very slowly. Most buildings can be improved, with every 5 levels changing their model. Each building in the game plays a specific role.

In residential buildings people live. Without them, the population of the city will not grow. When the townspeople acquire housing, they begin to look for work. For example, I can go to work in the office. Offices are the main source of tax credits to the city budget. Someone may go to work for the plant. Factories produce resources - metal and concrete. All buildings require electricity. To do this, you will have to build a power plant. In addition, in some buildings you can work yourself, while the player’s energy will be consumed, which will be restored over time. For example, a player lacks metal. Instead of waiting a couple of hours while it will be produced at the factory, the player can go into the factory building and work there himself. And of course, the essence of the work is to click on the button labeled "Work" 100 times)
If residents cannot find work, they will have to pay unemployment benefits. However, if there are not enough workers, then the buildings will work inefficiently. Therefore, it is important to constantly monitor the balance of jobs and the number of inhabitants.
There are quite a few buildings in the game. There are hotels and restaurants, police stations and fire stations, banks, fitness centers, exchanges, etc. The player gains experience for the construction and improvement of buildings and can improve the level of the city. At the same time, he receives 1 golden key, which will be needed during the construction of special buildings. Something like skill points in an RPG. For example, you can build a bank, then when you exit the game, all accumulated funds will be transferred to the deposit, and the player will receive interest for the entire time of absence from the game. Or you can build a tax office, which increases taxes to the city budget by 10%. In addition to the main and special buildings, there are also service, unique and decorative.
There are vehicles in the city. They are made only for beauty. If you build the city administration, limousines will start to ride around the city. You can also build a show room, then sports cars will start to drive around the city.

Among the outbuildings there is a beer pub and post office. Having built them, players will be able to interact with each other, for example, to exchange invites and resources. At the same time, network access is not needed. Players will exchange bitcodes, this is something like promotional codes or coupons. Each city has a unique index, which is displayed in the mail building. It must be indicated as the recipient when sending the parcel. In addition, of course, you will need to choose what to send. For example, you can send 1000 units of metal, and the received code will need to be reported to a friend. However, this code cannot be sent to several players, since the recipient’s index is encrypted in it.

Similarly, promotional codes are generated that are inserted into messages on the wall in social networks.

How successful this decision is, time will tell. I hope that the players will appreciate the original idea.
The project managed to take part in Games Jam Kanobu 2016 and end up in a selection of interesting projects. Alas, I didn’t reach the final. But it was possible to communicate with other developers and get useful feedback. Unfortunately, there are practically no real players at such events. But most importantly, the game is our publisher. He became the Russian company Herocraft. And this is awesome! In addition, there were 2 more proposals, the discussion of which ended without a result.

So, I’ll briefly share my tips on hackathons and jam:
- look for a team. The probability of finding a team at the hackathons is small. Usually already formed teams participate, and free artists rarely look at hackathons. Try but don't count;
- look at other projects and their results. Determine for yourself the most popular areas in development and “borrow” good ideas);
- Look for reviews about the game;
- look for the publisher;
First of all, an agreement is drawn up, which stipulates platforms for the release of the game, legal and financial issues. Before signing the contract, you should prepare a list of questions and get answers to them. For example:
- what improvements will be required?
- How long can the refinement process take?
- what are the conditions for termination of the contract?
After that, the process of finalizing the game begins. The publisher makes a list of tasks to be completed. I advise you to immediately decide on a convenient project management system. In my case, it was GitHub. There you can store the source of the game, builds and other files, as well as start tasks and bugs.

Regarding Tap Tap Builder, the main tasks were:
- completion of the training system
- improvement of the interface
- integration of analytics tools
- increase in game performance
- creation of content for game stores (icon, banner, description, screenshots, video)
- development of tests
I note that many subtasks had to be postponed until release due to the complexity of their implementation. At the test development stage, testing is iterated on the developer's side, because all submitted tests must be at least correct.
After the game is finalized, iteration of testing on the publisher's side and correction of detected bugs begins.
And brought to the standard and lay down build is sent to the soft launch (soft launch). Softlanch is a limited publication of the game, for example, in any one country and on one platform. The task of the softlanch is to get feedback from real players and determine the main metrics of the game (income, retention, etc.). Based on these data, a decision is made on the appropriateness of the world release. In case of a failure of the softlanch, the contract is terminated, the game is returned to the developer, and he can publish it on his own.
The first attempts to run the game on mobile devices revealed significant performance issues. The game showed 10-15 FPS on an empty island, while the devices were far from the weakest (Sony Z1 and Asus Transformer). Subsequent therapy allowed to increase the speed of the game several times. So, in order.
Do not feverishly start looking for problems in the game and optimize the most likely bottlenecks. The first thing you need to do is run Profiler (available in the free version of Unity 5).
I immediately discovered problems in the scripts. The correction did not take much time; all optimization was reduced to caching information and reducing the number of calls. By the way, I recommend the Memoizer design pattern. Its essence is to remember the result of the function for subsequent calls with identical parameters. Parameters can be not only simple types, but also structures and classes. Below is an example of using this template.
After the problem with the scripts has been resolved, it's time to do the rendering. The fact that building models were made of primitives that had different materials (and colors) had a significant impact on performance. Statistics showed about 600 draw call (after autobatching) and 100,000 triangles. For comparison - at the moment, for a spherical game in a vacuum, it is recommended to have up to 100 draw call and up to 30,000 triangles.

The first solution is to reduce the number of surfaces. I had to import an asset for editing meshes. Many buildings contained cylinders, and a standard cylinder has 20 faces and 80 triangles. Simplification of the geometry of the cylinders to 12 faces gave a tangible effect to the detriment of the small angularity of buildings.

The next step is to remove the invisible faces from the primitives. As a result, a significant reduction in the total number of triangles (about 20%) brought no increase in FPS. The engine probably doesn’t spend resources on rendering invisible geometry, even if it is within the camera’s visibility. The result is a futile step.
The next step is an attempt to use static batching. I’ll briefly talk about batching for those who don’t know. Batching is a grouping of meshes (mesh) into 1 common mesh before calling a draw call. It just so happens that it’s easier for a video card to draw 1 object at once than in parts for several calls. There are several nuances. Merging is only possible for meshes that use the same material. Secondly, the total number of vertices in general should not exceed 900. And thirdly, changing the transform (position, rotation and scale) of child objects is impossible. For example, the wheels of a “skatched” car model will not spin. Batching is static and dynamic. Dynamic batching runs at run time and requires no action by the developer.

Static batching can be implemented in two ways - check the Static checkbox for an object on the scene or prefab. These can be static game objects, such as landscape elements, vegetation, and buildings. The second way is to use StaticBatchingUtility at runtime.
It was logical to do static batching for buildings, which I did. As a result, the game began to work even slower! As it turned out, forced static batching disrupts the operation of dynamic batching, which works more efficiently and groups meshes from different logical objects, for example, belonging to different buildings. In addition, automatically grouped objects can move relative to each other (the grouping is canceled). Bottom line - the use of static batching can be useful only with a deep understanding of the logic of its work.
The next observation - disabling dynamic shadows reduces the number of triangles by exactly 2 times, which gave an increase in FPS of about 20%. Changing the quality of the shadows did not produce a visible effect. In this situation, there is a simple standard solution - replacing shadows with sprites. Such shadows are often called static. The question immediately arises - how to get the shadows of objects, because not to draw them by hand ?! The easiest solution is to use blurry circles. This would be suitable for character shadows in a simple runner or shooter. This solution did not suit me - the shadows should repeat the geometry of the buildings. Well, I had to write a script to dump the shadows. The algorithm is simple - the script takes turns creating buildings (from prefab) and takes a screenshot of the building with a shadow, saving it to disk. Then the second script performs elementary processing of the received images, by filling the shadow with black and removing the background (in color). Alas, I did not find any ready-made solutions, share your experience in the comments.

The result is a 20% increase in speed when minor artifacts appear (when applying and layering shadows). By the way, sprites with shadows need to install a Packing Tag, for example, Shadow. Then Unity will create an atlas for them, and ALL shadows will be drawn in 1 draw call. And one more note - on some devices there may be problems with displaying dynamic shadows, so replacing them with static shadows will avoid problems.
Unfortunately, the result of the optimization was unsatisfactory - the game showed 20-25 FPS on a powerful device. I decided to write another script for baking models. The bottom line is to generate a new mesh and apply a texture to it to paint the buildings. The texture is a simple 8x8 color palette that can automatically fill up when new materials (colors) appear. In this case, it is necessary to form the correct UV-scan (UV-mapping). There are plugins on the Asset Store for baking objects, but they did not know how to generate a texture based on the colors of materials. I will not dwell on the details of the implementation, because I understand that my situation is quite specific. Although, on the other hand, this method can find application in the now popular voxel graphics. After all, making prototypes of models in Unity is much more convenient than studying modeling. Unless, of course, in your team, there is no modeler like mine. I didn’t have anyone at all).

The final result exceeded all expectations - the speed of the game increased significantly. The game shows 50-60 FPS in a huge city. Further optimization is not required.
I apologize for the fact that the article was very large. After all, I wanted to talk about the game and share experience. And finally, my advice is to make cool prototypes and do not waste your energy on optimizing the game until the working version appears.
I will be glad to hear your optimization tips in the comments, and also ready to answer questions!
Link to a game on Google Play

About the game
The main feature of Tap Tap Builder is a symbiosis with a clicker, and this genre is firmly established among the general public. So, for the construction of buildings from the player will need, in addition to a certain amount of resources, pretty to put your fingers together, clicking on the screen. True, you can build a construction crane, which will build for you, but very slowly. Most buildings can be improved, with every 5 levels changing their model. Each building in the game plays a specific role.

In residential buildings people live. Without them, the population of the city will not grow. When the townspeople acquire housing, they begin to look for work. For example, I can go to work in the office. Offices are the main source of tax credits to the city budget. Someone may go to work for the plant. Factories produce resources - metal and concrete. All buildings require electricity. To do this, you will have to build a power plant. In addition, in some buildings you can work yourself, while the player’s energy will be consumed, which will be restored over time. For example, a player lacks metal. Instead of waiting a couple of hours while it will be produced at the factory, the player can go into the factory building and work there himself. And of course, the essence of the work is to click on the button labeled "Work" 100 times)
If residents cannot find work, they will have to pay unemployment benefits. However, if there are not enough workers, then the buildings will work inefficiently. Therefore, it is important to constantly monitor the balance of jobs and the number of inhabitants.
There are quite a few buildings in the game. There are hotels and restaurants, police stations and fire stations, banks, fitness centers, exchanges, etc. The player gains experience for the construction and improvement of buildings and can improve the level of the city. At the same time, he receives 1 golden key, which will be needed during the construction of special buildings. Something like skill points in an RPG. For example, you can build a bank, then when you exit the game, all accumulated funds will be transferred to the deposit, and the player will receive interest for the entire time of absence from the game. Or you can build a tax office, which increases taxes to the city budget by 10%. In addition to the main and special buildings, there are also service, unique and decorative.
Transport
There are vehicles in the city. They are made only for beauty. If you build the city administration, limousines will start to ride around the city. You can also build a show room, then sports cars will start to drive around the city.

Player interaction
Among the outbuildings there is a beer pub and post office. Having built them, players will be able to interact with each other, for example, to exchange invites and resources. At the same time, network access is not needed. Players will exchange bitcodes, this is something like promotional codes or coupons. Each city has a unique index, which is displayed in the mail building. It must be indicated as the recipient when sending the parcel. In addition, of course, you will need to choose what to send. For example, you can send 1000 units of metal, and the received code will need to be reported to a friend. However, this code cannot be sent to several players, since the recipient’s index is encrypted in it.

Similarly, promotional codes are generated that are inserted into messages on the wall in social networks.

How successful this decision is, time will tell. I hope that the players will appreciate the original idea.
Participation in Games Jam
The project managed to take part in Games Jam Kanobu 2016 and end up in a selection of interesting projects. Alas, I didn’t reach the final. But it was possible to communicate with other developers and get useful feedback. Unfortunately, there are practically no real players at such events. But most importantly, the game is our publisher. He became the Russian company Herocraft. And this is awesome! In addition, there were 2 more proposals, the discussion of which ended without a result.

So, I’ll briefly share my tips on hackathons and jam:
- look for a team. The probability of finding a team at the hackathons is small. Usually already formed teams participate, and free artists rarely look at hackathons. Try but don't count;
- look at other projects and their results. Determine for yourself the most popular areas in development and “borrow” good ideas);
- Look for reviews about the game;
- look for the publisher;
Publisher Experience
First of all, an agreement is drawn up, which stipulates platforms for the release of the game, legal and financial issues. Before signing the contract, you should prepare a list of questions and get answers to them. For example:
- what improvements will be required?
- How long can the refinement process take?
- what are the conditions for termination of the contract?
After that, the process of finalizing the game begins. The publisher makes a list of tasks to be completed. I advise you to immediately decide on a convenient project management system. In my case, it was GitHub. There you can store the source of the game, builds and other files, as well as start tasks and bugs.

Regarding Tap Tap Builder, the main tasks were:
- completion of the training system
- improvement of the interface
- integration of analytics tools
- increase in game performance
- creation of content for game stores (icon, banner, description, screenshots, video)
- development of tests
I note that many subtasks had to be postponed until release due to the complexity of their implementation. At the test development stage, testing is iterated on the developer's side, because all submitted tests must be at least correct.
After the game is finalized, iteration of testing on the publisher's side and correction of detected bugs begins.
And brought to the standard and lay down build is sent to the soft launch (soft launch). Softlanch is a limited publication of the game, for example, in any one country and on one platform. The task of the softlanch is to get feedback from real players and determine the main metrics of the game (income, retention, etc.). Based on these data, a decision is made on the appropriateness of the world release. In case of a failure of the softlanch, the contract is terminated, the game is returned to the developer, and he can publish it on his own.
Game optimization
The first attempts to run the game on mobile devices revealed significant performance issues. The game showed 10-15 FPS on an empty island, while the devices were far from the weakest (Sony Z1 and Asus Transformer). Subsequent therapy allowed to increase the speed of the game several times. So, in order.
Script optimization
Do not feverishly start looking for problems in the game and optimize the most likely bottlenecks. The first thing you need to do is run Profiler (available in the free version of Unity 5).
I immediately discovered problems in the scripts. The correction did not take much time; all optimization was reduced to caching information and reducing the number of calls. By the way, I recommend the Memoizer design pattern. Its essence is to remember the result of the function for subsequent calls with identical parameters. Parameters can be not only simple types, but also structures and classes. Below is an example of using this template.
public static readonly Func SumMemoized = Memoizer.Memoize(Sum);
private static int Sum(int a, int b)
{
return a + b;
}
Implementing a Memoizer Template
public class Memoizer
{
public static Func Memoize(Func func)
{
object cache = null;
return () =>
{
if (cache == null) cache = func();
return (TReturn) cache;
};
}
public static Func Memoize(Func func)
{
var cache = new Dictionary();
return s =>
{
if (!cache.ContainsKey(s))
{
cache[s] = func(s);
}
return cache[s];
};
}
public static Func Memoize(Func func)
{
var cache = new Dictionary();
return (s1, s2) =>
{
var key = s1.GetHashCode() + "." + s2.GetHashCode();
if (!cache.ContainsKey(key))
{
cache[key] = func(s1, s2);
}
return cache[key];
};
}
}
3D model optimization
After the problem with the scripts has been resolved, it's time to do the rendering. The fact that building models were made of primitives that had different materials (and colors) had a significant impact on performance. Statistics showed about 600 draw call (after autobatching) and 100,000 triangles. For comparison - at the moment, for a spherical game in a vacuum, it is recommended to have up to 100 draw call and up to 30,000 triangles.

The first solution is to reduce the number of surfaces. I had to import an asset for editing meshes. Many buildings contained cylinders, and a standard cylinder has 20 faces and 80 triangles. Simplification of the geometry of the cylinders to 12 faces gave a tangible effect to the detriment of the small angularity of buildings.

The next step is to remove the invisible faces from the primitives. As a result, a significant reduction in the total number of triangles (about 20%) brought no increase in FPS. The engine probably doesn’t spend resources on rendering invisible geometry, even if it is within the camera’s visibility. The result is a futile step.
Batching
The next step is an attempt to use static batching. I’ll briefly talk about batching for those who don’t know. Batching is a grouping of meshes (mesh) into 1 common mesh before calling a draw call. It just so happens that it’s easier for a video card to draw 1 object at once than in parts for several calls. There are several nuances. Merging is only possible for meshes that use the same material. Secondly, the total number of vertices in general should not exceed 900. And thirdly, changing the transform (position, rotation and scale) of child objects is impossible. For example, the wheels of a “skatched” car model will not spin. Batching is static and dynamic. Dynamic batching runs at run time and requires no action by the developer.

Static batching can be implemented in two ways - check the Static checkbox for an object on the scene or prefab. These can be static game objects, such as landscape elements, vegetation, and buildings. The second way is to use StaticBatchingUtility at runtime.
It was logical to do static batching for buildings, which I did. As a result, the game began to work even slower! As it turned out, forced static batching disrupts the operation of dynamic batching, which works more efficiently and groups meshes from different logical objects, for example, belonging to different buildings. In addition, automatically grouped objects can move relative to each other (the grouping is canceled). Bottom line - the use of static batching can be useful only with a deep understanding of the logic of its work.
Shadow optimization
The next observation - disabling dynamic shadows reduces the number of triangles by exactly 2 times, which gave an increase in FPS of about 20%. Changing the quality of the shadows did not produce a visible effect. In this situation, there is a simple standard solution - replacing shadows with sprites. Such shadows are often called static. The question immediately arises - how to get the shadows of objects, because not to draw them by hand ?! The easiest solution is to use blurry circles. This would be suitable for character shadows in a simple runner or shooter. This solution did not suit me - the shadows should repeat the geometry of the buildings. Well, I had to write a script to dump the shadows. The algorithm is simple - the script takes turns creating buildings (from prefab) and takes a screenshot of the building with a shadow, saving it to disk. Then the second script performs elementary processing of the received images, by filling the shadow with black and removing the background (in color). Alas, I did not find any ready-made solutions, share your experience in the comments.

The result is a 20% increase in speed when minor artifacts appear (when applying and layering shadows). By the way, sprites with shadows need to install a Packing Tag, for example, Shadow. Then Unity will create an atlas for them, and ALL shadows will be drawn in 1 draw call. And one more note - on some devices there may be problems with displaying dynamic shadows, so replacing them with static shadows will avoid problems.
Further optimization of 3D models
Unfortunately, the result of the optimization was unsatisfactory - the game showed 20-25 FPS on a powerful device. I decided to write another script for baking models. The bottom line is to generate a new mesh and apply a texture to it to paint the buildings. The texture is a simple 8x8 color palette that can automatically fill up when new materials (colors) appear. In this case, it is necessary to form the correct UV-scan (UV-mapping). There are plugins on the Asset Store for baking objects, but they did not know how to generate a texture based on the colors of materials. I will not dwell on the details of the implementation, because I understand that my situation is quite specific. Although, on the other hand, this method can find application in the now popular voxel graphics. After all, making prototypes of models in Unity is much more convenient than studying modeling. Unless, of course, in your team, there is no modeler like mine. I didn’t have anyone at all).

The final result exceeded all expectations - the speed of the game increased significantly. The game shows 50-60 FPS in a huge city. Further optimization is not required.
Conclusion
I apologize for the fact that the article was very large. After all, I wanted to talk about the game and share experience. And finally, my advice is to make cool prototypes and do not waste your energy on optimizing the game until the working version appears.
I will be glad to hear your optimization tips in the comments, and also ready to answer questions!
Link to a game on Google Play