[Engine] Connections [ 1 ]![]() | [Engine] Main cycle time [ 2 ]![]() | [Engine] Bytes per send [ 3 ]![]() |
[Server] CPU usage![]() | [Server] Memory usage![]() |
Development of gaming applications on canvas is a promising direction in which many development teams are now moving, for example, the Estonian project mightyfingers.com
Some ideas are taken from the article www.html5rocks.com/en/tutorials/canvas/performanceHTML5 canvas Basics
Terminology: a tile is one square, for example 32 Ă— 32; chunk - 8 Ă— 8 tiles.
Consider the features of the tile engine. Here is the model I came to:
1) Subpixel drawing for tiles is simply not needed.
2) Several transparent canvas-s located on top of each other are used. If terrain was not modified, there was no scrolling, then you can not redraw it - this gives a small performance boost (compared to cache optimization).
3) As in the game model, the whole field is divided into 8 Ă— 8 chunks. Since 64 drawImage calls for 32 Ă— 32 images cost much more than a single call for 256 Ă— 256, visible chunks are drawn into a cache consisting of two layers: under the player and above it.
4) The cache is associative, that is, a pair of canvass is used by several chunks on the map: a chunk with coordinates (X, Y) uses a cache with coordinates (X mod A, Y mod B) where (A, B) is the cache size. At the same time, cache sizes should be such that two chunks with a common cache do not appear on the screen at the same time, there will be a race.
5) In addition to the canvass, cache numbers in the cache are stored in the cache, which are generated by the logical map. By the number you can uniquely draw the tile.
6) When changing the visible chunk, the corresponding object in the cache is marked as dirty, requiring redrawing. At the same time, neighboring chunks can also be marked as dirty: you never know, suddenly a new shadow falls there, or there you have to draw a cut of the stone.
7) In case if it is necessary to redraw a dirty or new chunk, the table 10 Ă— 10 is put in the buffer and the surrounding tiles. This buffer generates an array of tile numbers - it is already 8 Ă— 8. The tile number can also contain information about the shadow falling on this tile. In those places where the tile number has changed since last, a redraw occurs.
8) For tiles, it is more convenient to use not drawImage, but createPattern and fillRect. fillRect is also more convenient because they can draw several identical tiles in a row, and this will be faster.
This test reveals a strange effect: that for textures 32 Ă— 32 there is a significant performance increase on the iPad 2.5 times:
jsperf.com/canvas-texture-draw-vs-fill
Comments with the addition of TheShock9) And finally, why all this is necessary: ​​when scrolling instead of drawing a heap of small images 32 × 32, drawing from the cache takes place. The drawImage call is much smaller, it seriously affects performance.
Green, yellow, shadows - are drawn on the first layer, each 32 Ă— 32 square can be drawn independently of the others, since the tile number contains information about the substrate, wall and shadow.
The second layer is drawn in red, it is shifted 8 pixels up.Found bugs
So, during the collective testing the following problems were discovered:
1) On a large map of a certain size, the tile cache suddenly started up, which led to wild brakes that only chrome could handle. The whole thing was in the periodic map: the card sizes (W, H) should be divided by the cache sizes (A, B).
Let W, H = 7 A, B = 3, corner chunks fall on one cache position, and on a periodic map they fall on one screen, which leads to a race. The same situation is observed throughout the “seam” of the card.
2) Client profiling revealed bottlenecks in code using java-collections. In these places, the collections are replaced by samopisnye structures.
3) Changing the network protocol and switching from utf8 to binary web-sites in those browsers where they are supported (Chrome, Firefox) allowed reducing the average client traffic to 12 Kbps. The native ArrayBuffer is used, I note that Firefox does not implement the DataView class, so we manage with the usual Uint8Array.The graphs at the beginning of the article show:
[1] Connections - the number of active connections to the server.
[2] Main cycle time - how long the different stages of the main cycle occupy. Tick ​​lasts 100ms, preprocess - checking commands from clients, tick - update game state, Observers - forming messages for clients. Total - all together, plus the time it takes for messages to go to the network buffer. It is measured in milliseconds within one second, so that when Total approaches the 1000 server badly, overhead starts to appear.
[3] Bytes per send — net traffic sent to clients, without websocket frame headers, in bytes / sec.Gameplay Changes
Almost all the time it took to accelerate the engine, client and network protocol. Serious work was done on optimization and refactoring, eliminated a lot of bugs.
But I managed to do a little more by the gameplay: it became easier to lock up the enemy. But most importantly - added a ball and gate! Those who score receive a rescue shield. Particularly agile collect 3 shields and run like superheroes :)
Source: https://habr.com/ru/post/145505/
All Articles