Creating a strategy from scratch - a detailed guide. Smart or stupid client? Create a turn-based strategy

theme_park:
IWe will need:
building sprite
sprite menu
SPRITES OF VARIOUS BUTTONS SUCH AS:
srite with the inscription (construction, construction, build, etc.)
a window that appears
building drawing,
1) we’ll add the rest ourselves
2) the word counterfeit - created by myself, because we will need to fake it to match our source)
IILet's get started:
1) create everything that is written in point I except 1)
Let's create a global variable called money, set any initial amount of money
We will also create a mouse & keyboard object
Let's create a text, call it info, create an always event, and create an action in it:
select info in the action selection select set text in the text write this:
"money: " &(global("money".
2) add a menu, the main task of the menu is not to interfere, but to help the player navigate (how can it interfere? - it’s easy if you place it in the middle of the game); before making the menu, we’ll create a new layer, which we’ll call menu, in its proportions ( settings, options) in the display item we write:


we will add a sprite in it and take the image of the menu that was in the pre-production materials (point I) and place our menu in a secluded place where it will not interfere, but will be visible on the screen
Let's also place a button made of pre-finishing materials (point I) with the inscription BUILD (or something like that)
let's put it on the menu
Now go to the Event Sheet Editor
create an event (#blah blah blah# - this is my message (explanation) to you only instead of blah blah blah there will be my comment for you; >> - action; ll - division of windows for example:

mouse&keyboard ll on object clicked ll left clicked to object #your menu button with the inscription BUILD (or something like that)##the rest later (see point 3)#
3)now the hardest part(I broke this down into two points so it wouldn't be so complicated),
create a sprite from pre-finishing materials “a window that will appear”
then we create an empty sprite called p1, move the window off the screen, and put p1 in the place where your window should appear when you press the build button (or something like that CHVER)
great! Now go to the event sheet editor
Let's write down the unfinished event to the end:
Text ll set text ll bla-bla-bla)
mouse&keyboard ll on object clicked ll left clicked to object #your menu button labeled BUILD (or something like that)#>>
4)The second part of the hardest part:
let's create a sprite where the image of the building (pre-finishing materials) will be drawn, let's call it h1
let's create an empty sprite, call it p2, now place it in the place where the window should open,
Let's create a sprite, also a window (pre-finishing materials), in the window we will beautifully write the name of the building, its cost and description (optional) and call it i1
let's create another empty sprite called p3, place it next to p2, only so that it only touches p2 with the upper left corner
Now let's create several events, but first we make the previous event one new action:
mouse&keyboard ll on object clicked ll left clicked to object #the button of your menu with the inscription BUILD (or something like that)#>> sistem ll create object relative to object ll #your window# #layer number under the name menu# #X ;Y-don’t change# to object p1
>>sistem ll create object relative to object ll #your second window# #layer number under the name menu# #X;Y-do not change# to object p2
We also need to give him an event back:
copy the event and invert it
new event
mouse&keyboard ll is over object ll h1>>sistem ll creat object to relative to object ll i1 #layer number under the name menu# #X;Y-do not change# to object p3
Let's make a sprite with a building (use pre-production materials) and call it house
Let's create a window where our buildings will appear when they are selected in the menu, call it rlo
events:
mouse&keyboard ll on object clicked ll left clicked to h1>>sistem ll create to object relative to object ll house #layer number under the name menu# #X;Y-do not change# to object rlo
>> sistem ll subtract from value ll #amount of money that should be taken away during construction#
Now it was impossible to build an event
I’ll tell you my former method of prohibition (when I finish writing, I’ll explore another method that made me blue when I remembered the game theme park world)
events:
house ll on collision with another object ll to house
>>house ll destroy
>> sistem ll subtract from value ll - #double the amount of money that was taken away during construction##note you must put - quantity#
basically everything.
III what I want to say:

I was very upset by the collapse of my game. It was a strategy game, and this article was compiled according to its scheme. I ask you not to criticize too much, I wrote for a long time, if you find any speech errors, write and I will correct them
and also here is the source code for your viewing pleasure, look, in principle everything is the same as it is written here, only this was a demo version of the game. The main thing is not to change anything, otherwise it will be buggy!
use it, experiment, check it, do what you want, it’s all right for me

KaMiKaZa:
All “system” expressions must be included in the “Code” tag.
Then I think it will be better.
Also, it seems to me that screenshots would not hurt here. And also the source code, for beginners.

theme_park:
I don't know how to take screenshots of events.

Well, that's not necessary.

iamnp:
theme_park , there is a special button on the keyboard - PrintScreen

theme_park:
I know some people just do it differently. Moreover, everyone has their own sprite
and if I set up all these sprites, few people will understand.
Well, maybe someone will give a plus? No wonder I was struggling?

burlachenko:
In order for such a lesson to be of interest to anyone, it needs to be formatted accordingly, but here “anyhow it was.”
And yet, if you want, a little at a time, when you have time, please “beautify” it.

theme_park:
ok, I'll come home from school and get ready.
PS. added source

Serega Lebedev:

iamnp, where do these screenshots go later?

KaMiKaZa:

To the clipboard.
Go to any text editor, and perform the "Paste" operation, or press Ctrl+V.

In this article, I’m unlikely to touch on anything unknown. All calculations are simple and understandable to anyone who knows what Ajax is. I have already written an article about how to combine a client with a server in real-time games (). In this article, I address the same issues as they apply to turn-based games.

So what is turn based game? The following definition can be found on Wikipedia turn-based strategy - is a genre of computer games whose main feature is that players take turns making moves, as opposed to real-time strategy. I would simplify this definition a little:

  • Step-by-step strategy - is a turn based strategy game.
  • Strategy game - This is a genre of games in which the key to achieving victory is planning and strategic thinking..
  • Turn-based game - is a genre of games whose main feature is that players take turns making moves.
