📜 ⬆️ ⬇️

The story of the creation of a classic RTS at home from scratch + analysis of the main stages of development (AI, network, etc.)

image

The article will discuss one very new project that was created at a completely different time and in completely different conditions. This is my old RTS called Onimod land. To make it immediately clear what it is, you can watch a short video:


This article has a sequel .



It would probably be correct to immediately clarify the following: This game was once programmed by me personally from scratch without engines, designers and other modern tools. The project is quite large and was done on pure enthusiasm for several years. Contrary to all the laws of economics, I managed to bring this work to an end, though by the time the number of the development team had already decreased to one person. Unfortunately, at the time of its release, this game could not for many reasons be in demand by the broad masses of players - the games market changed first towards 3D, then towards casual games. For a long time, this game lay with me, so to speak, "on the shelf", and I myself have long ceased to be a game developer.
')
Nevertheless, it is quite interesting for me to recall how all this was done. Especially since I personally play my own game with great pleasure. I’m not at all sure that the well-known Blizzard made its Starcraft in the same way as I did, but I remember that in the course of development I occasionally ran into the same problems that the Blizzard developers had come across in their time. This could be understood by the decisions that they and I had to use to get out of the same “situation”. My version of how I did all this, I bring to your attention. In the end, I will briefly describe the conditions themselves in which the development took place, as well as the “rules of the game” that were set by Russian publishers at that time.


The article attempts to describe the general structure of the classic RTS. Naturally, the description will not be particularly detailed, since there is no way to describe in detail such amount of code (and, frankly, even for the author to keep in mind subtleties of implementation is unrealistic, especially considering how many years have passed). However, I would like to hope that even a superficial idea of ​​how all this is done from scratch, I will be able to illuminate. In some places I will insert small pieces of the source code, but I will do it, rather, to demonstrate the principle than for anything more.

The following mechanisms will be considered, which, in my opinion, are fundamental:



Rts


As a game genre, the RTS with the type of 2D graphic in isometric was unambiguously chosen. This choice was justified solely by our personal preferences. I chose C ++ as my main programming language, which after a long programming process on an assembler on a Z80-type processor (ZX-Spectrum) seemed to me very simple, because in its essence it strongly resembled a very high-level assembler. Although I have to admit that for a long time I could not get used to using virtual functions, and without this, C ++ is very similar to ordinary C.

In my opinion, if you make a game from scratch, the first thing to do is to get at least some image on the screen. This means, of course, that the image should be meaningful, i.e. their appearance test game mechanisms. The main point here is the way the graphics get into the game and its subsequent rendering. From these two moments you need to start everything.

Graphics


At that moment, the vast majority of games used 8-bit color resolution, and we logically decided that we would go further and our game would use 16-bit colors. Why not 24-bit? At that time, 1 MB of memory was present on the video cards as the norm. The amount of memory that is required for one screen surface of 800x600, respectively, will be 800 x 600 x 2 = 960,000 bytes or 937.5 KB. Those. under one screen surface, almost all of the video memory of the video cards of that time was used. Initially, our game should have been able to work in the 640x480 screen mode, but later this question was revised at the moment the game works with any resolution that the monitor allows.

Graphics storage requires memory. It is clear that it will not work in my case to store something on a video card, and this is not possible given the requirements. Therefore, the sprites should be in RAM.

The following requirements were made to the sprite itself:


image


image


image

The picture shows the size of sprites in the “cells” and the position of the sprite relative to the cells, i.e. pivot.

As a result, on the sprites, I made the following decision: to store the graphics in memory in compressed form and decode it at the moment of output to the screen. The main advantage of this option is the small amount of memory that is required by the sprite, the main disadvantage, of course, will be the constant decoding by the processor of each sprite at the time of drawing. What is required for this? You need a compression format and an algorithm that will compress and decompress the image.

For compression, the following method was chosen. The algorithm ran along the rectangular area of ​​the sprite vertically, starting from the upper left corner. Vertical inseparable stripes of pixels served as the basis for drawing (the vertical was chosen from the simple consideration that units basically have a larger size in height). Each such strip was remembered as an array, about which the number of pixels and the colors of these pixels are known. When the strip was interrupted by an “empty” pixel, the next vertical strip was searched for. Between the end of one strip and the beginning of the next, the offset of coordinates was preserved, which made it possible to avoid storing and, most importantly, drawing “empty” pixels. To all this, tricks with palettes were added that could switch on the go. In a simplified form, the compression format consisted of the following commands:


