Hi Habra!
This article continues the series of articles about the game engine StalinGrad. In the last article we
raped DHTML , and in this one we will rape architecture and prototypes.
There will be practically no code, so if you are not a web developer, you can simply read about architecture and OOP. And yes - about HTML5 there will be nothing again, only DHTML-hardcore :)
')
Immediately short demo:Example 1 - Parallel Worlds
Example 2 - one world with different cameras
Example 3 - bots
Example 4 - Nyangat, clouds and portal cannon
Work logic
Somehow I wanted to write a quest where a man would walk around the bar and talk to different people. And the events and people in this bar were randomly generated, and each time a new goal appeared in front of the player.
How to make this walk on javascript? In most cases, it looks like this: there is a screen and a character that goes from one end of the screen to the other.

Implementing is pretty simple. But what if the bar is bigger than the screen? Then you can come up with such a scheme: there is a screen - this is DIV1 with positioning. Inside this lies another DIV2, which is larger than DIV1 and is positioned relative to the parent. DIV2 - this is our level. All people and objects in the bar are inside DIV2 and positioned from its left edge.
Implementing the scheme in principle is simple, but not necessary. This is an absolutely wrong approach. What if we have a thousand objects in the bar? We get a non-scalable foundation, which over time can make us suffer.
I began to look for information about how to make 2D games and went through a bunch of articles on Habré about games on HTML5. Unfortunately, in all found articles that I read, there was not a single algorithm or reference to the architecture - only scripts for one-off games. This fact saddened me, and I asked the guys at work about what they thought. My colleague, Vitaly Nikitin, advised to read the book "The secrets of game development in macromedia Flash MX" (by Jobe Makar). Since this all started.

The book was fantastic. If you miss all the moments about Flash, you can learn a lot about algorithms and logic. At least, for me, as a person who knows nothing about the engines and experience of game developers, the book was just a revelation.
With the knowledge gained, the idea of ​​games expanded and there was a keen desire to write a game with a developed world. At first I looked at the JavaScript game engines that were already on the market. Drew attention to the fact that many lacked normal documentation. Hardcore options - also not found. But following the logic of Job Makar and igrostroy experience, the level of hardcore can be made transcendent.
What did I want? Mario, where you can kill / rape / rob caravans. What should have been done:
- Add weather
- The ability to teleport
- Gantry cannon
- Gravity cannon
- In general, many different guns and cold weapons
- The ability to ride a transport
- So that transport has limited fuel and it can be refilled
- The weather
- Day and night
- Narco and Alko Tripy
- To be able to build in Minecraft
- Karma for each player
- and many other fun and funny things.
And what is characteristic is that this entire list has already been implemented, and at the moment there is a desire to add many more chips and implement them.
At some point, I realized that I could not write a game. This thing is getting bigger and bigger, but I don’t see a logical ending for it. Then it was decided to describe everything, put it on the Internet and see who is interested. Starting to write the documentation - I realized that a lot of things do not work as they should. And the API can and should be simplified. But let's get back to the point and the main ideas.
Any game consists of three objects.The first object is the world in which the action takes place. We can place and delete objects from the world. The world, in turn, monitors all the clashes, forces and events. It can also have an additional effect on objects (for example, to specify gravity).
The second object is a character or characters that are in this very world. And here it is not at all necessary that this should be a living object. It can be a box, a barrel, a wall, a bullet, a stone, or something else.
Third, the camera. The camera is needed to look at the world, because The previous two objects are exclusively mat. abstraction and does not make themselves felt.

What do these three objects give us? The ability to create a huge number of
parallel worlds , look at one world
from different places , shove
a lot of characters into one world.

