📜 ⬆️ ⬇️

The tragicomedy in NaN acts: how we made the game on JS and released it on Steam

“Eka Nepal,” you say, “In your top-100, there is no game, so it's not protected.” Also true. But over the year the development of Protolife, we have accumulated some experience that we can share with potential future igrodelami. Veterans of the industry, I am afraid, will not find anything interesting for themselves. But maybe, at least have fun from the heart.


What kind of game? And who are “we”?


We are a team of three people ( GRaAL , A333 , icxon ), by the will of fate called the Volcanic Giraffe without any intention. We worked together for a long time, three of us participated in Ludum Dare several times (writing contests over the weekend), and once decided to bring one of our handicrafts called Protolife to release.

In short: this is an unusual tower defense, where you have to run as a hero-cursor and build a block defense against the constantly growing red biomass.

From the comments to the draft article:
icxon : you need to write at least a little about the gameplay. And then some screenshots on which the hell understand what is happening

If you paint in more detail, what is in the game:
')
  1. There is a set of levels, at each level there is our base and our avatar is a robot builder.
  2. Part of the level is filled with red growing biomass that spews tons of mobs out of you.
  3. Mobs, clearly a matter of, run to the base and try to crush it. And we are building a defense of the towers and do not give it.

“But what’s so unusual?” You ask. And the main differences from the majority of tower defense are as follows:

  1. We manage the construction robot directly from the keys, i.e. It is necessary to manually worn on the level and have time to build everything / repair.
  2. All that a robot can do is build and dismantle blue blocks. One such block does nothing useful, but a few blocks laid out on a specific pattern turn into a useful structure. Template examples:



And so the game passes: we run, build, get the answer, and quickly repair it. Directly visualization of the game development process is obtained.

But this game was not immediately. Back in April 2017, on Ludum Dare 38, it looked like this:

If you do not pay attention to the difference in the graph, then it seems that not much has changed. Already on the LD version there was a cursor builder, the construction of block towers and a standoff with red biomass. We left it because it worked and people liked it.

But in fact, a lot has changed under the hood besides the concentration of graphonium per square pixel. On a year-long journey, we had to make various decisions — gameplay, technical, organizational, and even marketing. Some were successful, some were not.

Here I would like to tell about such decisions.

Immediately make a reservation - there will probably be several articles, because material, if you start to dig, gets out a lot. At the end of this article you will find a short survey - which topics you would be most interested in. Well, links to the game itself, too.

A boring story of creation.


I don’t think that it’s really interesting to someone how the game came up with at all, so I’ll remove it under the spoiler.

Somehow Conway, Matheson and Petri go to a bar ...
And the bartender says: undefined is not a function.

On the eve of LD38, it turned out terrible: our colleague and the only artist A333 will fly the whole LD over the Atlantic, and will not be able to help us. Therefore, it was necessary for the remaining forces to make the game as undemanding as possible.

The topic by the way was “Small world”. And then during the brainstorm, everything went like this:
  • No artist - simple graphics, primitive
  • Small world - small size, or maybe something microscopic, such as any microbes
  • Microbes are a type of micro-life.
  • And life is such a cellular automaton. Well, the cells in every sense. Let the player fight a cellular automaton against another cellular automaton.
  • Poked Conway Life - no good. A player who is not familiar with the rules of the machine is likely to build something that will be destroyed by itself. Very difficult to control. According to the rules of life, let's be only the enemy, and we will build orderly structures.
  • ... but then the enemy will self-destruct every now and then. Okay, correct the rules for him. Let it only grows, and we must destroy it.
  • So, we already have: the red cells of the enemy, which are only growing, and our blue ones, which we build ourselves according to predetermined patterns. We will build towers and walls, i.e. It turns out such a tower defense. For diversity, there are not enough moving enemies (mobs).
  • The day before I re-read once again “I am a Legend” by Matheson. So the main character at night keeps the siege from vampires, and in the daytime, when vampires are inactive, he restores defense, as well as expands the sphere of influence. It sounded like a good gameplay element, so the phases of day and night appeared in the game. During the night, enemy cells shared and lit up houses, crawling mobs, in the afternoon - everything was quiet, it was possible to counterattack.
  • We call our color pixels “microorganisms” and shove them into a circular arena - the Petri dish.
  • Take our favorite engine Phaser.js ...
  • ... and 31 places in our pocket

This is a story that is not very interesting, as I warned.