All this is not bad, but the question arises, what about the color that identifies the team, i.e. constantly changing? The solution in this case is quite simple. This identification color can always be coded by the 0th index in the palette. Then the zero color in the palette will be, as it were, reserved for variability. But before drawing, the sprite is sufficient in the used palettes to replace the very first color with the color of the command, and the issue is resolved.

As an additional optimization, this technique was used. Many buildings contained an animation that took up much more space than a static sprite of a building would take. But since the building itself was always displayed as a static sprite, and the animation was superimposed on top of the sprite, then from the animation you could throw away those pixels that coincided in color with the color of the static sprite of the building. Usually this would remove most of the pixels from the animation sprite.

For example, in the game, Farm looked like this:

image

In reality, the graphics were stored as follows:

image

Now it is necessary to solve the problem with decoding the compressed image. And this task is much more difficult than the compression itself. The principal difference is that the image can be compressed in advance, i.e. not caring about the speed of this process. Decoding is to be done on the fly, all the time for each sprite displayed on the screen. And it was precisely in this moment that the main difficulty lay. The second difficulty arising from the first was that sprites must be decoded in different ways. For example, imagine that some unit turned on the “masking” mode, which visually looks like it starts to quickly become transparent almost to complete dissolution. Or, for example, flying units should have a shadow on the surface of the landscape, with the shadow actually being a separate sprite so that the flying unit could change its height while the shadow remained on the ground. The shadow is always black and always has transparency, which means it can be decoded using our own algorithm.

image

image

Or one more example aimed at optimization - if it is known in advance that the sprite uses only one palette, then it is enough to set it once and, when decoding, not to check for a change in the palette at all. In general, there are plenty of options for improving the decoding algorithm for particular cases. There was even such an exotic version of the sprite decoding, which checked for hit in the visible area of ​​these coordinates, which was required to check for an exact mouse hit.

In addition, all decoding options were duplicated for the following cases:


For decoding, it was decided to use assembler, since this algorithm was the bottleneck in the entire program. The assembler allows the programmer to effectively use the registers of the processor, rather than placing variables on the stack, as the compiler usually does. As a result, several dozen functions were written on the assembler, which, in fact, did the same decoding, but with different nuances. The required function was called from C ++, receiving as arguments information about the compressed sprite and the intricacies of its visualization.

As an assembler, tasm32.exe was used. The command line for assembling an assembler looked like this:

tasm32 / l / ml / zi modul_s.asm , where modul_s.asm is the source code in assembler. The result was an object file modul_s.obj , which could already be linked to a C ++ project.

The function call itself from C ++ looked like this:

extern "C" bool AsmSprite (parameters) ;

Resources


The question of storing and editing resources is perhaps the most important question that determines everything else in the future. Therefore, in my opinion, the creation of any not the smallest game should begin with the creation of a resource editor. Ideally, you should have a visual editor that allows you to make almost any changes to the game without the help of a programmer. Those. when creating a game, you need to play the game, oddly enough, last but not least, and your first assault should be aimed at creating such an editor. The resource editor should not be confused with the map editor - they have completely different purposes. The map editor is a task; on the contrary, it is already “under a curtain”, since in order to draw maps you practically need the game itself to function.

So back to the resources ...

In my case, the game should handle the following types of resources:


With sprites, everything was more or less clear. They need to be loaded, compressed and stored in large portions in a separate file, which the game can then open by itself. Since the sprite has a name, in some cases it is quite possible to identify by this name the belonging of the sprite to non-living objects.

image

image

As I said earlier, at the initial stage it is very important to get an image, so you can start the resource editor with the ability to load and compress sprites.

Next, it was necessary to think about how to teach the resource editor to create Living Objects. To do this, it is not enough just to have a set of pictures, it is necessary to combine these pictures into animations and explain to the game what exactly this or that animation is used for.

Let's see what the usual actions can perform a combat unit in the RTS. Obviously, the unit must be able to be in a state of "doing nothing" - let's call it Waiting. Next, the unit must be able to move - let's call this action Move. A unit must be able to fight - let's call this action Impact (in general, the impact may be positive, for example, Healing). And, finally, a unit that was unlucky with the commander must have the action Death. These actions are a basic set of a true warrior.

In fact, the situation with the Impact was much more complicated, as there were additional stages. A complete Impact cycle could look like this:


In addition, this whole scheme is greatly complicated by the fact that we do not have 3D, and therefore it is impossible to simply turn the model to show it from different sides. Therefore, the animation must also have a Direction (up, down, right, left, etc.). These directions, of course, must be fixed, and we must take into account that each Direction, in practice, requires duplication of all unit sprites. Based on this, 8 directions of movement were chosen for the units, and in reality they were reduced to 5, since only the “up” and down ”directions do not mirror in opposite directions.

image

For units that were too abruptly changing their direction, an action was added: Turn, for example, the Tank turned in place to change the direction of movement.

Properties possessed by the animation:

  1. Action - shows the game capabilities of the object (Standby, Move, Impact, etc.).
  2. The direction of movement (5 pieces) is required so that the object can be visually displayed in different directions. Usually, for each direction of movement, you will need to duplicate the animation with all its properties, except, in fact, sprites.
  3. Completion of animation:

    • Ending - the animation ends (if there is nothing more to do, then the animation usually starts on Waiting)
    • Looping - animation starts from the beginning (useful for Buildings where animation can be played indefinitely Waiting)
    • Transition - launching another animation, additionally you need to select an animation (allows you to combine animations with one another, you can use random animation from several options).
    • New object - at the moment of termination the current object turns into a new one, which must be specified (useful thing for the case when, for example, the Shaman revives the Statue, ie, it is required to turn the building into a unit).
    • Death - the current object is destroyed in the memory of the game.
  4. Interruption of the animation is a sign that the animation can be interrupted by the user's actions (poked somewhere with the mouse). Additionally, you can specify the animation that will be executed if the user tried to interrupt the current animation. (For example, a Soldier in a state of rest holds the machine gun in front of him, and when he walks, the machine gun behind him - instantaneous switching between these states would not look very good, therefore there is an intermediate animation that visually removes the machine gun behind his back, and it will work before the Soldier budges).
  5. Speed ​​- allows you to specify the speed of the animation in percent.
  6. Icon - an image for the control panel where the user clicks to perform this action.
  7. Cost - some actions are not free, for example, for a magician to conjure, he needs energy.
  8. Frames - animation consists of sprites or frames. This is the most important property of animation. Most often, the frame only displays the corresponding sprite, but there is also a lot of additional functionality:

    • Pause - determines the delay of the animation on this frame. It is possible to use random delays. Pauses well help to portray the unhurried decomposition of a killed warrior, since usually the corpse is present on the map for a very long time after his death.
    • Sound - you can play any sound file. This property is well proven in the animations of firing from a machine gun or pecking an enemy with a sword.
    • A function is a specific action that is performed on a given animation frame. It was possible to simultaneously set several functions on one frame (for example, launching rockets from two barrels at once). We list the main functions (although they are a bit more):

      • Impact - this is the very moment when the enemy gets a hatchet on the head. This, of course, is the simplest use of the Impact function; in fact, additional parameters allow you to specify the type of this impact. And here in the list there is not only a Physical blow, but also Treatment, and Stealing magic, and a whole lot more. Also, the type of impact includes such ordinary affairs as Resource Extraction (the Worker knocks the Resource Extras function on the tree with a hatchet and fills him with wood from the tree), and also here is a banal Repair of a Broken Building when the Employee returns the Building life. Any Impact is always directed at the current target on which the unit performs the action.
      • Explosion - according to the coordinates of the current object, an explosion is carried out at which the force and radius of the shock wave are adjusted. For an explosion, you can also choose the type of impact, but usually it is just a Physical blow. It is logical to assume that the Explosion function is usually used not by Living Units, but by Projectiles that explode when reaching the target.
      • Object origin is a function for creating another object. For example, an Archer using this function should create an Arrow on a given animation frame. Additionally, for the Arrow, you will need to specify the coordinates by which it will be created, that is, in practice, you will still have to drag the Arrow sprite across the screen to correctly position it relative to the Archer. Naturally, to position the object being created relative to the creator is necessary for all directions of movement, i.e. in practice, this operation was performed 5 times. In each case, an Arrow sprite was selected that would be more appropriate for the current direction of the unit. The arrow in the game began its existence with this sprite, and then it could turn on its own, depending on the direction to the target.

In fact, the possibilities, of course, were much more, but there is not much point in enumerating everything - the main thing, perhaps, principle.

