Writing a turn-based PvP arena with simultaneous moves

In this article I’ll talk about what prompted the creation of the game in such an unusual genre, what kind of a genre it was, how development progressed, what difficulties we encountered, and how less than a year of work in the evenings we managed to create a fully playable prototype.



In the spring of 2017, I came across an Atlas Reactor incentive . The game was a kind of wild mix of chess, poker and mobs, and such an unusual combination of genres really caught my eye. She became my favorite game, I participated in online tournaments and events, and everything would be fine, but ...

In the summer of 2019, the servers were closed, because due to the low popularity of the game, their support was unprofitable for the publisher. The game was built on the model of game-as-a-service , so shutting down the servers turned the clients into broken pieces of code.

Due to the unique combination of genres of similar games, there simply weren’t any games on the market, but I really wanted to play something like that.

Shortly after closing, I came across a post on the reddit that a group of enthusiasts from among former players decided to create a private server and adjust the client code to work with it, but I didn’t have a special desire to join them. I have never been interested in reverse engineering, and besides, such an activity does not look too legal.

In the end, I decided to gather people and organize work on the “spiritual heir” - to make a game with similar mechanics, but new characters, abilities, cards and ENT. We began to work on pure enthusiasm in the evenings and on weekends. We wanted to bring something of our own to the genre, add variability and depth, and correct the flaws of the original. From the very beginning it was clear that the game would not become super-popular, but I was sure that at least several thousand people might be interested in it. At least - fans of the closed Atlas Reactor.

Gameplay


In general, the action is reminiscent of the multiplayer XCOM, but with 100% chance of hitting and heroes (as in the MOBA genre).

The idea of ​​the game is that all the results are completely determined by the fixed actions of the players. The randomness is completely absent, and the variation is low enough so that experienced players can calculate all the likely outcomes and act on the basis of this information. To reduce variability, a flat playing field divided into cells is used, that is, heroes can only stand in the centers of the cells (although the abilities are usually applied anywhere, without reference to the grid).

Usually 8 people participate in a match. They are divided into 2 teams of 4 people, and each controls one character. However, due to the step by step, nothing prevents to create a mode in which only 2 people participate, and each controls all four characters of his team.

The game is divided into moves. Each move is divided into two stages - decision (decision making) and resolution(display of the results of the selected actions). The dynamics are added by the fact that both stages occur for both teams at the same time (this type of step-by-step is called We-Go) - there is no such thing that one team thinks, and the second simply waits, looking at the screen where nothing happens. Instead, both teams make decisions and observe the results at the same time. The simultaneity of the moves and the presence of the field of view of the characters leads to the fact that the game cannot be attributed to games with full information .

Decision- a phase in which players have an immediate opportunity to choose their desired actions. The playing field “freezes” for several tens of seconds, and the players must choose what specific actions the characters controlled by them will have to perform when the “time stop” is over. Each player sees what actions his allies are going to take, but the actions chosen by opponents are hidden.

Resolution - the phase in which selected actions are performed. In time, it takes about the same as Decision, or a little less. During it, the players have no control - they simply observe what is happening and think over the actions for the next move.

Resolution is divided into phases. The phases go sequentially, and for each ability the players know in advance in which phase it will work. In this case, the order of operation of the abilities within one phase does not matter.



As a rule, damage is done in the Blast phase, so the defensive abilities trigger earlier - you can either apply shields in the Prep phase or move (dodge) in the Dash phase. But when dodging, the enemy can take damage if it passes through a trap (set by the player in the Prep phase). Moreover, if the enemy does not dodge, but simply remains in place, then the trap will not work and will not cause him any damage. The same thing with shields: if you do not attack a protected character, then they will simply burn at the end of the turn, without having any effect. The interest here is that, as mentioned above, the actions of the opponents at the time of decision-making are hidden, and therefore they have to be foreseen.

The typical damage value does not exceed 35 to make it easier for players to count in their minds. Status effects and abilities are few enough for the same reason. The main game mode is the usual deathmatch, which goes up to 5 kills or 20 moves.

Implementation


I decided to write in C # (since this is my main language), and I chose Unity as the engine (since I had no previous experience in creating games, but it natively supports C # and is quite friendly to beginners).

Turn-based games do not require a large amount of resources, so from the very beginning I considered the most budget options for hosting the main server. There was an idea to host on heroku (because it’s generally free), but restarting the application at random time at least once a day is extremely inconvenient. He stopped at VPS with Linux for 45 rubles per month.

Given the limited resources on the hosting (especially - very little space on the hard drive) and the fact that all the game logic works in 2D, I decided to make it on .NET Core (by writing my lightweight 2d engine to calculate the areas affected by abilities), and use Unity purely for client-side visualization. This made it possible to simply control the field of view of characters on the server side, and send clients only the information that they should know about. The selected actions of players before processing on the server are validated, which completely excludes the possibility of cheating.

For the convenience of the players, a simple launcher for WinForms sawed it off, which, if necessary, downloads the updated version of the client from Dropbox.

Now I will dwell in more detail on the most interesting (or causing difficulties) moments that we encountered when developing the game.