“But Alexey, why the hell did you write it then?”. Reasonable question. The fact is that almost the first comment on the game sounded like “lol, you all flayed with Creeper World”. After reviewing the game much later, the actual LD, I understand why people think so. But still a little offensive.

If suddenly you also felt hurt that you spent the time on this nagging, then as a consolation, write to me in a personal code word “boring things”, and I will send you one of 10 keys, if by the time they are not over. Unfortunately, the keys are also over.

Choosing a game for the full implementation


As is often the case, once we just thought all three of us - but is it not time to try to make a full game? Often at this stage, some kind of concept is taken from the head, a la “dream game,” and work is carried out with it. And then how lucky to guess the desires of the audience.

Our task was simplified a bit by having a small “portfolio” on Ludum Dare. We saw how people react to different games, and could compare. Protolife had the greatest response, it was praised for originality and for interesting gameplay - this is at the zero level of graphics and only 4 levels!

In addition, itch.io helped us to make a decision, where we published our crafts. As it turned out, there are people who go to itch.io to play web games. Some of these people love the tower defense genre, and 5-10 people came in (and still come in!) To play that old Protolife every day .
Statistics of visits to the release of the game on Steam

You could say it was our first marketing research. We thought and decided that a “non-standard tower defense” may well fire. Tower defense is not so much, many of them are similar as two drops of water, and we can stand out among them.

Looking ahead, I can say that the tactic has justified itself.

Unsolicited Tip # 1: Better not act blindly. The ideal “dream game” in your head may be useless to no one. If we do not participate in any jams, there is always a sense to jot down a gameplay (!) Prototype and test it on yourself, friends and acquaintances.

Counter-Council: If you are not afraid that one and a half people will play the “dream game”, then who cares? Do it! Maybe the same luck.

Engine: Not the best solution


The version on Ludum Dare we did on the Phaser.js engine. We know it well, we know javascript well, web games get more feedback, it is quite convenient and easy to learn - a fairy tale, but not the engine.

And we faced an important question: change the engine or leave everything as it is?

The question was difficult. None of us knew any other engine and did not study at that time. Spending time studying is a good thing, but it was also possible to lose all enthusiasm. And then - what to take? Javascript is the only language that each of us knew, to take the C ++ / Java / C # engine is to instantly lose half of the developers. And the Unity level engines at that time seemed too cumbersome for a “simple 2D game”.

And then: here is the game. It remains to update the graphene, finish the levels - that's all. Work for a couple of months. And then there's study, rewrite ...

In general, we decided to stay on Phaser.js. Even worse, we decided to stay on the same code base, i.e. Build a game on top of the Ludum Dare prototype.

From the comments to the draft article
a333 : It's a pity, I didn't have that picture with a crutch instead of the Eiffel Tower ”

Unsolicited advice number 2: never do that! This is especially true of re-use of the prototype. The code on jams is always written quickly and dirty, without taking into account future development. There is a crutch, a crutch here - and now you understand that you are writing the legacy code right away, and instantly you start to suffer from it. Prototypes must be carefully read, and then burned in / dev / null and write again, only already clean and clean.

Counter-Council: consider, however, the peculiarities of psychology. It happens that the delay in a couple of weeks is enough to “cool off”. Better to make a game with bad architecture than not to do at all.

From the comments to the draft article:
A333 : I'm very inclined to this option. As far as I know, this is exactly the way most games are done, because time and fuse play a key role. If you have the opportunity to rewrite everything completely on a new engine, this is good, but this is not always the case. Do not be afraid to write quickly and dirty, roll out early prototypes, copy and paste pieces of code, and release zabago ... * sounds of blows and muffled moans *

Actually, what's the problem with Phaser.js


We have solved many of these problems along the way, some have not decided until the end. It is difficult to judge without experience with other engines, but it seems to me that any other engine would allow to process thousands of game objects without hellish tricks. However, I may be wrong.

War of styles


Do you still remember the “original design” of the original version of the game?


Of course, he has his charm, but for a serious product did not fit. Yes, and our artist just returned from a business trip, what is he to sit idle about?

He did not sit, making a few sketches of a possible design. History has retained two style chairs between which one had to make a choice.

Candidate 1: minimalistic design. In fact - the same "pixels", only prettier. Everything is bright, contrasting. It looks decent, but, let's say, undignified. In the sense that the game that looks like this will look good on mobile phones or VKontakte somewhere between Tetris and Arkanoid. But quickly and cheaply.


