📜 ⬆️ ⬇️

How to create a world, hide the corpse and scour the Persian. Stacks of sandwiched keys. Virtual FMS and Federal Drug Control Service (or who resolves 228 and "powders the nose")



Single video demo :
Example 1 - You are a big mushroom, and pills everywhere (like at an acid party)
Example 2 - Man drives a truck
Example 3 - Throwing defective bricks into a well
Example 4 - Battle of Robots (PvP)

Duplicate server: 1 , 2 , 3 , 4

')
Hi Habra! This is the third article about the game engine StalinGrad. In previous articles, you and I have already been raping DHTML and have a bit taken apart the architecture. This article will be in two parts - practice and theory. If you are not a web developer, you can immediately browse to the theory (perhaps it will be interesting for you or write some advice).

What considered in past articles:
Launch before production
JS -> EXE -> CHM -> APK
About architecture
Analysis of weapons
Parsing camera

We write the first game


An example of creating a game:

<div id="TV"><div> 

 var S = StalinGrad, //  ,    Russia = S.world(), //   ORT = S.camera(Russia, "TV"), //   Vova = S.people("robot1"); //   S.map(Russia, "Moscow"); //     S.module.controller(Vova); //     ORT.bind(Vova); //     Russia.add(Vova); //     

A demo of what happens can be seen here .



Expect more? But the next 10 thousand letters, we will disassemble what is written here. You can see the code for the demo examples in this and past articles. Scripts almost everywhere fit in less than 20 lines. Perhaps some of you will ask why the title of the article is not such: “- How to write a game in JavaScript in a minute?”. Because we all (well, or 95%) are well aware that this is just a demo. In order for the game to go into production, you also need to connect a bunch of modules, a game interface, menus, missions, videos between levels, screensavers, messages, think through the plot, think over and draw level maps, create a bunch of bots, discuss everything with different managers and designers, get reprimanded from the customer, fix 100 magic bugs, write a bunch of "pasta code" for all sorts of "whistles and fakes." And on the day of release, the customer will ask for another small edit and remake of the half-project (moreover, only with crutches, since there’s no other way). We can say that StalinGrad is not for those who write the game on HTML5 in 20 minutes, but for those who scoffs at JavaScript for several days / weeks / months.

We have dealt with the general logic of the work in the last article, and in this article let's look carefully at each line and API.

How to create a world



 StalinGrad.world(); 