Naturally, units must have characteristics, such as strength, protection, amount of life, movement speed. For them, the building in which they will be produced, production speed, cost, etc. must be defined. There are units that can carry other units, i.e. Transports. There is an option when units are not just transported by Transport, but also shoot from it. Some units can fly, respectively, for them it is necessary to determine the altitude "from" and "to", within these limits the flying unit can change its height randomly. Some Buildings can act by analogy with transports, i.e. units can get inside, for example, Archers can be placed in the Defense Tower to shoot from it.

Separate should briefly mention the objects Shells. The projectile can hit ground or aerial targets, be navigable, i.e. , .. . . , 8 , . , . , . - , , .

, , , . . , . , . , , , , . . , , 3:


, , . :


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

— . , , , 3- , . .


, AI. . , , , , SHIFT . , . , COM_WAIT , .

. AI . : «» . , COM_DESTROY . , COM_DESTROY , , , .

, . — COM_GO . . , COM_GO, . , , . , , .. , . , , , . , , , , , .. , , , .

. :


, , . Those. — — . . , .

, . , N . , , . , .

, , :


. .

. . , .

, . . , «», , , .

« ». .

, .

, . ? « », , . COM_EXTRACT, .

, , , . , ( ) , « » . . , . , , ( ). (distance = ). , , (distance = 1). — . , distance. , , .. . ( ). , , .

COM_EXTRACT 4- :


The above example was intended to demonstrate the mechanisms underlying game mechanics literally "in a nutshell." I wanted the information that I gave about the capabilities of the resource editor to be somehow related to the specific actions of the units. Please note that the COM_EXTRACT command for both the Worker and the ITR is completely identical in terms of code. But the settings for unit animations in the resource editor make the resource extraction process completely different. That is why it is important to have ample opportunity to customize animations.

Under each action that can be performed must exist its own team. In this case, for example, the commands COM_GO (go to the specified point), COM_DESTROY (destroy a specific target), COM_ATTACK (go to the specified point, destroying everyone in your path), COM_RUN (run away from the attacking enemy, since there is no possibility to defend) and COM_LEAVE (move away from the place where the building will be built) are, in fact, different. In my case, in the code I observe about 50 teams, which ensure the functioning of the entire game.

Also note that there is even such a nice command as COM_CANCEL, which simply cancels all other commands. Why is it needed? The fact is that at the end of the article I will briefly describe the network, and the network should be able to transfer any actions that the player performs, since these actions should be repeated one-on-one on another computer.

Finding the way


Units must be able to navigate the map and find an effective route in difficult cases. This issue should be given sufficient attention and time. At the initial stage, I would recommend to make any simplest solution, if only you can test the units created by artists. The real path finding algorithm will be needed at the moment of creating the AI, when the computer should be able to build a path in any of the most florid situations. Without a normal pathfinding algorithm, AI cannot act effectively, except for some exceptional cases where the playing field is the “bare steppe”.

So, we have more or less debugged game mechanics, units are able to perform the prescribed actions that they receive with the mouse, and the program does not fall out every 2 minutes. The next stage is the creation of an effective path finding algorithm. At the moment, there are quite a few tips on this topic on the Internet; I cannot say how good or bad these methods are, since I once invented my own and, in my opinion, a very effective solution to this. It is possible that it roughly repeats some kind of subsequently published algorithm, since the solution is really quite elegant. But, as I said, all the solutions that I show in this article at that time I generated with my head on my own.

Detailed article on my method of finding the path in the classic RTS is available here . In addition, in fact, the search algorithm itself, there is described the process of managing this algorithm. This section deals with such problems as the collision of units with each other while moving, following another unit (the target is constantly changing its position) and other not so obvious moments. The article was written 10 years ago and for me personally it was intended to “not forget” about my own decision. I don’t see much sense in duplicating this information here, especially considering that this information is already quite a lot.

AI (aka Artificial Intelligence)


Let's first deal with what is AI. Personally, I define AI for myself as an algorithm that replaces the actions of a live player that it performs with the mouse and keyboard. Now pay attention to what ... if the units in your game are not completely stupid, then they can independently make some decisions and carry them out without the help of the player. These solutions in my case include the following actions:


As can be seen from the above examples, all these actions do not require player intervention. Accordingly, my own formulation that “AI is an algorithm that replaces the actions of a live player that it performs with the mouse and keyboard” does not correspond to these examples. So, all these examples have no relation to AI. For myself, I once gave the name “Instincts” to these actions. Unit instincts are the most important part of the game, since they take care of routine actions, which are often impossible to keep track of, from the player. And the overall comfort of the game depends on the quality of the available Instincts. Instinct has a specific unit, AI controls groups of units and performs global tasks such as “Establish a new base for resource extraction”.

