📜 ⬆️ ⬇️

Tanchiki on node.js

image Here, finally, is my first work that has overstepped the barrier in the first 90% of readiness. I want to submit to your court javascript tanchiki, which can be played directly in the browser. It requires a browser with support for websockets and canvas (should work in Chrome 14.15 and Firefox 7). The server is also written in javascript. I will try to do without the code in the article, but if anyone is interested in the source code - they are here . I also make a reservation that nodejs was chosen not because of some of its features, but only because of javascript, which I am trying to master. Demo which pulls only 2-3 games at the same time and will not sustain any habraeffekt.

I decided to make an article in the form of several paragraphs covering various aspects of the code. I do not want to go into obvious things, so I will try to touch only interesting moments. These include topics that I have discussed online.

General principles

Let's start with a description of the architecture of the project. The code can be divided into two main parts, the server and the client. Since everything is written in JavaScript, some parts of the code are used on both the client and the server. But I would not call it a big advantage, because the logic of the client and server classes are usually different.
In essence, the client’s role is to handle user input, and display the game process. Everything else happens on the server. Calculation of the movement of tanks, the flight of bullets, the handling of collisions and in general everything that relates to the logic of the game — takes place on the server, since it is not worthwhile to trust such things to the client. The client is also assigned animation, which does not affect the game. For example, on the server of a tank object, only the property is set, that the tank is now armored or that the tank will be given a bonus. And the calculation of which sprite to draw at each time point is performed by the client. Also, for example, an explosion of a tank triggers an animation on the client from a sequence of corresponding frames, but on the server the tank is simply deleted at the moment of the explosion. Such an organization allows you to protect game data from outside interference. And also not to load the server with unnecessary actions.

Canvas

The display of the playing field is made quite primitive. At the time of the start of the game, with the help of setInterval () every 50ms a function is called that redraws the entire field. I think it is possible to redraw only the changing parts of the playing field, but for now these are only plans for the future. Each frame has its own number, increasing by 1 each time. Based on this number, the displayed objects make up the list of sprites to be drawn. For example, an armored tank "consists" of two sprites, the tank itself, and the twinkling contour around. This feature rather simplifies complex animations.
')
Map

Perhaps the main code that is used both on the server and on the client is the code of the playing field. I had to break my head over how to store objects on the map. One could take a two-dimensional array, the indices of which would be coordinates. So, to describe the map, it would be enough array of size 26 * 26. Also, this method would allow to easily search for the intersection of objects. But this is only at first glance. In order for tanks and bullets to move more smoothly, the size of the array would have to be significantly increased. Plus, the tank can go on the ice, or a bullet can fly over the water, that is, the array will need not two-dimensional, but three-dimensional. In general, a lot of questions and their solutions are not at all attractive. Although this option is not suitable for storing data in memory, it is very convenient for storing the card in a file.

The second way I stopped is to keep a list of objects on the map. Each object has coordinates, as well as width and height, which are used to search for intersections of objects. The disadvantage of this method is that in order to find the intersection it is necessary to go through all the objects present on the map, and these objects (pieces of walls, trees, water, tanks, bullets ...) on each map are about 1000. That is, for each moving object you need 1000 times to execute the function intersect () which checks the intersection of two rectangles.

The third way, which is currently in development - is to present the map in the form of a tree. Where the root is the whole map, the first two branches are the left and the right half, and so on. the leaves of the tree are actually game objects (tanks, bullets, walls ...). This approach reduces the search for intersections with the object from 1000 operations to 20-100, depending on the fullness of a particular sector of the map.

Synchronization

Go ahead. The data between the client and the server must somehow be synchronized so that the client always has the current state of the game. I chose the following option for this: all changes on the server are logged and then forwarded to the client upon request. Each client has the last sync time, so each time the sync, the client receives changes in the last few tens of milliseconds. This method takes quite a lot of CPU time, but I didn’t think of another fairly simple option. Of course it makes no sense to transfer to the user in general all the server data. The user does not need the data of games of other players. Therefore, there are several logs on the server: all users are online, all games are online, users in the current game, objects on the map of the current game, etc.

The log is formed as follows: every time the data that must be transferred to the client changes, a reference to the changed object and the time of the change is written to the log. If the log already contains a record of this object, then it is deleted and a new one is recorded. That is, there is only one link to the changed object in the log, and this link is marked with the last modified time. When a client requests data for synchronization, the server selects objects in the required period of time, serializes them and sends them to the client. Here there is another field for activity, since if the object has only one coordinate changed, the entire serialized object will still be sent to the client. But this method has one big plus. The user can connect to the server at any time and get the current status of the game, chat and list of players. Although it is not possible to connect as a player to an already-running game, I plan to add the ability to view already-running games.

For data transfer, the socket.io library was chosen, since it is the only known way for me to support WebSockets on the server. Socket.io is not ideal, because it is somewhat redundant, as it supports data transfer not only via WebSockets, but also via flash sockets and in several other ways. But the game comes with adequate speed only when using WebSockets. There are also plans to switch to a secure connection, which will allow you to establish connections through some proxies, which for some reason do not want to send traffic to WebSocket.

Performance

Now with the performance is bad. The server on Intel Pentium 4 3.00GHz can pull only 2-3 games at a time. The results of profiling pointed to 2 problems, it is the search for object intersections and the logging of changes on the server. As they say, the second 90% of work remained.

Copyright

Sprites for the game were found and downloaded from the Internet. Enlighten anyone as far as legal. That is, if I want to make a remake of a game, can I even use the graphics from the original game?

The second part, about optimization.

Source: https://habr.com/ru/post/132362/


All Articles