Each world has a set of standard parameters, for example: length, altitude, time of day, gravity, air friction force, etc. Here, as well as when creating objects, you can set your parameters in the following way:

 var temp = StalinGrad.world({ frictionOfTheAir: 0.01, //   gravitation: 88.2, //  jiffy: 0.05, //  ""  .  meter: 10, //  ""  .  max: { x: 3300, //   y: 400, //   speed: { x: 20, //     X y: 20 //     Y } }, respawn: { //   ,      x: 0, //       —       y: 0 } ...  .. }); 

Every object in the world has its own API. The most important functions in it: add () and remove (), to add objects to the world and remove them, respectively.

How to create an object



The general scheme of creating objects:

 StalinGrad._("_"); 

For example characters:

 StalinGrad.people("nyancat"); StalinGrad.people("ninja"); StalinGrad.people("mushroom"); 

Or something different:

 StalinGrad.block("brick"); StalinGrad.material("floor"); StalinGrad.weapon("portal"); 

With this creation, each object receives the coordinates x = 0 and y = 0. Therefore, when creating the object, it is desirable to indicate the coordinates in which it will be located. If you do not do this and start adding an object to the world, it will generate an error in the console: “I cannot add an object. This place is already taken by another object. ” Therefore, write this:

 StalinGrad._(x, y, "_"); 

For example:

 StalinGrad.block(0, 25, "brick"); StalinGrad.material(0, 50, "floor"); 

The standard width / height of most objects is 25 by 25. Why should the initial coordinates be set at the time the object is created, and not later? Because objects have start and end coordinates. If you specify the initial x and y when creating the object, the final x and y will be obtained automatically (x max = x min + length). If you decide to set them later, you will consider the maximum coordinates yourself. For example:

 vav cat = StalinGrad.people("nyancat"); cat.x.min = 50; cat.x.max = cat.x.min + cat.width; 

And even better - do not climb into the coordinates at all, because Most likely, your problem can be solved through the API.

If for some reason you need to change even more initial properties of the object, you can use the following syntax:

 var temp = StalinGrad.block({ x: 0, //    X y: 25, //    Y type: "brick", //  width: 400, //  height: 200 //  ...  .. }); 

Information on all available types you can see in this table .



On the Internet, the Cocos2D game engine has become widely known. I also liked it very much, so I decided to import its base into StalinGrad. Unfortunately, after refactoring the code, the essence of the coconut has changed a lot (only the name remains). You can get it with the following command:

 StalinGrad.grugs("cocos"); 

And add it to the world, for example, like this:

 var cc = StalinGrad.grugs("cocos"); Russia = StalinGrad.world(); Russia.add(cc); 

And what is characteristic, the coconut is also here in 2D. Detailed information about the remaining objects of the class drugs you can see in the catalog.

Many of you probably have the following API question: “- Why are the various functions in the API not separated by their type by additional words?” For example, it is more logical to do the following schemes:

 StalinGrad.object.people("nyancat"); StalinGrad.object.grugs("cocos"); 

Then it becomes immediately clear that people and grugs are methods for working with objects. But it is obvious for experienced ones, but for juniors it is the opposite. In addition, we need to memorize an additional word in the design, which can be reduced. Perfectly reveal the essence of jQuery and Backbone. In jQuery, many methods are so intuitive that you can guess their presence without even reading the documentation. But backbone without documentation is not at all obvious.

How to cheat Persian


We analyze the work with the personal belongings of the character. Honestly, I did not come up with an adequate API, so at the moment the scheme is this:
 .bag.___API(); 

Imagine a man with a backpack and look at the API:
 left(cat); //      right(cat); //      show(cat); //   (,   ) hide(cat); //   ( ,     —  ) set(cat, index); //     ...  get(cat); //      add(cat, id); //    id ...   (    ) remove(cat, index); //     ...   

For example:
 cat.bag.left(cat); 

The API is crooked and I don't like it. So I will rewrite in perspective. Character has to prokidykivat every time, because because of the nesting, I cannot get from the prototype a link to the original object from which the call chain began. If you have ideas on how to implement the backpack API in a more appropriate way, write in the comments.

How to create a camera



The moment has come to create a camera and see the world:

 var NTV = StalinGrad.camera("id_div_"); //     DIV    var RTVi = StalinGrad.camera(_, "id_div_"); //          DIV 

As always, when creating an object, you can specify additional settings:

 var MTV = StalinGrad.camera({ world: Russia, // ,    maxRendering: { //   x: 400 y: 300 }, screenResolution: { //   x: 400 y: 300 } x: 0, //    y: 0 //   Y }); 

Also, each camera has an API, like a joystick: left (), right (), up (), etc. See the documentation in the previous section. For example:

 RTVi.left(); //    RTVi.resize(); //      

Well, the most important for us is the bind () method, which is necessary to bind the camera to the character. For example:

 ORT.bind(Vova); 

Tying the camera to the character, by the way, is possible, including, and only on one axis. For example:

 ORT.bind({ object: Vova, // ,     type: "x", // ,    correction: { //   x: 2, y: 3 } }); 

You, probably, have already asked yourself the question: “What else for the proportions of coordinates ?!” The fact is that if the character is ideally located in the center of the frame, it looks strange. Therefore, a small offset is specified. In this example, the character will be shifted to the left of the beginning of the frame along X on a half-screen, and below from the beginning of the frame by one third. You can change the settings and make sure that the position in the center cuts the eyes (correction x = 2, y = 2).

Demo with downed settings



How to create a joystick



As I wrote in the last article, in order to control the character, we need a joystick. Let's create it:

 var joystick = StalinGrad.controller(Vova); 

Any joystick has a standard API: left (), right (), attack (), use (), turn (), up (), down (). It is through this API that you should control the character, but do not meddle into the coordinates;)

But we will not control the character from the code? Therefore, we do not really need a joystick. And we need a controller module. We will cast a character into it. The module itself will create a joystick for the character and tie the joystick to the keyboard.

 StalinGrad.module.controller(Vova); 

Object Registry


Any factory, from those that were considered above, before returning the object to you will add it to the unified register of game objects. This is necessary in order to remove direct links to each other between objects. We have already dealt with this topic in the previous article. Refer to each other - bad. This creates problems with scalability, with the removal of objects, with attempts to stop memory leaks, etc. In addition, when nested objects are completely impossible to convert them to a string via JSON.stringify. Therefore, the character’s things are only IDs of things, passengers inside vehicles are only passenger IDs, etc. Convert everything to a string for save / load functions. But about them another time.

The registry of objects is like FMS, only virtual. You can refer to it like this:

 StalinGrad.registry.get("id_"); 



Through it, all third-party modules work that do not have the right to keep a link to the instance in themselves, because this can create unexpected problems in the form of a memory leak, etc.

How does the controller module work


In almost any game we make, we have a main character, which is controlled by a player. Management in all games is standard, so time after time we have to solve the same task - to tie the joystick to the keyboard. To get rid of this, the code is moved to the module, and the module is used in 99% of cases, therefore it is included in the engine core. As for the control itself, it is: AWSD or arrows, E - to use, Enter - to shoot, e - to switch the mode (for example, the mode of a gravitational gun). In addition, a bunch of duplicate buttons is attached so that the player can immediately take over and begin to control, regardless of where he points with his finger. Misunderstanding of management was a frequent problem of old games on JavaScript (and new ones too). You sit, watch the demo, poke all the buttons on the keyboard, and the character stands still. And the game seems to be good, but it's impossible to play. What did I follow when choosing buttons? Here is the situation:



You can try, it's funny. And, of course, there are obvious bugs that need to be resolved. For example:

“We push to the left, then we clamp upward. Let go up (the button to the left is still clamped) and ... everything, the character no longer runs to the left "



The same problem with the combination left + right - right. Many, of course, will advise to better deal with keypress, but no, it does not work in JavaScript or it does not work the way it feels good.

The way out of the situation was suggested by the algorithm of the old snake, in which key sticking was used: by pressing a key, information about the direction was simply recorded in a variable followed by a timer. In fact, an intermediary appeared between the game and the keyboard in the form of a variable that remembers the last direction. And what to do in our case? And if we want two players to play a game at the same time? And what if it is necessary that one person could manage at once a whole army of bots? Then we start thinking ...



At the entrance we have a joystick with configs (or without them, then it will receive default configs). Create a keyboard map and mark the buttons, which will follow. Next, create a stack of events and an array of joysticks. If any button was clamped, the module looks at who signed it, and puts the event information in the right stacks. Parallel to this, by timer, checking stacks is triggered. A special function bypasses all stacks, looks at what the latest events are recorded in them. Next, for each stack, an array of joysticks is moved (which are listed as this stack). And for each joystick, the event belonging to it is distributed. If a player releases the keyboard button, the module removes it from all the stacks that subscribed to it. So, when the timer comes to the stacks, it will take the previous event, which worked on the last button. Try to hold several buttons at the same time in the demo.

On the one hand, the scheme is complex, and on the other, it gives excellent prospects for scalability. We can set configs and list the list of buttons we want to subscribe to. So you can make a game for two or more players.

Some demos:




Registry of substances


Examples of substances in GTA, Stalker and Fallout showed that the game with them can become more interesting. There was a task to add substances to the engine. Initially, everything was simple - you use the drug and the camera adds an extra class to the display. Trip is created by means of CSS3 (rotation, transformation, etc.). But the scheme was completely inoperative in older browsers.





Then it was decided to leave CSS3 and write effects using other methods and means. I divided the effects of all the trips into two types: filter overlay on the display and control reverse. To put a filter on the image, created an additional div (transparent screen of the trip) with a z-index higher than all other objects. The trip screen takes a class with background-image, and there is a transparent png picture with effects like an instagram ʻa. Then, the transparency screen at the trip screen changes from 0 to 1, depending on whether the character is tripped or not. In addition, this screen has another function - the function of the protective screen for devices with a touchscreen. If the owner of the touchscreen stubbornly clicks on the picture, he may have a suggestion from the system to save the picture. If there is a transparent div on top of this image, there will be no messages.



The task with the reverse control was not so obvious. In addition, for each trip, it was necessary to create a timer, then to turn off the action of the trip and make the screen of the trips again transparent. When scaling (creating heaps of worlds, characters, and simultaneous trips), such a scheme turned out to be completely ineffective. Not only was a bunch of timers created, so the connections between the character / camera / joystick objects were amplified, which was contrary to the logic of modularity. There was only one way out - to write an additional kernel module, which will resolve everything related to triples (we have our own Federal Drug Control Service with a timer and database).



Let us analyze the module circuit:

There are n-th number of worlds with m characters in them. If someone takes a substance somewhere, then information about it is sent to the registry of substances. There she is recorded in the database, and all the cameras that were watching the character are sent a command to add a trip. There is only one timer in the registry, which once per second bypasses all entries and minus the time spent in the registry. If the record time is zero, it is removed from the registry, and all cameras that were associated with this record are sent a message saying that you need to disable the trip. In addition, the register of substances can report whether a character is listed in it or not, and if it is, then in what condition it is at the moment. Joystick objects, for example, use this API to find out information about their client. If the joystick sees that the client is strongly undershot and undershot, it turns on the reverse control or imposes other effects.

How to hide a corpse


With how to kill a character everything seems to be clear, but what to do with his corpse is completely unclear. The first thing that comes to mind is to immediately remove the carcass from the world. But this is not scalable logic. And what if we make a game about zombies and around some corpses? Or do we have an immortal, sort of Duncan MacLeod, who cannot die? Or we are writing a game about Chuck Norris, and Chuck Norris should not die, even if his life is zero. Therefore, you can not remove the carcass. On the other hand, they usually remove it and make it beautiful.



But we cannot know in advance what kind of death animation the developer wants to implement.

Then, perhaps, the output will be just to leave everything as it is. Suppose that each developer writes a cycle that timer will check the lives of the characters. If the character has smoked, the developer himself will be able to come up with an action with the corpse. But with such a logic of reasoning, it turns out that all the developers will again and again write the same search function for the blind men of the sky. Then you can include it in the engine and get rid of code duplication.

Ladies and Gentlemen, we meet - "function of death":

 world.dead(); 

The function is called every time when someone suddenly "glues flippers" in our instance of the world. As an argument, the function expects a callback function to be called. The object that “played in the box” will be passed to the callback function. For example:

 world.dead(function(man) { alert("  ID " + man.id + "  "); }); 

Just in case, I’ll clarify that the world is a world in which we will follow death.

But we got a little distracted. The question “How to hide a corpse?” Is not yet resolved and there are several options.

Classic option

The character who gave the oak flies up. A good option, but if our level consists of concrete floors (for example, the main character runs through the building), the carcass will not be able to pass through the ceiling. Let me remind you that no one has yet canceled the calculation of collisions and physics, and for the corpse all forces continue to be considered. Then you can change the type of object to "scenery." The scenery in our nat. calculations are not involved and can be arranged as you like. But you must first remove the object from the world, because it is listed in objects that can move. And then add it to the world again, already as a decoration. There is also an option with a flashing object.

Flashing option

If someone is killed, he stops, flashes and disappears altogether. For this effect, we must take an array of sprites and replace each even element with a transparent picture. Well, of course, a timer is added on top of everything, which at the end of the animation removes the dead character from the world. The old game "Worms" in a hurry to offer us a third option.

Tombstone version

We memorize the coordinates of the corpse, remove it from the world, and in its place create the object "gravestone".

, . , , . , :

 StalinGrad.module.controller.remove(); 

, . For example:

 StalinGrad.module.controller.remove(man); 





Questions and answers


, .

guyfawkes :

«— ?»

Nowhere. open source HTML5 github`. — , , , « ». — , — , — . , — .



Since , , , - , :

 StalinGrad.brain = function() { } 

:

 StalinGrad.module.YouModule = function() { } 

StalinGrad — , . — - ( ). - . canvas` WebGL` ;)

s1im :

«— src , , background - div- background-position»

, ( , s1im , ).

, :

  left: [ "partizan_left_1.png", "partizan_left_2.png", "partizan_left_3.png", "partizan_left_4.png" ], right: [ "partizan_right_1.png", "partizan_right_2.png", "partizan_right_3.png", "partizan_right_4.png" ] 

, . . . , :

  image: "partizan_left_1.png" left: [ [ 0, 40 ], [ 40, 80 ], [ 80, 120 ], [ 120, 160 ] ], right: [ [ 0, 40 ], [ 40, 80 ], [ 80, 120 ], [ 120, 160 ] ] 




, ( ). . :

  left: [ [ 0, 34 ], [ 34, 56 ], [ 56, 89 ], [ 89, 112 ] ], 

, .



background-size, background-size . « », , , . . CSS :





, 404` . , , s1im ( ). , . , , , . .

, , , - jQuery, JavaScript - ? ?

VoVanJinnI wanted to find out, “how to make a cat to crap with a rainbow”.

Answer: “- Nothing”. I think this is a task for a one-time game that is solved with crutches, since to be like a rainbow to objects is not natural. Since , , . , , . , . , — . — ( , ).

«— Canvas !?»

SerafimArts jetman , LG Smart TV 2012 FPS Canvas`. : LG Smart TV 2012 (Netcast 3.0, LM640T): 0-1 FPS, .

At last


, . , CSS . , :
1-
2-
3-

( , ).

. . , — , . – .

: , , , . , , - , API . — (, , - ).

— , , . — . , Y ( , , — ). — . ( , .. , )

.

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


All Articles