The reasons for the manifestation of instincts in the game a person often sees with the exact opposite. For example, when an employee runs to repair a building, it seems that this is an initiative of the employee himself. In fact, the burning Building “calls for help” in a small radius from itself of all the units that have in their arsenal Impacts like Repair. The same applies to the situation where nearby soldiers come to the warrior to help - this is a response to the call “Help-and-and-those! Killing-aaaa! ”

Despite the fact that, in my opinion, Instincts have nothing to do with AI, I put a small description in the section with AI, since for many such an arrangement would seem quite expected. But, in my opinion, Instincts belong to the general game mechanics, i.e. according to the mind, they should have been described a little higher in the text. And the game mechanic at the time of writing AI should already fully function including the section on Instincts of units. Instincts should work in the same way for the team that the live player controls and for the team that the AI ​​controls. In their decisions, AI should not descend to the level of Instincts, its tasks should sound something like this: “form a detachment and send it to the attack at such and such coordinates”, and the fact that this detachment will already crush everyone in its path is a task Instincts.

So back directly to AI. In my case, for each command that the computer manages, the AI ​​handler is called approximately 1 time per second. AI is guided in its actions by the following global principles:


To be precise, the intellect can perform 15 types of tasks. The most interesting thing is of course the tasks that relate to the attack. Whole intelligence can attack in 6 different ways:


Genre: RTS - real time strategy
Programming: Alexey Sedov (aka Odin_KG)
- Programming Technologies: C ++, Assembler, DirectDraw
- System requirements: Windows XP, Windows 7 (the network in 7-ke does not work, but otherwise everything is in order)
Drawing: Roman Kovalenko, Konstantin Ivanov
- Graphics style: 2D isometric (approximately 12 thousand sprites are used)
Music: Dmitry Golov
Way of development: Enthusiasm
Development time: 1998 - 2005
Download page: Land of onymodes ~ 53 MB .
Mode of distribution: free

Starting conditions and their consequences (lyrical section)


So, in the yard in 1998. Let's take a quick look at the surrounding reality of the then IT industry. Intel released the processor, already at 233 MHz, Blizzard is already known for its “Diablo 1” and “Warcraft 2”, and Microsoft distinguished itself with the help of “Age of Empires 1”. Almost all PCs have “Windows 95” installed, which some are already trying to upgrade to “Windows 98”. On the flea markets of the whole immense homeland, pirated discs, which are very popular among the people because of their low cost, diverge in tons. Licensed software can exist somewhere (for example, in Moscow), but, at a minimum, people do not even understand the difference between a pirate and a license. — , 6- 30- , , , «» . …

So…

3 , , . 1 , , - ( ) 2 , , .

, . , : Intel 200-MMX, — 32 , — 2 , HDD — 4. , 9000 ( 700 ), 1500$. .

- , — + « ». , , — , . .

, , . . , . , , , , , Dual-Up 5 /, .

, , , , 3D Studio Max, , . . , «», . Those. , .

. , , . 3D Studio Max, Character Studio. , - - . , 3D Max , Windows 95. 3D Max , 48 , 16. , . . 3D Max .

, « ». , , «» . , «» . - .

, , . , . , . AI, , , , . , - . … -, , . , , , , , . , , , — - . , , - , , - , .

, «» , - , , , . , , , . , .

— . «» , , . , , . . , : (), . - , :

) .
) , , .

. , .

, , :

. , , 10 000 $ + . ( ) 25%, 15% 20%. , , , , . , , , , , . , , - . , , , , . … , , , - , , .

, 10 000 $. , , , , AppStore . Those. , - , . - , , , , .. — , « » ( ) — . - , - . «» , e-mail — 11 000 . «» , - , Blizzard- 10 000 $. , , , , « » « », «» «- ». «» , , , , , .

. , Fargus-, . , .

, , , ( , ). , - 5000 $. Those. 2 , . , — , , , . , , .

?

. ? , , , , , . , , — - «» «» . , .. , , . , , « ». . - — , . , -, , .

, — 15 . , , , , . -, - , , , , . - , «» .

, , , — . , « » . , .

… - . , , - , , , . , - . , , « », - . «Magic Particles». , , API, . , . .

Magic Particles, , , , , , .


15.04.2016: ...

!
Greenlight . ( , ), … , , , « IT». , , , , « ». !

Respectfully,




, , , « ».


.

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


All Articles