Losing 95% of the size to make “Hundred Scenes of the South of the Yangtze River” into a mini game

Losing 95% of the size to make “Hundred Scenes of the South of the Yangtze River” into a mini-game

Last year, the ancient-style simulated business mobile game “Hundred Scenes of the South of the Yangtze River” successfully broke out and became a phenomenon.

Developers this year asked themselves how to move it to the mini-game platform? Is it a conversion or a complete rewrite? Which technical solutions can be used to achieve a game experience equivalent to the app version while the package size is only 1/20 of the original version? Dacheng Xiaopang, the head of the development of Cocos version of the games, brought his nearly 300-page PPT to make a comprehensive technical sharing in two offline activities Cocos held this past month.

Convert or rewrite

The “Hundred Scenes of the South of the Yangtze River” game package is about 600+MB. Some users reported that their mobile phones became very hot when the game ran in the early launch stage. Cocos Creator was decided upon for the mini-game version, and they would rewrite the project. It only took 1 day to make the demo. After 4 months of optimization, we finally compressed the package body to about 30MB while ensuring that the game experience is comparable to the App version.

During the optimization process, we also did the following work, in which the code part needs to be redesigned and written.

Rendering optimization

The first few jobs for bringing the native version of “South Hundred Scenes map” transplanted to the mini-game was to solve the high power consumption, stop the game from heating the phone, high draw calls, and other issues.


Co-batching is the fastest and most effective way to reduce Draw Calls. Optimize the same texture and merge multiple pictures into one atlas so that no matter how many different pictures are generated, the batch rendering will not be interrupted, and the Draw Call will be reduced.

However, “Hundred Scenes of the South of the Yangtze River” has many resources, and the order in which each player uses the resources is not the same. If the resources used by the players are on different atlases, the batch rendering will still be interrupted, and a Draw Call will occur. Therefore, the response to this situation, we have adopted a Multi-Texture optimization. The principle is that the traditional atlas determines which sheets are needed, and converts them into one batched atlas, thus greatly reduces the Draw Call produce.

In addition, through the command gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS), you can know that the Shader supports at most several atlases in a device. The test found that more than 90% of mobile phones currently support at least 8 images, so we set the number of the batch atlas’ to 8 images. Because there are 8 atlases in a batch, we use this idx to determine which atlas to use for a certain image. The code is also straightforward.

Dynamic picture

The mini-game version uses Cocos’ dynamic picture combining mechanism so that the pictures downloaded on the CDN can also be combined. To improve the efficiency of combining pictures and avoid wasting space, we will crop the pictures with substantial length or width.

For example, the flagpole in the picture on the left, because the picture is too long, will lead to a waste of space when dynamically combining the pictures, so we cut the picture of this flagpole into two, as shown in the picture on the right, and then stitch them in the project.

Use the same material resource

In “Hundred Views of the South of the Yangtze River,” the original picture outside the display range will change from ink to color when the player moves the map.


The traditional solution is to change the material of the picture. When the map moves to the node to be displayed, the node switches the material one by one to achieve a “fade in and fade out” effect. But after trying it in the project, we found that this will cause the Draw Call to rise, and dragging the map is a very frequent operation, and the actual effect in the game is very poor.

Therefore, here we use a unified Material for all urban object resources, whether a character or a building, normal state or fade-in state, and use vertex data to pass “time parameters” to save performance and achieve the goal of ease of viewing all buildings and characters. Creation, movement, destruction, etc., can all be completed with only one material.

Many people think that using such a complicated scheme for an ordinary picture will affect performance and result in poor performance. But the actual test results are not bad. This also tells us that prototyping should prevail in game development, and we can’t take it for granted.

Optimize Shader’s input data

Since the attribute of Color will not be used in the image resources of “Hundred Scenes of the South of the Yangtze River,” we will remove the original Color data in the material.

The following image is normal vertex data:

Next, remove the original Color data to store other information needed in the project, reducing the amount of data transferred between the CPU and GPU.

Hierarchical planning

We place different types of resources in corresponding levels. “Hundred Scenes of the South of the Yangtze River” is divided into 13 levels. The following figure shows only some of the more essential levels:


One of the more interesting ones is the banner layer. The flag is a common element in “Hundred Scenes of the South of the Yangtze River.” Still, due to the actual technical limitations of the project, it is impossible to make a flag in a complete keel animation. So we adopted a way of dynamically organizing hierarchical relationships to solve this problem. For example, this is an original banner prefab:

Using the method of dynamically organizing the hierarchical relationship, the flagpole is separated from the flag surface. The flagpole is placed on the ordinary building layer below. The flag surface is divided into a separate flag layer and placed on the upper layer to avoid constant rendering during rendering. Circumstances that have been interrupted to join the batch.

UI rendering optimization

In the UI part, we did not use dynamic graphics or MultiTexture. We left dynamic graphics for the characters and buildings in the game, and we didn’t use MultiTexture mainly because of the development cost. But under our optimization, the Draw Call of the game can now be reduced to meager numbers.

We have also done layering in terms of UI, such as our button layer on the left side of the picture below, which contains button parts, and the right side is our label card level. In this way, we can divide the atlas according to the functional area and then correspond to the level in the game without interrupting the batching.

Custom engine

Cocos is an open-source engine. We can customize and modify the engine according to the actual needs of the project to achieve better results.

Enhanced TiledMap

Based on the original TiledMap component of Cocos Creator, we have expanded new functions to it. The following figure shows the component that comes with Cocos.

I won’t go into details here. If you are interested, you can go to the official documentation to check it. Let’s mainly talk about the new functions that have been expanded.

  1. Diamond Tile: Many tiles in TiledMap are used in the game, but the default transmission method of the engine is a rectangle, which will cause data waste and redundancy.

These pictures are the first diamond rule, so it is a simple and straightforward calculation based on the width and height were.

Cut the extra part around the rhombus, so it is evident that the size of the picture is reduced by half. Note that non-standard graphics cannot be used in this way.

  1. Share Culling: “Hundred Scenes of the South of the Yangtze River” has three layers of TiledMap. When checked, only the first map layer of TiledMap will be processed to determine the scope of the visible area, while other map layers will directly copy the first layer. The processing result of a map layer, which can save a lot of performance.

  2. With Color: If you don’t need color data, you can remove it to reduce the amount of data transmission.

Turn the road into a tile

The road in the game does not need to be faded in and out. If it’s used as a common building resource to render with the previous material, it will consume a lot of performance. Therefore, we use the road as a part of the TileMap so that the road does not need to be rendered with the materials mentioned earlier.

There is also a minor detail. The width and height set in the Tiled Map Editor have nothing to do with the actual project. Therefore, the map block can be reduced according to the actual project requirements during generation to reduce the use of resources.

Resource compression

To compress an original 600+M game to the final 30M or so, resource compression is indispensable. We need to reasonably compress the game resources to make them more suitable for small games to run without affecting the final display effect of the game.

Picture zoom

For resources of different types and definitions, we can set different zoom ratios. We scaled most of the buildings to 0.65 times, and the mountains and rivers in the background were scaled to 0.3 times. In addition, even if the characters are used in the same position because each character has different colors and accuracy of the background, you can also set different zoom ratios for them.

So we use CUSTOM mode for all Sprite components, which allows us to control the proportions freely. Different images use differentiated configurations, set different zoom ratios, and use scripts to control the zoom ratios. In this way, various image quality and volume versions can be packaged, and dynamic combined images’ utilization and partial performance are also improved.

Picture subtraction

After comprehensively comparing the two well-known tools, tinypng and pngquant, the project finally chose to use pngquant to compress PNG images in batches. pngquant can customize the compression quality, and pngquant is open source, easy to maintain, and risk controllable. Pngquant also provides tools like ImageAlpha, which can view the effect of the image after color reduction in real-time, and it is convenient to adjust the parameters.

pngquant can be found at https://pngquant.org/

It should be noted that since Cocos will combine images and if the images before the build are compressed, some of the previous compression work may be invalidated when the images are combined, so we need to compress the images after the build.

In addition, we also recommend learning more about the image format and its principles. Not all images need to use PNG format, and JPG may also be used.

Scene culling

Part of our demand is only to render visual objects. So what method is used to determine which objects are visible? In the beginning, we used a quadtree, but its effects in JavaScript were not good. So we have to divide the grid map, the Grid cell size should be no issue, but the cell edge length should be an integer power of two to facilitate the use of bit operation to enhance performance.

As shown in the figure below, the red frame is the lens, so what needs to be rendered is the grid that appears in this red frame. Then we calculate according to the coordinates and size of the building to determine which row and column the building is in to determine whether the building is an object that needs to be rendered.