Candidate 2: realistic, fantastic, austere. Here you can already see a certain story, context. Unexpectedly, the contrast of not only colors, but also forms works well - our buildings are more ordered and square, the enemy buildings are round, irregular in shape.



This is not to say that we hesitated for a very long time. We imagined which of the “screenshots” we would like to play more, and the first candidate simply had no chance to survive. We had no idea what we would have behind the plot, where it all happens, what happens - but we chose option two.

Of course, there is nothing to compare with, but it seems to me that this was a good option. Yes, we would finish the first option faster, it would be less demanding on performance and graphics, and probably it would even be possible to port it to mobile phones.

But we wanted to play the second option.

So microorganisms and Petri dishes were replaced by a distant planet, robots, and a mysterious alien organism.

Unsolicited banal advice number 3: do what you want to play yourself.

Counter-Council: if you have 3 mortgages and no other work, no one will blame you for doing something that sells well, but what you want to do is wash your eyes and hands with soap.

Crouching biomass, hidden worm


I mentioned the contrast of colors and shapes, and if you compare that sketch and the final version - the contrast only intensified. Compare - the squares of our buildings, round enemy buildings, and the total roundness of the red mass.


By the way, the red mass, like almost everything in the game, somewhere inside the game logic fits into the grid in the same way as the blocks. But at the same time it looks chaotic, as if it is untied from the grid.

I have a demo where I debug the appearance of biomass. There everything is arranged in the following way: a conditional “square” is taken and filled with circles of different sizes as closely as possible. Below is an example of such a filling. In the game itself, the filling is more dense (and therefore readable worse).

There are lines between the circles - connections. If the biomass needs to grow into some cells, then suitable circles intersecting with the desired cells are selected, and growth occurs in them along the lines.


Here’s how it looks in motion:


From the comments to the draft article:
icxon : I see this demo for the first time, lol
In the game itself, everything works exactly the same, except that all sizes, colors and speeds are fitted by our artist to look better.


Oh, I think I forgot to tell where exactly this decision came from, how the discussion stages went, the candidates were dismissed ...

But no, I did not forget. They were not there. Initially, we planned to do everything as in the LD version, i.e. growth in squares. Just one evening I got bored, and I sketched this demo to practice. And the guys went. So did.

Unsolicited Tip number 4: plans, plans, but do not be afraid to try something new. Intuition can tell you some good solution.

Counter-Council: if you have already set a release date and signed a contract with the publisher - perhaps you should not experiment.

Biomass animation


On the gifs above, you could notice the idle-animation of biomass — even at rest, it “breathes” vigorously, the red buds seem to open and close back. In an ideal world, it would be carefully drawn by the artist with animation sprites, which are arranged in the same pattern with circles. In reality, it would be thousands of game objects that Phaser.js simply could not handle. And this is already a proven fact - in the version with Ludum Dare I already came across hellish brakes when biomass filled at least half of the map, and there wasn’t even any idle animation there.

A shader comes to the rescue, which spins on the GPU there and does not occupy the processor. It is even better if this shader is really simple - then it will not load the video card too much.

The shader needs to somehow talk about what and how to draw it. What are the ways to transfer information to the shader:


Method 3 could be useful to us, but in the case of Phaser.js it is not available to us. You can’t convey a lot through uniform (say, an array of all circles with their radii in uniform will not fit - there are limitations). Texture remains.

The trick is: I first paint one state (say, closed buds) in blue:


Then the second state (open buds) is red:


If you add them up, you get a purple mess:


The shader sees the texture, sees the current time, and with a certain period shows us the “blue” state, then “red”, smoothly flowing between them. Well, by itself, applying the desired color palette. It turns out like this:


From the comments to the draft article:
a333 : Oh, here's how this b *** i f *** and works
icxon : this is great because I still haven't understood how it works
The same, but using the example of rectangles:

Texture:

Final animation:

The texture is updated only as it grows / collapses, gpu-only animation works the rest of the time.

Player care


In the development process, we tried not to forget about the players who see our game for the first time. It's not as easy as it seems - after a while the eye becomes blurred, and you begin to consider everything that you, as a developer, know, is obvious.

We understood this, and therefore the first beta test was carried out as much as 8 months before the release - as soon as we had the first 10 levels ready and 40-50% of the content. That beta test gave excellent feedback on level design, which I will talk about sometime next time. At the same time, we learned that we ourselves quite well forecasted some moments.