What problems does the creation of different worlds solve? We can put the code on the server and finish the game mode over the network. In fact, the world is an array and a couple of methods that serve it. It does not work with the DOM, which means it doesn’t care where it exists. In addition, by generating worlds, we can create games with parallel worlds that exist simultaneously.
Having different worlds and karma, after resetting lives, you can throw characters from the main world, into the world of “Paradise” or the world of “Hell” and create a non-linear plot. In addition, because worlds will exist in parallel, it is possible scenarios in which the characters of one world teleport to another world and start a war there.
The architecture described above is pure OOP. Therefore, the engine core is the prototype on the prototype, ubiquitous encapsulation and modularity. If you start thinking in this format, the architecture of games seems to be quite the opposite, and a lot of additional questions arise.
ExtensibilityThere is a guy picks up a gun and sits in the car. Another guy shoots him from a bazooka. Who will die from what? How are they going to die?
- The rocket flies to the car, a collision occurs. Recursion is going through the nested objects (the search of passengers and things of these passengers) and all the objects inside the car are destroyed. Then the machine itself is destroyed. This scheme first comes to mind.
- The rocket flies to the car, a collision occurs. A heat / damage zone is created. All objects are damaged. If the object is stronger than the damage caused to it - it remains in the world. This scheme seems more logical, and there are more opportunities for scaling (for example, you can expand the damage zone and get a blast wave).
- Scheme two, only for embedded objects, the degree of nesting is taken into account. So, the more nested the object is, the more protected it is.
- Scheme two, only takes into account the additional damage from the fuel in the car. It turns out the damage from the rocket + damage from the explosion of fuel in the car. Such a scheme is more realistic.
What is the weather?The weather is what spins in the background. Does it affect the player? Can we say that the zone with the wind is a zone with gravity that pulls not down, but to the side? Should we set the property of sail to different objects and multiply the force of the wind on them? How to stop the wind with obstacles in its path (for example, a wall), if it is gravity - then the wall obviously will not stop it?
The correct answer to each of these questions can give a very big benefit in perspective. The solution should be as simple and universal as possible, and then on its basis it will be possible to implement several more solutions or add additional functionality that you haven’t even thought about before.
For example, the weapon is divided into types and functionality. Using ready-made methods and functions, on the basis of the construction mode, the function of firing cartridges and shells was added. In fact, we build a projectile instead of a brick in front of the character and set the initial velocity to the projectile.

Based on the fact that we can set the size of the camera (not the size of the screen on which the image is displayed, namely the display area of ​​the world), the zoom function was very easily implemented. And the link for the timer to a separate variable mdash; made it possible for the camera to add a
stop / start button to stop the rendering of the world.

Designing objects of the world
Any object that is added to the world must have mandatory standard properties. For example: coordinates, size, ID, class and type, drawing method. These are the so-called standard properties and absolutely all objects have them. Further, depending on the class and type of object, additional properties and methods are hung on it.
For example:
- The background gets nothing, because he doesn't need anything else. Nobody interacts with the background.
- The character receives physical parameters (mass, acceleration, forces, etc.), a backpack (empty array, where it is possible to add ID of things later), characteristics of a living object (brain type, life, rating, karma, etc.) and etc.
- The car receives almost the same set of properties and methods as the character, but there are also special characteristics of the vehicle (fuel stock, consumption, passenger capacity, etc.)
To create a huge number of objects with the same properties and prototypes - inside the engine there is a factory of objects. Each object, depending on the type and has a set of configs. Then there are two arrays - an array of properties and an array of prototypes (in fact, these are not arrays, but also objects, but this is already part of the OOP in JavaScript).