This is a simple detection function that you can expand according to your own project requirements.

In addition, in order to prevent the emergence of special circumstances, the visual range of judgment needs to be larger than the actual range.


The pathfinding algorithm used in “Hundred Scenes of the South of the Yangtze River” includes A* for single source and single point and Dijkstra for single source and multiple points. But what we will talk about here is not the pathfinding algorithm but the usage optimization in the game.

When the map is huge, and there are many buildings and characters, the performance of these algorithms will be very lossy when executed together. So we spent time working on shared wayfinding. The pathfinding process is assigned to several frames of a calculation, so it will not be a lot of computing concentrated in a certain period of time. The game will not be much impact on performance.

In addition, we also made bold optimizations in the game by managing the wayfinding tasks in a unified way and only serve one character at a time. Someone may ask, won’t a character not know where it is going and what objects are waiting there? First of all, the starting and ending time of pathfinding for each role is different. The exact time is very short, which is equivalent to assigning pathfinding to different frames and performing alternately.

Talk about performance again

Blur effect

When the player opens any interface of “Hundred Scenes of the South of the Yangtze River,” the background of the game needs to be blurred, and the character animation in the background still needs to be played normally.

After a series of studies (data refer to the PPT), we chose a preferred embodiment. A scene is rendered to the small RenderTexture, then Kawase Blur after amplification as shown below.

RenderTexture pool

When creating a RenderTexture in a small game or on the Web side, performance is lost. After we finished using RenderTexture in the game, we don’t destroy it but put it in a buffer pool. The next call from the buffer pool is added to RenderTexture to meet the requirements.

Click to detect

“South Hyakkei map” There are many buildings, and when the user clicks, not simply to get information on a block of the terrain, but to give each building a polygon detection area. However, if the polygon detection area is also moved, the performance and logic are not good as the building is movable.

So in practice, we let building movement and the corresponding polygonal detection area not moved and set it on the origin of coordinates. When the user clicks, the clicked coordinates are subtracted from the coordinates of the building relative to the origin, and then click detection can be performed. Similarly, if a building is in a reversed state, you can click on the image coordinates, and the polygon detection area still can not be adjusted. Similar to other situations, you can also find out how to deal with polygons in each situation.

Array sort

Array sorting is an optimization module that everyone tends to ignore. Quick sorting algorithms like Array.sort() are more suitable for chaotic data. In the “South Hundred Scenes map,” each frame will be on the scene characters and sorting building. The difference between two consecutive frames will not be great, that is, the relative order of the data, which is more suitable for use the Insertion sort algorithm.

Other optimization

“Burn after reading”

There are some low-frequency displays of large pictures in the game, such as the announcement when entering the game, the cards drawn, etc., the player will not see them again after reading it in the game. For this type, we use the “burn after reading” idea.

Like these big pictures, we usually download from the remote server to the local cache first and generate Image objects and cc.Texture2D and renderer.Texture2D.

Let’s briefly explain it through pseudo-code. When loading the image, add the image to the TextureRecycle, a recycling tool class we created ourselves.

When the view is closed, these pictures are recovered through the tool class.

In the recycling phase of the picture, all the objects used above can be cleaned up.

Build optimization

In the build and release process, the project uses a large number of automated scripts to optimize the build process. Including full platform to build, upload gaming platform, resources, pre-treatment and post-treatment, CDN and secondary synchronization, version control encryption, etc. However, the script is sometimes successful, and the script sometimes fails. The long build time also caused many troubles, so we also need to do some additional optimizations.

The new version of Cocos adds a third-party open-source compression tool called Sharp. The compression level is 0-9. The larger the value, the longer the compression. The default parameter of Cocos is 6. Since we have been compressing the pictures ourselves, we’ll place the parameter to 0 to reduce a lot of time to build.

The construction time of each platform is always extremely long. The reason is that every time the platform is built, Creator has to regenerate the corresponding platform atlas. After finding the reason, before each build, we modify the actualPlatform parameter in the info.json in the corresponding directory to the corresponding platform name before packaging. This change shortens our build time from 15 minutes to about 10 minutes and Improved efficiency by 30%.

After ongoing optimization, we saw that during the live demonstration, this premium game’s official demo game still only had 6 Draw Calls and still had rich characters in the game scene.