Situation: there is a player's base that must be defended - its destruction leads to the failure of the mission. The player does not follow the base all the time - he watches his robot avatar. And it follows constantly - the pace of the game is quite fast, there is not much time to stand and contemplate. As a result, we, the testers, sometimes did not notice the enemy who came to the rear, bombing the base.

Solution: first, we will show the damage on the base with concentric circles running through the half-card.


Secondly, if the base is damaged significantly, then it independently strikes back, sweeping away the enemies, which is well marked and sounded. This draws the player's attention to the problem, and gives time to take action.


Situation: as in a typical Tower defense, the enemies go the beaten path. However, this route is not always guessed. And to understand the route is very important for successful defense: some towers shoot very close, and a tower that is too close will be demolished by the next wave.

Solution: draw blood after killing enemies. After some time, a clear trace appears on the ground, which can be oriented:


Question: and, actually, why the trodden route? Are you not able to implement A *?

Answer: implemented a bunch of times :) we honestly experimented with enemy AI. We had smart worm-seekers and slimes dodging bullets. It became impossible to play. The player could not build an effective defense and could not rejoice, watching how it works. The fun of the game plummeted. This does not mean that “smart” enemies are bad. Just for the chosen mechanics - when our buildings are static - such enemies did not fit. For “smart enemies” you need to either attach a machine gun to a robot, or legs - to towers. And this is a completely different game - not one that people liked on LD and itchio.
From the comments to the draft article:
icxon : But there are still slimes. And they still dodge
GRaAL : think a bit of fiction
They really are, but they dodge much more lazy than in the original version

Situation: the enemy has long-range guns (mortars), which may be located outside the visibility of the player. At the very beginning, almost the entire field was hidden behind the fog of war, because of which the arrival of enemy shells could go unnoticed - they flew out of the darkness and immediately inflicted damage. If the player looked at this moment in a different direction, he could never understand what had happened.

Solution: flying projectile can be seen above the fog of war. This gives the player the opportunity to notice the threat and take action.


By the way about the shells. Distract from the gameplay-UX problems.

Collision handling


There were not so many moving objects in the LD version - a dozen bullets and a dozen worms at any one time. In the game this was not enough. In order to feel the challenge, you had to increase the number of opponents, and at the same time give the player a rapid-fire weapon to counter. So the situation in the game is not uncommon:


For all game objects it is necessary to consider collisions. Bullets crash into worms, worms crash into blocks, and all of them on the screen are sometimes several hundred. And the guns should choose their goals in range. If in the LD version we could afford to iterate over all enemies in search of suitable ones, here it was already beginning to slow down.

In general, this problem has solutions, on Habré, I have repeatedly met articles on this topic (here’s one of them ). Since we have a logical grid on the screen, and most of the active objects are no larger than 2x2 game cells, we decided to use this grid to determine collisions.

Each cell is associated with a list of objects that are in this cell. There are static objects (such as blocks or stones) that occupy exactly one cell entirely and do not move anywhere. And there are dynamic ones that move along the grid, “flowing” from one cell to another.


Since the object can be somewhere on the border of two cells (although its center clearly lies within only one), then when considering collisions, all neighboring cells are examined. Those. we take a bullet with coordinates X, Y, and see what lies in the cells (X, Y), (X + 1, Y), (X-1, Y), (X, Y + 1), (X, Y -one). If there are objects with which the pellet can interact, then already for each of them a collision is accurately calculated based on the shape and size.

In order not to get up twice, the same grid is used to select goals for the towers. The tower after creation “subscribes” to certain cells of the grid. If something is hit in the cage that can be shot down, the tower receives a notification (and usually shoots after that). Thus, if a thousand enemies are crawling obviously far from the tower, the tower will not spend time counting the distance to them.


If the map is large, then there are too many cells in the grid, and all updates and checks start to take longer. Also, problems start if there are any buildings / units the size of several cells - for them you have to check more cells around.

To level these moments, we have a large, 16x16 cells on top of a fine grid. She considers everything to be the same, and she is used for quick screening of situations where there is nothing around a few cells around and there is no need to check for collisions.

Promised links and survey


The article turned out to be quite long, and my list of topics in the notebook has decreased, God forbid, by a quarter. Tell me what you would like to hear about in the next article? You can vote for a few points.

Well, if suddenly there are any specific questions about how it's done, or why it's done - write in the comments. Answer if you remember :)

Links to the game:


Thank you all for your attention.

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


All Articles