When we create an object, a factory (according to the configuration) inherits its property package and prototype package. Thus, we have something like a factory of factories. Which can collect any object, giving it any set of properties and prototypes.
Design weapons
It so happened that the weapon was divided into three types according to the type of shooting:
With the affected area.
Depending on the firing range, a defeat zone is created in front of the character. Next are all the objects that fell into the affected area. Of these, select the object that is closest to the character. When the victim is found - it is damaged, depending on the type of weapon.
A similar algorithm uses pickax. An example can be found
here.With the creation of the object of the projectile.
A projectile object is created in front of the character, which has the effect of gravity disabled The projectile flies and checks for collisions with objects. If he collides with a character, the projectile disappears and the character is damaged.
A similar algorithm uses a gun. An example can be found
here.With a check of free space.
This is a special type of shooting, necessary for the construction mode. When shooting, a block of material is created and an empty space is searched around the character, where you can put it.
A similar algorithm is used in construction. An example can be found
here.How to make a melee weaponEverything is simple - any swords, knives, picks work according to the first principle (with the affected area)
How to make quick and slow weaponsThe rapid-fire weapon works on the first principle and deals damage immediately. Slow weapons (missiles, etc.) - the second, and waiting for a projectile collision.
How to make a gravity gunShooting is done according to the first type, but instead of damage, additional acceleration is applied to all objects. Its direction depends on the shooting mode (either to yourself or from yourself).
An example of a gravitational gun can be found
here.
How to make a portal gunIn the same way as the construction of blocks - on the third principle. The only difference is that the gun keeps a link to the created portal in order to link it to the second created portal.
An example of a portal gun can be found
here.How to pump a portal gunSince it is possible for us to throw portals between two different worlds, it is possible to create a gun that would throw its victim to another world, or to the other end of the map. It turns out not quite a portal gun, but rather a teleport cannon
How the camera is implemented
What problems does the camera object solve? Since Canvas does not work everywhere (the prospect of pumping e-books with games still beckons me), I decided to make a camera that can draw the world using typesetting. Do you say addiction? Perhaps, but it works everywhere. At the moment, the camera can draw pictures and divs with the background. To transfer everything to canvas if you need is not a problem, because to do this, you just need to rewrite several rendering functions (in fact, here, for fun, you can even draw with text). My friend constantly told me that without canvas everything will hang, because shaders ... bla ... bla ... bla ... html5 ... etc. - but it turned out not. In the beginning, the camera of course hung, but the problem every time was not in it, but in the algorithms for calculating the world.

When I showed the scheme of work to one colleague at work - he said that the camera should immediately draw the whole world, and not constantly create and delete DOM elements. This is an absolutely wrong understanding. Suppose there are 40 objects in the world, which means we have 40 DOM elements to work with. Well, okay, this is not much and the browser will be able to display them without brakes. And if we have a thousand objects in the world? Following this logic, we have to create a thousand DOM elements and suffer from the brakes (although on the screen at the moment, there will still be only 30 pieces, because it will not fit any more).
The camera is an object of the world. It has dimensions and coordinates, and is constantly looking for the collision of other objects with itself. All who face it - fall into the array of visible objects and are created on the display. If the object is beyond the scope of the camera - its view is destroyed.
The world is rendered at the moment in two ways - with pictures and divs. For both methods, one display is suitable in the form of a parent div ʻa, relative to which the entire layout is positioned. In the future, you can add more rendering via canvas, but at the moment I don’t see any special need for this, because There are more interesting tasks.
Another interesting point. Since the whole drawing of the game mdash; is a layout, then any layout designer (even with little experience) will be able to easily fix the CSS file and replace the images, thereby giving the objects a completely different look. For example, you can create a
knitted world (for comparison, the
usual ).
How to choose between a picture and a div?Well, it depends on the situation.
If you have a big screen and some very long object, then you should draw it with a div with its definition (for example, a brick wall). It will look good and it will be just one DOM element (the smaller the DOM elements on the display, the slower the rendering is).
If you have a small screen or an object with animation, then you should draw it through img, since by changing the address of the picture, you can recreate the animation, and besides, the pictures look adequate when compressed.
All elements on the screen are set strictly as a percentage, so the screen is rubbery and stretches anywhere, in any kind. The ratio of the displayed area, screen proportions, etc. can be set when creating a camera object (see the documentation).
We must also understand that the camera is a separate object in itself, and it is in no way connected with any other character in the world. But each camera has a bind method that binds it to another object in the world (not necessarily to the character, you can also tie it to a brick). Such an object interface makes it easy to realize camera switching between different players (you have already seen an analogue in Counter-Strike, when, after death, you can watch the fight from the face of still-living players).
What benefits does the camera object itself give us?
Obviously, we can just get a full screen div with a fixed aspect ratio. It is not necessary to use the camera for the main purpose, this object can help us to restore old games (for example, to get a perfectly square board for backgammon, at any aspect ratio of the screen)
How to use the engine
This game engine can be used in two ways:
- Make a new game based on it
- Restore the old game using common modules
To create a new game, you need four main components:
- World. In the world there are all objects, and the plot develops. The world monitors the collisions and actions of objects. The world controls all physics.
- Camera. The camera shows you the world or watching the character. Thanks to the camera, we can know what is happening in the world.
- Character. The character is one of the objects that we place in the world. We will also add his surroundings, enemies, weapons, etc. to the world.
- Joystick. To control a character, we must attach a joystick to it. If you give the joystick to a computer, it will be able to control the character.

