Interactive online game of HTML, CSS and JavaScript
Having somehow played at hexbug in the office, the idea was born to write a toy for similar reasons. I am a web developer by current activity and therefore I wanted the game to use only HTML, JavaScript and CSS - tools familiar to every web developer. No flash or canvas. It sounds hardcore, but in fact now HTML + CSS3 is a very powerful and flexible means of visualization, and writing game code in JavaScript is a pleasure. In addition, I wanted the game to be with network multiplayer, moreover, interactive - there are no drafts, card games, step-by-step strategies, everything should be in action and movement.
Here's what happened in the end:
')
In the article I will leave a set of notes arising from the writing of a prototype of a toy, aimed more at the “how to make it easier and faster” approach. I think the article may be useful as a kind of help for newcomers in this exciting business.
Gameplay
The task of the game is to grow your colony of beetles and destroy all enemy units. The beetles randomly run around the playing field, and the player’s task is to help them find bonuses in the form of various foods and assist their units that were attacked.
Bonuses in the game: cupcake - gives 5xp, when typing 15xp the beetle multiplies Apple - restores 50hp, if the beetle is completely healthy then it adds 15 additional hp pepper - increases attack by 5dm acorn - gives 2xp and is thrown at the nearest enemy, on hit it does triple damage the fly agaric - gives 1xp and allows you to make a poisonous shot, on hit deals 1/2 damage and slows the victim
Can play from 2 to 4 people. You can also simply connect to the server from different browser tabs, and play alone. You can try to play here . Sources on github . Archive with the game .
Graphics
HTML and CSS are certainly not very quick in terms of performance when it comes to rendering graphics required in interactive games. But if our goal is to write a prototype of a toy, then this option will completely go away. In the end, the “narrow moments” in the form of drawing the main game scene can be further quickly transferred to the canvas.
To work with graphics in a 2D game, we need to move, rotate and scale the sprites.
Move the sprite by setting its position: absolute and changing the left and top
To rotate the sprites, use transform: rotate . And with the help of transform: origin, you can specify the axis of rotation (by default, it is in the center of the sprite).
For scaling, we change the sprite size using the width and height properties, before setting the appropriate value in the background-size:
Hardware acceleration
To improve the performance and therefore the smoothness of the animation, you can force the browser to use the GPU to draw animations. To do this, you need to work with sprites as with three-dimensional objects. Now let's do the operations of moving, rotating and scaling through translate3d, rotate3d and scale3d:
All these operations were enough to collect the graphics in the game from several sprites drawn in the “paint”.
Physics
In addition to drawing game objects, you also need to adjust their interaction with each other. In bugsarena, all the interaction is in the handling of collisions of sprites. Since it is planned to do everything as simple as possible, we restrict ourselves to school mathematics. Probably one of the most frequent mathematical operations in games is finding the distance between two points. According to the sute, the task is to find the hypotenuse in the triangle:
We get the formula:
Now, thanks to this simple formula, you can do a lot of operations, such as finding the distance to an object, finding the nearest and furthest objects, finding objects in a given radius, as well as detecting objects in the form of a circle. All game objects are drawn in fairly small sprites 20x20 in size, you can neglect their shape and calculate collisions as if they are all inscribed in a circle with a diameter of 20. Then you can say that 2 objects collided when the distance between their centers is less than or equal to the sum of their radii.
And a few more notes:
To set the angular values, use radians, not degrees. All angular values ​​from Math are returned exactly in them. Let me remind you a full turn equals 2 * PI radians
Use the concept of a vector to specify values ​​that have direction. Even the position of the sprites can be described by a vector. You can create your own vector class or use the class described in this article or anyother . For example, the vector sets the speed of objects, as it has a magnitude and direction. In this case, to increase the speed in two, we simply multiply the vector by 2, and to change the speed in the opposite direction, we invert the vector (multiply by -1).
If the game requires complex physics, you can look in the direction of box2d-js . This library will allow you to create a game world with objects of various shapes, gravity, mass, inertia, frictional force and other benefits of Newtonian physics.
In a few words, the game logic can be described as follows: There is an object of the Game class describing the game world which has an array of objects inheriting from the GameObject class — these are all objects of the game world. Each Game game frame is traversed across all game objects and calls the step method for each. The step method of each object describes what it should do for this frame (move, handle collisions, destroy, etc.). To implement the OOP in the game, use the Class object from John Resig's Simple JavaScript Inheritance , modified to support mixins and static properties. Probably one of the most successful patterns for creating new objects in games is the use of the factory method . The bottom line is that we will not directly call new Create objects, but use the method that does it for us. The factory method will save us from the fuss of connecting a new object to the game world.
For example, we want to create an object of the class Block to include it in the game world and place it in a given place:
So when the game is already written I want to diversify it with several game cards. Creating all game objects with code (calling method after method) is very tedious and unattractive. Writing your map editor will take a lot of time. But there is a simple way - you can use a text editor or your ide for visual creation using the following approach:
The network code is written using web sockets using the socket.io library, the game server is written on nodejs. To make a simple implementation of an interactive network game, and with the condition that only TCP is available to us, that is also a problem. Now for such games they use fast UDP protocol which unfortunately is unavailable through socket.io, though if you have a strong desire you can look towards WebRTC. It is important that the game goes smoothly without jerks and is synchronized on all clients. The server will be simple and will only deal with the transmission of customer messages, since only their actions affect the course of the game. He will not be engaged in transferring the states of game objects, and just do not know anything about the game world, except for the state of the game - waiting for players / game is in progress
The entire temporary tape of the game can be divided into frames. Clients send messages about their actions to the server, the server accumulates these messages, and after a certain number of frames sends the accumulated to clients. This is like a variation of a very accelerated turn-based strategy - all players are given only a few frames to make their turn (send messages to the server). After these frames, the server sends to the clients all the actions for the previous turn, which immediately begin to play. At the same time, players can make a new move.
This approach is good because it is simple and customers always know in which frame they start the actions coming from the server and can safely continue the game until this frame. The server, on the other hand, must send client actions some time before the onset of this keyframe, so that clients do not stand idle. The disadvantage of this approach is not too fast reaction to the actions of the players, and some active shooter would be difficult to play.
One may wonder - if we transfer only the actions of clients, then how to synchronize the behavior of objects based on randomness? After all, various bonuses appear in completely random places, but all customers must have the same place. Beetles run very chaotically, constantly changing the direction of their run, and at the same time all this “chaos” should be completely the same and follow the same scenario for everyone. The problem with the synchronization of such behavior can be solved so that everywhere where random variables are used, not to use Math.random for this, but to use your own pseudo-random number generator (PRNG). The point is the following - before starting the game, the server generates a random number and transfers it to each joining client. With the help of this number, the client initializes the PRNG that on all clients will produce the same sequence of pseudo-random numbers. The simplest implementation of such GPSNG is the Miller Park-generator. Implementation on js:
var initializer = 333; // , var gen = new ParkMillerGenerator(initializer); // gen.next(); // 0.5967310000000001 gen.next(); // 0.46109599999999773 gen.next(); // 0.07891199999994569;
We do service from nodejs of application
Maybe not a little bit, but also a useful note. When the server is written, it would be nice to run it on the combat machine as a service for permanent work. I will describe how this can be done on the example of Ubuntu. Go to /etc/init.d and create a shell script with the name of our service there, I will have bugsarena. I’ll draw your attention to the fact that the block that starts with “BEGIN INIT INFO” is not just a comment, but you shouldn’t delete our service settings.
to start and stop the service. You can also make the game server start at system startup by executing
update-rc.d bugsarena defaults
Do not forget about XSS!
Lastly, you just need to remind you of a very simple attack typical of browser games. Imagine that we have a list of players in some div. And a player with the name "<script> alert ('Vasya comes into the game!") </ Script> "comes into our game. His name is added to the player list div, and all customers receive an annoying alert message. And it still flowers. Through XSS vulnerability, you can safely upload any script from any site. So do not forget about the screening of data transmitted from clients.