Turn-based games include:
  • Turn-based strategies
  • Card games
  • Board games (chess, go, monopoly, etc.)
I note that turn-based games impose fewer restrictions on the complexity of the interaction protocol compared to real-time games. Namely, reaction time to a particular event does not play a key role. The player is usually given 10 seconds to make a decision. Even if the ping is gigantic, say 3 seconds, then the player still has 7 seconds to think. In addition, the ping can jump and jump, but we don’t care about this at all (in real-time games, this situation practically kills any protocol).

Typically (in 95% of turn-based games) exactly one player makes the decision at any given time. Consequently, the number of requests to which we must respond adequately is narrowed.

Therefore, when creating a protocol, we will focus primarily on the ease of its implementation and support. This will allow us to make more profit in less time.

The above reasoning is based on the 2-month development of a certain card game.

Smart or stupid client?

First, let's decide how “smart” our client can be. I'm debating whether it's worth duplicating the application logic (game rules) on the client. Of course, the server must be smart to prevent potential hacking of the application. But is it worth teaching business logic to the client?

This directly depends on how much the full amount of data about the state of your game weighs. If this amount of data is large, takes a long time to collect on the server and is transferred to the client, then it makes sense to implement part of the logic on the client in order to relieve the server. For example, in Civilization the memory used gauge is always off the charts. Can you create something similar, leaving only the UI on the client?

On the other hand, the smarter the client, the more expensive the game development will be. Let me note that the server development time does not depend in any way on the client’s erudition. Even if the client is super-duper-mega smart, if the user wants to reload the browser window, the server will have to collect and assemble all the data about the game to transfer it to the client. A la "Loading a saved game". Conclusion: A smart client can speed up an application, but it will always require additional resources to develop the application.

I suggest the following test:

1. Does the channel volume allow?

Estimate the average weight of the full amount of game state data. Next, multiply by the average number of requests to the server per second. If the resulting number exceeds the outgoing data channel capacity, then the stupid client is unacceptable. If this number exceeds 20% of the outgoing channel, then you should think about whether it will work?

2. Is it labor intensive?

Estimate the complexity of the game data collection algorithm (in fractions of a second). Here, take into account all queries to the database. Next, multiply by the average number of requests to the server per second. If the time exceeds one second, then a stupid client is unacceptable. If this number exceeds 200 ms, then you should think about whether it will last?

Continuation:

Hi all! Now I will tell you how to make a simple RTS (RTS - Real Time Strategy, that is, a real-time strategy) open a hammock 8.1 (operability is not guaranteed on 8.0) create an objControl object, that is, this will be our main object, create a creation event ( Create) Add event => Creation (Add event => Create) the creation event is done only once - when creating, click on the control tab in the right vertical menu and right-click on Execute code and write the code (it’s best not to copy the code, but to write it yourself, it’s much easier to remember):

200?"200px":""+(this.scrollHeight+5)+"px");">startx=0; //Declare a variable for the start point of x
starty=0; //Declare a variable for the beginning of the point by y
draw_rect=false; //Do not draw a selection rectangle


Variable: A piece of memory that contains information. They have their own name with which you can contact them. Variables in GML can contain a real number or a string. For example, a table is a variable, wooden or glass is a value
Now we create a step event (Step, Add Event => Step) and perform the operation again (right-click on Execute code):

200?"200px":""+(this.scrollHeight+5)+"px");">
if mouse_check_button_pressed(mb_left) //If LMB is pressed
{
draw_rect=true; //We draw a rectangle
startx=mouse_x; //Start x position = mouse x position
starty=mouse_y; //Starting position = mouse position
with all selected=false; //This is not yet a declared variable, we will find out what it will do later
}

If mouse_check_button_released(mb_left) //If LMB is released
{
draw_rect=false; //We don't draw a rectangle
for(i=0;i<=instance_number(par);i+=1) //Читайте про цикл for ниже
{
ii=instance_find(par,i); //We are looking for an object that has not yet been made
if(collision_rectangle(startx,starty,mouse_x,mouse_y,ii,true,false)) //Here is our collision rectangle (contact)
{
ii.selected=true;
}
}
}

The code is large and complex while we learn about the if conditional statement:
The code with if is executed like this:

200?"200px":""+(this.scrollHeight+5)+"px");">
if (condition)
{
action
}

It may also contain an else statement (otherwise), example:

200?"200px":""+(this.scrollHeight+5)+"px");">if (condition)
{
action
}
else
{
action 2
}

And for is a loop operator, it is executed like this:

200?"200px":""+(this.scrollHeight+5)+"px");">
for (<переменная> ; <выражение> ;<действие переменной>)
{
<действия>
}


The for operator is a very powerful thing, it helps a lot in difficult situations

Operator - actions built into a language, for example, the most common are int, if, else, string, switch, for, case, break, exit, etc., etc.

Now we also create a drawing event (draw) and write in the same way:

200?"200px":""+(this.scrollHeight+5)+"px");">if draw_rect=true
{
alpha=.8;
draw_rectangle_color(startx,starty,mouse_x,mouse_y,c_green,c_green,c_green,c_green,true);
}

Everything is easy here, here’s the same thing only in Russian:
if we need to draw a rectangle, then we select transparency and draw a rectangle
here are the arguments

200?"200px":""+(this.scrollHeight+5)+"px");">draw_rectangle_color(x1,y1,x2,y2,color 1, color 2, color 3, color 4, outline)


outline - whether only the edge (true) or the filled rectangle (false) will be drawn
We found a new word - constant, this is a numeric expression or code replaced by a word, the hammock has built-in constants:

200?"200px":""+(this.scrollHeight+5)+"px");">true - 1
false - 0
pi - 3.1415...


Well, we figured it out, now we need to create a new object - a parent object that will connect to its children. Let's call it par (to change the name you need to change the code in the control object step event), write in the creation event:

200?"200px":""+(this.scrollHeight+5)+"px");">selected=false; //Here is our variable, whether the object is selected

This is all. Now, of course, we need an object that can move, we call it objTest, and write the code in the creation event:

200?"200px":""+(this.scrollHeight+5)+"px");">gox=x; //Where to go...
goy=y; //by y
selected=false; //We are not selected =)
object_set_parent(self,par) //Here is the choice of parent

New action:

200?"200px":""+(this.scrollHeight+5)+"px");">object_set_parent(ind,obj)

Sets the parent object to the object named ind.
And a new operator: self, it means that the action will go to itself
Don't be afraid, there is still a little left in the step event:

200?"200px":""+(this.scrollHeight+5)+"px");">if distance_to_point(gox,goy) > 20
{
mp_potential_step(gox,goy,6,solid);
}
if(selected=true) && mouse_check_button_pressed(mb_right)
{
gox=mouse_x;
goy=mouse_y;

Formation of strategy
You can't trust amateurs:
their plans may unexpectedly work out,
and no one is ready for this.

(A. Cuningham)

In the two previous issues we learned make simple 2D games, control sprites, scroll the game screen, track collisions of game objects, build an interface (buttons, mouse, keyboard, text areas) and work in full-screen and windowed modes. All this was done using an arcade game as an example.

This time we will move from arcade games to a more “serious” genre - strategies. Here we will have to master a whole series of new mechanisms, but there will be nothing complicated here either. In this article we Let's study the structure of a turn-based strategy(and also real time strategy- it’s even easier to do it with LKI-Creator) and we’ll make a game as an example, designed, however, only for multi-user mode (and also map editor for her). We will deal with the single-player mode in the next issue of our column - dedicated to basics of artificial intelligence.

Since this is already the third lesson, we will not go into detail all example code - fortunately, much has been done exactly the same as the two previous times. For reference, there is an example program (there are many comments in it) and previous articles.

Well, you can find the materials from our past classes on our CD, in the “Do it yourself game” section specially created for this purpose.

Formulation of the problem

Let's write a strategy game consisting of a battle between two fantasy armies. The goal of the battle is to capture several obelisks, placed on the map. Before the battle we deploy our troops, consisting of 6 swordsmen, 4 archers, 2 knights, 2 magicians and 1 ghost, within the territory allocated to us. In addition to them, there are neutral ones on the map dragons.

Characteristics of fighters
Fighter Movement Hits Range Damage Protection Capabilities
Swordsman4 8 1 7 2 -
Archer4 5 7 5 1 -
Knight3 15 1 9 4 Healing, Knight's Strike
Mage3 12 5 6 0 Fire ball
Ghost4 7 2 5 5 Regeneration
The Dragon6 30 2 12 5 Flight

The characteristics of the fighters are presented in the table. Treatment- this is the right to heal a neighboring warrior (except a ghost) to full health once per battle. Knight's Strike- the right to inflict triple damage once per game. Fire ball- the magician’s attack removes hit points not only from the immediate target, but also from surrounding squares. Regeneration- recovery of 1 hit per turn. Flight- the right to move over obstacles.

The game is played in multiplayer mode, in the Hot Seat version (play from one computer, turns one at a time). After the players turn, neutral dragons make their turn, attacking any enemy within a radius of 7 cells.

The party ends when one side either captures more than half of the obelisks present on the map or dies completely.

The map was initially set in the map editor. There are obelisks, dragons and obstacles (objects through which you cannot move or attack).

Preparing for work

Before we start, we will need to reinstall the package LKI-Creator. The fact is that compared to last time, many changes and additions have been made to it.

(I hope that Delphi you already have installed; if not, then read recommendations on this topic in our previous article - in the June issue of the magazine or on the CD of this issue or on the website.)

It is important: the previous version of LKI-Creator had some compatibility problems with new versions of Delphi. In this version they are eliminated.

Take the file with program texts and pictures from our CD (section “Game with your own hands”) and unpack it into the project directory.

Now you can download necessary files from here .

We should have three subdirectories. One - Units - stores DirectX libraries and modules of the LKI-Creator package. In another - Project - we will work; the pictures that we will need are placed there in advance, and previous version our arcade. In the third - Escort - a ready-made program that we should succeed in.

Now let's install (reinstall) LKI-Creator. In the Delphi menu, open the Component item and select Install Component. If you have already installed this package, stay on the Into existing package tab, otherwise go to the Into new package tab and fill in the empty lines as shown in the figure (in the top line, the easiest way is to select the LKI2dEngine.pas file using the Browse button, and in at the bottom just write down LKI). Then click OK and select Install. You should now see the LKI tab in Delphi's top panel.

Now all that remains is to upload our project. In the File menu, select Open, open the file Project\Obelisk.dpr...

Where's the map, Billy? We need a map!

However, before we get into the big stuff, we'll need to work a little more on the graphics engine.

In Star Escort, our previous project, the “map” had no meaning: the stars were placed randomly and did not affect anything, and the position of other objects was either specified directly in the code or determined by chance. This is not suitable for every project. This means it’s time for us to add to our engine area map.

You can probably already guess what it will look like - we put a map object on the project window, and then register it in the property Map our engine.

That's how it is... but we have more than one class of card. Let's take a closer look...

Types of cards

The map consists of something landscape And objects installed on it. The landscape is most often (but not always) divided into cells called tiles- tiles.

As we know from a school geometry course, a plane can be covered without gaps or overlaps with regular polygons of three types: triangle (equilateral), square, hexagon. Triangular fields are not particularly convenient, so square cells or hexagons are more often used.

In a sense, living with squares is easier: if we have a two-dimensional array of cells, it is immediately clear how to find the cells adjacent to a given cell. These are +1 and -1 for each of the two indices. With hexagons, everything is a little more complicated... but the hexagonal board has a very valuable property: all directions in it are the same. This is not the case with a square grid: the diagonals are significantly different from the horizontals and verticals. Therefore, for serious strategic calculations, hexagons may be better than squares.

There are also non-tiled cards. LKI-Creator supports two types: graph and patchwork.

A graph map is a map on which only a few key points have meaning, plus perhaps special areas (for example, impassable ones), and the rest is just a pattern that has no game effect. This is how star maps are often made, as in, say, Master of Orion: stars and black holes are key points, the rest is background. In this mode, they sometimes make global maps, for example, for a role-playing game.

The patchwork map is divided into areas, and inside the area all the points are the same; you cannot move along the “patchwork”. This is good for global strategies, where a province is the minimum unit of territory.

Examples of cards from various games, indicating the type, are in the pictures.

So the majority two-dimensional maps (three-dimensional - a special article) can be divided into four classes:

  • Rectangular- TLKIRectMap. This is a tiled map, the cells are squares. Such a map, for example, in Civilization III.
  • Hexagonal- TLKIHexMap. Tiled map with hexagonal cells. Used in many wargames, and not only: this is, for example, how the Heroes of Might & Magic battle map was traditionally made.

    These two types of cards are descendants of the general class TLKITileMap.

  • Graphovaya- TLKIGraphMap. This card has background (Background property) and the key points highlighted on it are static objects. The position of other objects on this map is expressed either by ordinary coordinates (like a spaceship in interstellar space) or by reference to an object (the same ship in orbit of a planet). These are the cards Master of Orion, Arcanum (global), and so on.
  • Patchwork- TLKIClusterMap. It has a background property, like the graph one, and a second property - mask, which determines which point belongs to which region, and the property Borders, which defines the connections between the “shreds”. This is how maps are arranged, for example, in Medieval: Total War or Victoria.

It is important: map classes are described not in the LKI2dEngine module, but in LKI2dMap.

Tilt angles

But if you think that this exhausts the capabilities of LKI-Creator for displaying maps, then you are very mistaken.

The map can be presented top view or isometric- look at an angle to the vertical. For example, the map of Civilization III or Heroes of Might & Magic IV is isometric, but Civilization I adopts a top-down view.

Typically, isometry is used for tiled maps, while graph maps are used with a top view, since the scale of graph maps is usually smaller. But there are exceptions: for example, in Medieval: Total War there is a patchwork isometric map.

The map property is responsible for isometricity IsIsometric and two parameters that set the angle at which our camera looks: Phi And Theta.

The first is responsible for the rotation of the map relative to the vertical axis: for example, if you set it to 45 degrees (it is measured in degrees), then the rectangular grid cell will be oriented with an upward angle, as in Civilization. At Phi=0, one of the sides of the cell will be horizontal.

The second controls the tilt of the camera relative to the vertical. For convenience, it is given as the ratio of horizontal and vertical units of length. Let's say, if we want our cell to be drawn in height half as wide as it is in width, we need to set Theta to 2.

With a tiled map, we are not allowed to choose these angles arbitrarily: after all, we do not (yet) have 3D. They directly depend on the parameters of the tiles. For example, if we have a diamond-shaped axis with an upward angle, and the vertical axis is half the size of the horizontal axis, then we must set the parameters 45 and 2.

But graph and patchwork maps give you the right to assign these parameters as you like (and even, if desired, change them in the process), but you should not get carried away with this - in addition to the fact that such turns take a lot of time, they also do not look very cool. And don’t forget that if your map is artistic, with pictures, inscriptions, etc., then they will turn with it... In general, sometimes it’s easier to draw a patchwork map taking into account the required rotation - fortunately, distances often don’t play any role there .

Joints

Patchwork map, top view.

Tile maps have another problem - tile joining. It is controlled by the parameter TileBorderStyle. Most often this tileStraight, a mode in which the tiles simply fit together without any edge effects, or tileBorder, in which lines are drawn separating one tile from another - the boundaries of the cells (in the latter case, do not forget to define color lattices in parameter TileBorderColor).

But there is a more cunning option, when identical tiles are adjacent to each other without changes, and different ones are used using a special “transitional” tile. This is usually done if the map consists mainly of wide expanses of one type of territory, say, large green areas, and an individual cell is not important and should not be noticed by the player. This is the Heroes of Might Magic card. But if each cell is processed separately, as in Civilization, then this method is not suitable, and it is better to clearly separate the cells from each other. “Fused” technology (also called mask) is specified by the TileBorderStyle value equal to tileMasked. We will talk about their structure another time - this is a rather complex topic.

Tile

Map element - class object TLKITile- has a simple structure. It initially contains: coordinates, the sprite that draws it, the tile type code (which determines what we have here - a hill, desert, road, sea?) and cross-country ability (this is relevant in most games). The last one is the number of move units that are spent on moving through this tile land squad. For impassable tiles, this is a negative number.

Another parameter - Objects, a list of objects located on this tile (type TLKIGameObject).

To find out which cell was clicked on, the map has a method MouseTile(x,y) returning the selected tile.

Tile methods include IsNeighbour(Tile, Distance). This function returns true if the Tile is no more than Distance cells away from the given tile (by default this parameter is equal to one, that is, if you simply write IsNeighbour(Tile), the function will return true for the tile immediately adjacent to the given tile. For a square grids, those tiles that border diagonally are also considered “neighbors”.

Functions FirstNeighbour And NextNeighbor are used to check all cells adjacent to a given one. The first of them points to some neighbor cell, and the second can be called only after calling the first, and it gives out the next neighbors, one at a time.

Enumeration of neighbors

// Causing damage to a cell

procedure TObeliskTile.Damage(dmg: integer);

if(Objects.Count > 0) and// We may have

// no more than one object per cell

(Objects.ID > 0) // Passive objects

// not damaged

Dec(Objects.Hits,

// Automatically subtract protection from damage

Max(0,dmg-(Objects as TObeliskGameObject).Defense);

if Objects.Hitsthen Die; // We remove the dead

// Fireball attack

procedure TObeliskTile.Fireball;

var Neighbor: TObeliskTile;

Neighbor:= FirstNeighbour as TObeliskTile;

Neighbor.Damage(6);

Neighbor:= NextNeighbour as TObeliskTile;

until Neighbor = nil; // Until the neighbors run out

An example is in the “Enumerating Neighbors” sidebar. This procedure calculates the fireball hitting a cell and all its neighbors.

This is interesting: for her work it doesn't matter at all, we have a hexagonal lattice or a square one.

Often we need some other parameters, and usually the class of tiles that make up the map - descendant TLKITile. So in the example - TObeliskTile is inherited from TLKITile.

It is important: If we bring a tiled map into our game screen, the coordinates, as well as the distance-related TLKIGameObject methods, by default start measuring distance in tiles rather than points. The coordinates of buttons, icons, etc. continue to be measured in pixels! But this mode can be turned off - this can be useful for real-time strategies.

Selecting a card

So, let's start with a rectangular lattice (TLKIRectMap), an isometric mapping (angular parameters 0, 1.5). Let the grid be drawn (tileBorder style). Let's tell the engine that this particular map should be displayed. So far, all the necessary actions have been completed without writing a single line of code.

These operations must be done before engine initialization, as well as font declaration.

We will declare the figures, as before, as sprites.

Map editor

Patchwork map, isometric.

There are quite a few difficulties here. Exactly the same engine, the same tile declarations... The interface, like selecting a tile, loading/saving, etc., can be easily done standard means Delphi: no one is forcing us to translate it into full screen mode. We will not analyze this here - everything is in the example file. The example code deliberately used the simplest way; If desired, you can, for example, make the object palette and tile palette graphic.

The editor has only two features that are unfamiliar to us. The first is quite simple: it's a new mouse feature designed specifically for tile maps. Function TLKIRectMap.SelectTile returns a pointer to the exact tile that was clicked, so we can easily handle the click.

But the second new product deserves more careful consideration.

In fact, there are many ways to save data to and read data from it. We chose the method encoded in the file CannonBase. Cannon is a tool for reading and writing descendant objects TCannonObject with type checking and some other features.

Let's look at the code (“Write card”).

Recording a card

procedure TObeliskMap.Save;

var i,j: integer;

InitSave(FName);

WriteStr(MapName);

Write(Map.Width, SizeOf(Map.Width));

Write(Map.Height, SizeOf(Map.Height));

for i:=0 to Map.Width-1 do

for j:=0 to Map.Height-1 do

Write(Map.Tiles.Code, SizeOf(integer);

Here's how it works. First you need to open the file using a special procedure InitSave, whose only parameter is the file name. Then we save the header for type control using a special procedure WriteHeader. Then we write down everything we need using the procedure WriteStr for strings, and for all other fields - Write(its second parameter is the size of the written data in bytes). You can write your own procedures for object fields as needed Save with a header record. Finally, we close the file with the procedure FinSave.

All objects that have their own header must be declared separately. In chapter Initialization module (optional section that comes after Implementation, which contains commands that must be executed at the very beginning, when starting the program), you should write the following line, for example:

RegisterUserName(tpMap, "TObeliskMap");

TpMap is a constant that you must declare too. Equate it, say, to 1. And in the constructor of the TObeliskMap object, assign the value of this constant to the parameter TypeID.

Why all this fuss? Besides type matching, you get one very important benefit.

If the file format changes, say, due to the addition of new fields, you will not need to write any “converters” that convert the old files into new ones. Your code will automatically read them.

This code will automatically initialize the new field as empty if it is not saved in the file. And you can write a file by simply adding the WriteStr(Name) line to the very end.

Comment: If you still don't understand what this process is for, don't worry. You can use more conventional recording and saving methods. But in truly large-scale game projects, this path provides significant advantages.

Let's play

First of all, we need to create a new class derived from TLKIGameObject. We will miss the properties of the old one. In the new class, you need to add fields for the main characteristics: range, movement, and so on.

It is important: Our old speed parameter remains with us, but it indicates the speed of the piece moving across the screen, and not the distance it will travel per turn. If we were making a real-time strategy, we would not need a new parameter, but otherwise we will have to introduce it.

On our screen we will apply TLKIButton buttons in the form of archers, swordsmen, magicians, ghosts, knights.

First we have the arrangement. Let's define the placement zone for one side as the top three “lines” of the map, for the other - as the bottom three “lines”.

The code works like this. When you press any of the buttons, the installation of the corresponding fighter is activated; Clicking on an unoccupied square in the placement area places the figure there and disables the button. As soon as all buttons are disabled, the move is transferred to the enemy.

At the beginning of each new move, all the buttons are turned on again: this is done so that it is easier for a person to notice who he has not yet resembled. Accordingly, clicking on the button selects the figure, and as soon as a move is made, the button disappears. Another button - “End turn” - appears only after the placement phase.

Last time we already did operations to enable and disable interface elements, so we will not analyze this operation in detail - look at the example code.

Move of the figure

// If the selected cell is occupied by the enemy, we attack,

// if free, we move around, if busy with our own

// or an obstacle - ignore the click

Tile:= Map.MouseTile(MouseX, MouseY);

if(Tile = nil)// Click outside the game window

then exit;

// Moving

if(Tile.Objects.Count = 0)

and(Dist(Self)

and not Moved then

// Let's check if we can get there

if not HasWay(Tile) then exit;

MoveObj(ID, Tile.x, Tile.y);

// The game is turn-based - move immediately

Moved:= true;

//

if Attacked then

Icon.IsVisible:= false;

// Attack

if(Tile.Objects.Count > 0)

and(Dist(Self)

and not Attacked then

Obj:= Tile.Objects;

// We attack only enemies

if Obj.Side = Side then exit;

Obj.Damage(dmg);

Attacked:= true;

// If the move is complete, remove the icon

if Moved then

Icon.IsVisible:= false;

The move is processed as follows (see “Piece move”). The clicked cell is located. If there is an enemy on it, and they are within range, he is harmed; if it is empty and within range, the piece moves (if obstacles allow); if it is occupied, but not by an enemy, the click is ignored.

When both sides approached, the dragons acted. They operate very simply: they select the nearest non-dragon that is within 7 squares of them and attack. See the Dragon Actions code.

Dragon Actions

// Checking tiles within 7 squares of the dragon

for i:= Max(0, x - 7) to Min(MaxSize, x + 7) do

for j:= Max(0, y - 7) to Min(MaxSize, y + 7) do

if (Map.Tiles.Objects.Count > 0) and

(Map.Tiles.Objects.Code>1)

// 0 - obstacle code, 1 - dragon

then begin

// Selecting a point to move

if x=i then ax:=i

else if x>i then ax:=i+2

else ax:= i-2;

if y=j then ay:= j

else if y>j then ay:= j+2

else ay:= j-2;

MoveObj(NO, ax, ay);

// Let's attack

Map.Tiles.Damage(12);

// Breaking the cycle: no more than one attack

// each dragon per round

Finally, all that remains is to check whether more than half of the obelisks are occupied by troops of one side - and if they are, then stop the game and declare the winner!


So, we have a strategy game. However, for complete happiness, what is missing, first of all, is artificial intelligence, which will make it possible to give the game a single-player mode (we do not consider the simplest procedure for controlling dragons). That's what we'll do next time. See you in a month!

In future issues

In the following issues we will talk about:

  • particle systems for displaying smoke, sparks, etc.;
  • working with transparency;
  • three-dimensional engines;
  • AI basics;
  • debugging the program;
  • creating a game plan and script,
  • writing a design document;
  • game balance;
  • thinking through game characters and their lines;
  • working with Photoshop and 3D packages;
  • animations;
  • music and voice acting;
  • and much more.

It’s quite possible to learn how to do all this with your own hands. You will soon see this.

Write to us…

For those who think that the package can be supplemented with something: firstly, do not forget that there is no Final version package, but only the one that implements the functions described in our articles. Perhaps some of your ideas have already been implemented and are waiting their turn (see the sidebar “In future issues”). And in any case: when offering us an idea, try to justify why your proposal is useful for many games at once, and not just for your specific one.

For independent work

While waiting for the next issue, you can work on your own project, or you can try to improve this one. Here are some ideas for your own implementation:

  • divide obstacle objects into destructible (trees and bushes) and indestructible (rocks), and make sure that fireballs and the dragon’s breath burn the vegetation;
  • create pits (brown cell) or a fire that blazes for several turns (red cell) at the place where the fire attack was triggered;
  • allow swordsmen and knights to cover their neighbors, giving them +1 to defense;
  • make the movement of figures on the screen smooth.

What if in real time?

It's no more difficult to make a real-time strategy game if you just give players different input means. The easiest way to do this is over the network - we will talk about this in one of the upcoming issues. The following changes will also be needed:

  • no field needed GameSpeed at the class TObeliskObject- use Speed ​​from the base engine (the speed of movement across the screen is equal to the game speed);
  • integer calculation of distances is disabled;
  • the figure's movement code is rewritten, taking into account the fact that it is necessary to draw a trajectory around obstacles;
  • The "end of move" button is removed.

That's all. Will you try to do it yourself?

Nowadays, you can easily find a lot of trading strategies, the only problem is that most of them either do not work or are not effective enough. In such a situation, creating a strategy from scratch is an excellent solution to the problem.

And although in each specific situation the creation of a vehicle will be different, the main stages remain the same. That is, you can create something like a universal guide, sequentially going through all the stages, and at the end we will get a completely workable system.

Although we will consider the option of creating a strategy from scratch, starting with an idea and ending with the introduction of a filter and the final development of the vehicle, this does not mean that all ready-made systems should be immediately discarded as obviously ineffective. A number of TS can be used as a basis and simply add a few filters in order to weed out false signals.

Main stages of creating a strategy

If you take 5-7 indicators at random and try to create a working vehicle from them, then something useful is unlikely to come out. First you need to think through the main idea of ​​the strategy and only then move on to selecting tools. The following sequence of actions can be suggested:

  • if the system is created from scratch, then the first step is to work on the idea. At this stage, you just need to decide what your trading style will be, for example, whether transactions will be carried out along the trend or against it, or perhaps the strategy is planned exclusively for breakout. We’re not thinking about specifics yet;
  • After choosing the appropriate trading style, you can begin to work out the details. Before selecting instruments, you need to clearly determine the working currency pairs and timeframes, trading time, etc.;

Important! When choosing a time frame, it is not advisable to go too small, especially if the trader does not have experience in speculation on small time intervals. For beginners, it is generally better not to go below H1-H4; you can read more about choosing the optimal time interval.

  • the next stage is the selection of suitable instruments, and here the abundance of choice can play a cruel joke on the trader, because he has dozens of indicators at his disposal, the ability to use candlestick, wave, technical, and fundamental analysis. From all this variety, you need to choose several instruments that best suit your intended trading style;
  • One of the main rules of any strategy is that the received signal must be confirmed by several filters, but it is advisable to limit their number. For example, for indicator strategies, it is undesirable for the number of indicators to exceed 3-4, otherwise there will be confusion in the signals, and if you plan to work using technical analysis, then the indicators will play a supporting role;
  • rules for position support are developed, the result should be an algorithm of actions for any scenario;
  • the last stage is to run the vehicle first on a history account, and then on a demo account, or better yet, on a real cent account. The main goal is to test the viability of the strategy in real conditions, and a real account (even a cent) will allow you to feel the whole range of emotions, from euphoria to the bitterness of losses.

You can also advise not to get hung up on the complexity of the vehicle. Practice shows that excessive complexity is not at all an indicator of effectiveness. How simpler system, the easier it is to trade.

What should be included in the vehicle

Creating a strategy is a bit like a construction set; you just need to choose the right parts. When creating a vehicle you can and should use:

  • indicators. Moreover, you should not chase either their number or excessive complexity; even in fully mechanical vehicles, 3-5 pieces are enough;
  • graphic patterns - well-known flags, pennants, triangles, head and shoulders, double bottoms and tops still work well;
  • graphic constructions;
  • Fibo levels;
  • elements of fundamental analysis - now it is difficult to make money solely on news, but it is necessary to take into account the economic calendar in trading. At least just move transactions to breakeven before the release of important statistics for the US or Europe, or even completely close the profit.

There are also a number of techniques, the benefits of which are questionable:

  • Martingale is an initially unprofitable tactic, the only chance of success is that before the loss, you will be able to recoup the deposit amount and earn a little;

  • It is also undesirable to use locking unless absolutely necessary; then it is quite difficult to leave the castle;
  • It is also better not to use signals from outside. Signals from the outside mean, for example, a comparison of the number of sellers/buyers, various indices of the mood of the majority of traders, etc. But there are also a number of resources that you can listen to, for example, Autochartist gives good signals based on graphic patterns;
  • It is also better not to use elements of wave analysis (at least for beginners). Although there are exceptions here, for example, trading using Wolfe Waves is based precisely on wave analysis, but it is not particularly complex.

Development of a simple indicator strategy

Let's assume that the main idea of ​​the strategy is to trade exclusively in the direction of the trend. Moreover, you will enter the market not at the moment of the emergence of a trend, which is quite risky, but after the completion of a small correction.

The strategy should work on almost any currency pair; this is necessary because, due to the large time frame, signals will not occur very often. So the situation will be assessed using 5-7 currency pairs at the same time. As for the time frame, D1 is suitable; we proceed from the fact that Forex at the initial stage is only an addition to the main work, therefore we will trade on daily candles.

Now you need to decide how exactly the moment to enter will be determined. There are many options for identifying a trend:

  • visually - but eyeballing will not allow you to develop clear trading rules;
  • using one or more moving averages;
  • according to the value of the ADX indicator, if more than 30, then there is a strong trend, less than 20, the market is calm;
  • using graphical constructions;
  • Bollinger Bands allows you to judge the strength of a trend by the distance between the upper and lower boundaries of the channel.

In general, there are many options, but since the strategy needs to be as simple as possible, we will focus on moving averages. Or rather, one MA, we will judge the direction of the trend by the position of the price relative to it. You can start with periods that average the price over a whole period of time (week, month, quarter, etc.), for example, 20 will average the price over the past month. The most difficult thing is to choose the appropriate MA period, but here you will have to proceed by trial and error.

The main idea of ​​trading will be the following: we wait for the trend movement to appear, then a correction follows within one day, after its end a deal is concluded. If all this is presented in the form of clear rules, then the algorithm of actions will look like this (using the example of purchases):

  • first we check the price position relative to the MA, the price should be above the line;
  • Next we need to find a trend movement, we need a candle with a large body and small shadows (it is advisable that the value of the shadow does not exceed 10-15% of the size of the candle body);
  • it should be followed by a candle with a small daily range (distance from High to Close). The Close price should not reach the level of 50% of the previous day - on this day the price correction occurs.

In this case, correction can take place according to 2 scenarios:

  • When a candle with a long shadow below is formed, you can open a trade immediately at the close of the day. This shape of the candle indicates that the correction has already completed;
  • a possible option is when the correction candle closes at a minimum, i.e. the rollback has not yet completed, in this case you need to wait until the next candle closes; if it is white, you can open a long position.

As for maintaining the position and protective orders, the stop loss should be placed at least beyond the middle of the candle with a large body. To get the target level, it is enough to set aside the value of the same candle from the purchase level; you can use a trailing stop.

The rules of the strategy have been formed, all that remains is to test it in action. Practice shows that even such a simple algorithm produces more than half of profitable trades (and if you transfer the trade to zero early, the likelihood of a loss decreases).

Entry at the beginning of the trend

The TS proposed above misses part of the trend movement, but does not require much time for market analysis. If it is possible to monitor the behavior of the price of the day, then you can try to ride the trend at the very beginning of its formation. So the main idea of ​​the strategy is to enter the market after the price starts to leave the horizontal channel.

And here it is very important to receive a reliable signal on time, at the very beginning of the trend. Moving averages can again come to the rescue, but they will be used somewhat differently than in the previous strategy. You can take several MAs with different periods and judge the formation of a trend by their location relative to each other.

This is not a revolutionary approach; Bill Williams used something similar in his famous Alligator. So this indicator can even be taken as the basis of the TS.

If we add Alligator with standard settings to H1, we can see that it gives a lot of false signals; our task is to weed them out. To do this, we will enter the market with a pending order, and the level of its placement will be determined using Fractals.

As a result, the work flow will look like this (using the example of purchases):

  • We are looking for a flat area on the chart (all Alligator lines are chaotically intertwined with each other);
  • Next, you need a fractal candle (and it should be located above the lines, without even touching them with the lower shadow);
  • a buy stop order is placed just above its maximum, and a protective order is placed beyond the lower border of the consolidation zone;
  • TP is either fixed (50-70 p), or use a trailing stop. In general, it is quite difficult to predict the strength of a trend movement, so it is better to use a trailing stop than a fixed TP or determine the target level using the same technical analysis.

This example is not developing a strategy from scratch, but rather a slight adaptation of an already known strategy to the modern market.

Example of a simple combined system

The idea is still the same - entry at the end of the correction, but we will solve this problem in a different way. The goal remains the same - to determine the moment of completion of the correction and enter in the direction of the trend, but if earlier we used the moving average, now we will use Fibo levels and the oscillator to determine the completion of the rollback.

We will be interested in a pronounced trend movement; it can also be determined visually. After this, you just need to stretch the Fibo levels to it, this is necessary to determine the potential levels for completing the correction. The most significant correction level is 38.2%; it is allowed to trade on rebounds from 50% and 61.8%.

But the fact that the price touches the Fibo level is not enough; confirmation is needed. Stochastic can cope with this task quite well (you can try using RSI for the same purpose). For example, for a buy transaction, the price must be close to the correction level, and the Stochastic at that time must be in the oversold zone.

After the price touches the level, we wait until the Stochastic leaves the oversold zone and buys. The stop can be placed beyond the next correction level, and the TP can be placed at least at the recent high prior to the start of the pullback.

Fibo levels and an oscillator are just the minimum required to receive a signal. If desired, you can complicate the system by introducing additional filters.

Strategies based on technical analysis

If technical analysis is used in its pure form, then the task becomes somewhat simpler; it is no longer necessary to develop a vehicle from scratch. There are a number of techniques and tools, the effectiveness of which has been tested over the years, and the trader can only choose the appropriate ones and use them in constructions.
In principle, for successful trading it is enough:

  • trend lines - we build them on different timeframes, starting with the highest one and gradually descending to the working one;
  • Fibo levels, they should be stretched to significant price movements in history. Correction levels of 38.2, 50 and 61.8% will be of key importance; practice shows that the correction most often ends at these marks;
  • MT4 also implements a number of other technical analysis tools, for example, Andrews pitchforks, a set of Gann tools, several types of channels, etc.

But for a fairly accurate forecast, Fibo levels and trend lines are enough; areas of concentrations of levels and lines usually act as support/resistance. Trading can be carried out both for a breakout and for a rebound from levels and lines.

As for additional filters, you can add candlestick patterns and some kind of indicator to search for divergences (the same Stochastic or MACD). You can read more about the use of technical analysis.

Filters for trading systems

Any trading vehicle, even if the idea is working and trading is generally profitable, generates a lot of signals and some of them are unprofitable. After the system rules have been formed, you can use a number of universal filters that will help improve the ratio of profitable/unprofitable transactions.

There are a number of filters that will suit almost any strategy:

  • situation on higher timeframes. If, for example, the strategy is carried out on H1 according to a mechanical strategy, then it would not be amiss to look at what is happening on H4 and D1;
  • average daily range, meaning the distance that the price travels on average per day; when trading intraday, this will help filter a number of signals. Let’s imagine that during the day the price moves on average 100-120 pips, if on one of the days the price by the evening had already passed 90-100 pips in one direction, and the TS gives a signal to enter the market in the same direction, then it makes sense to ignore it , statistics say that traffic is unlikely to continue on this day;

Important! Compliance with this rule will lead to the fact that some profitable trades will be missed, but the ratio of profitable/losing trades will increase.

  • The number of candles after the signal is formed also matters; there is even a filter of 5 candles, which we will dwell on in more detail.

When the TS gives a signal, then ideally the situation should begin to develop in our favor almost immediately after the transaction is concluded (meaning on the next 1-3 candles). If this does not happen, then the more time has passed since the transaction was concluded, the less influence on the market will be those factors that mattered when the trader entered the market. Their influence fades over time.

You can take 5 candles as a criterion; if, after concluding a deal, the price has not moved in a profitable direction for 5 candles, then it is better to close the deal manually. This means a scenario where the chart remains in place, but if the price has gone to the unprofitable side, then this filter is not applied.

This condition applies to almost all timeframes (with the exception of m1-m5). Of course, there is still a possibility that the price, having hovered around one mark, will move in the right direction, but our choice is stability. It is better to sacrifice a couple of profitable trades, but avoid 3-5 unprofitable ones.

Summarizing

Starting to work without a trading system is like death, every trader should remember this. In this case, losing the deposit is just a matter of time.

Choosing a suitable vehicle is not difficult; moreover, you can find good working systems in the public domain. You can also worry about creating your own strategy, especially since this does not require knowledge of programming languages, you just need to have an idea and at least basic knowledge of how the market works and what indicators are used for what purposes.

The process itself is reminiscent of assembling a construction set - first we set a problem, and then we simply select the necessary tools to solve it. After checking the history, you can proceed to the TS test on a demo or cent account.


Top