Since Since we get all the above components to the so-called "Factories", we can create them in unlimited quantities. For example, you can create three worlds that will not intersect in any way. Or five cameras aimed at one world, to look through the eyes of different characters.
But there are some limitations - for example, an object can be placed only in one world, otherwise its behavior will be inadequate (it will react to events in different worlds).
General scheme of the engine
Explanation of the scheme- Kernel - a file that contains generic functions (create / delete / replace a class from a DOM element, generate an ID, assign an event, etc.)
- Worlds Factory - creates worlds where everything happens
- Camera Factory - creates cameras that show us what is happening in the worlds
- Object Factory - creates characters / things / weapons / environments that are added to worlds. Also, the factory can restore objects, but more on that below.
- Object configurations - the object factory has so many settings that some of them are moved to a separate file. Why not made all the settings? Because if you break / change the basic settings for all objects, the objects may become invalid and the worlds will not add them to themselves.
- Then everything that is created in factories gets into a single registry. It is needed in order to remove links between objects. If someone needs someone - he asks the victim from the registry, indicating his ID. For example, characters store in bags only ID of things, and things themselves come from the registry as needed.
- Next comes a group of modules that work with specific factory objects. So for example for the worlds:
- Weather - handles weather settings
- Map processing - builds a map according to specified parameters. You can get a string of such parameters in the map editor.
- For objects in the worlds (things / characters / obstacles):
- Mission Manager - monitors the character and his missions. Upon successful completion of the mission can do any action.
- Rating - is the ranking of players in the world.
- Clans - responsible for all that is connected with the clans. It can add a clan to a player, issue a list of clans, etc.
- Bag Factory - creates an object that can work with character things.
- Joystick Factory - creates controllers for characters.
- Brain - controls the characters. If you throw a character in there, a joystick will be created for him, and depending on his brain type, the computer will control it.
There is also a load and save module. When saving, it converts an array of objects in the world to JSON, and when loaded, it restores all objects in the factory and updates the registry.
Since The article was written in a few days - there may be logical inconsistencies, so sorry, if something goes wrong. A little more can be found
here . In the next article there will be more practice, an analysis of the API, as well as resources needed for game development.
Also I recommend to get acquainted with the works of these comrades:BEM:
“File System Organization” and
“Creation History”Vadim Makeev's
speech on CSS frameworks
Andrey Korotkov's
channel about how normal men saw architecture
Speech by Misha Davydov in Chelyabinsk on how to take and scale
The book of Job Makar "The secrets of game development in macromedia Flash MX"
A series of articles by Misha Kadikov “Level Design. Theory and practice"
UPD: There is such a thing that the site with the demos began to blunt because of the habr effect and the animation may not load (the load limit is exceeded). So this is not a bug, this is a feature :) For the next article I will change the tariff plan.