The procedure for using abilities


As mentioned above, the result obtained should not depend on the order of application of abilities within the phase. On the server, abilities are used in ascending order of the hero’s id, but the player should not think about it. This condition imposes restrictions on what and in what phases can occur (game designers are forced to take this into account when creating abilities).

For example, once in the Prep-phase you can apply shields, then you can’t do damage in it (otherwise it will depend on id, damage will pass to the shields, or directly to health before they are applied). Similarly, since damage can be dealt in the Blast phase, it is not allowed to apply the “Mighty” status effect, which increases the damage done by the hero. Similar rules appeared for all game elements.

Simple game logic


The very idea of ​​the game is that players are able to calculate in their head all the development of events within the course, and therefore the logic of all game elements should be simple and intuitive. But with the implementation of the plan a lot of difficulties arose.

Firstly, the movement . Heroes can move during the Dash and Move phases, and this movement must occur simultaneously. Two heroes are allowed to be on the same cell while they are moving, but at the end of the phases of movement, no more than one character should remain on each cell.

The simplest rule for resolving conflicts at the end of the movement is "whoever got up first, that and slippers." If the hero has traveled a long way before being on a “controversial” cell, then he should be pushed back, and the one who has traveled a smaller path should be left in place. If the heroes reached their destination, having traveled the same distance, then push them both. Due to its determinism, it was decided to push the heroes back along the trajectories of their movement. The repulsion is repeated until all conflicts are resolved.

The second problem is equivalence . Suppose we want to create a profile that, when it hits an enemy, deals damage to it and automatically ricochets to the nearestto him a character. The problem here is a combination of “automatically” and “nearest”. Heroes are located on a checkered field, which means that next to the primary target at the same minimum distance there may be several characters.

What to do?
  • Ricochet in both
  • id
In fact, the solution is that we have more data - the position from which the shot was fired. If you can’t choose one nearest target for the rebound, then you can sort the heroes clockwise, where the main goal is the center, and the hero who applied the ability is the starting position.

Honest Geometry


As it turned out, playing with honest geometry is not interesting. The walls are impervious to most shots and block the field of view of opponents, and in addition serve as shelters (and if damage comes from the side of the shelter, then it is reduced by 50%). However, the walls extremely limit the affected area for the hero himself, and because of this, the players did not see any reason to use them.

Paint times

— , — . ( ) — . , 90 .

It was decided to check the visibility range not just between the centers, but between some specific points.

Paint two


So, the use of an additional four points along the edges of the hero reduced the "dead zone" to 60 degrees (which has already become quite playable).

As a result, the walls became really useful.

Paint three

, 50% ( , ).

Additional points are located exactly in the middle between the center of the cell and its edges, and this choice is not accidental. For rectangular attacks, it is very important where the shot is fired from (since this point is used to calculate rebounds from walls, explosions, and some other attack modifiers). A simple algorithm made it possible to automatically shift the starting point inside the hero, depending on the position of the mouse. In this case, the point is located so as to maximize the range of the shot close to the wall.

Here is a link to the gif (20Mb) , where you can see how the automatic shift of the shot point works.

Localization


To add localization, I used string.Format (so that balanced changes in the damage numbers did not create new lines) and a Google plate. All strings used to display on the screen are wrapped in a method that interpolates and checks against the dictionary from known translations. At the start of the debut version of the game, translations from the Google plate are loaded into a text file in json format, and the lines from the source code without translations are automatically downloaded to the same Google plate at the first attempt to display on the screen. In the release version of the game, for obvious reasons, there is no online functionality for working with the table, and translations are simply loaded from the existing text file at startup.

For translators, the solution was not very convenient (because changing one word leads to the creation of a new line in the table), but it was very simple to implement and did not burden programmers with working with resources. It would be ideal if the lines did not change during the development process, but this was not the case with us.

Rake


Now - the most interesting. Some obvious tips that can help novice programmers who decide to cut their pet project in a team of like-minded people.

  • If possible, do not reinvent the wheel.
    If the problem is widespread, then there should be many ready-made solutions to it. Finding a suitable ready-made solution may take less time than writing it yourself from scratch (especially if the problem is complex).
    . , . , , . NetworkStream-, TcpClient- Json. , , . 30 ( MagicOnion, )

  • — , . — . . — . — .

  • — , . - summary readme. .
  • ,
    . . — , — , , .
  • Git — ,
    , . .
  • ,
    . .
  • ,
    . , , , , . , , , .


It turned out to create a playable prototype in less than a year. Part of the active community has joined in the development or testing, so now at least a few matches take place almost every day. It’s really cool to see how people enjoy the game, in the creation of which I take an active part.

Personally, during the work on the project I pumped spoken English (as the community is international), gained experience working with Linux, programming and using Unity. Pet projects are cool.

Over time, we will definitely add training for new players, normal models and animations, an adequate menu, and a more convenient UI as a whole. Since good 3D models are very expensive, they created an account on Patreon, and the community began to support us financially.

Links for those interested in the idea
Discord-. .

, , - — , Atlas Reactor

All Articles