📜 ⬆️ ⬇️

How I participated in the competition of small games js13kGames



Programming contests are widely accepted and welcome to write post-amtems. No funeral: in fact, this essay on "What I learned while participating in the competition."
Contest of small games on javascript js13kGames is not an exception, and I would like to share with Habr the accumulated feelings of a novice igrodel.

(For those who want to play, but do not want to read revelations, here is a link to the game .)
')


To start the contest itself


Habrayuser printf in his post-mortem already described it, but for completeness I will repeat:
You need to write a game on js, which after packing in zip will take no more than 13 kilobytes (more precisely “kibibayt” will be, but not everyone is used to this unit). Of course, no resources connected from the outside.

By the way, it turned out that 13 kb in zip for lovers of code golf is just a huge amount of space. Looking ahead, I can say that out of 13 kb, I managed to use only a little more than 8, which is why I still have a very unpleasant sediment - the game is unfinished, it was necessary to try better, the place allows, me-me-me! ... But that was later, and now to the very beginning.

Month before the contest


Yes, the work began already then. But rather, research and coordination. I (Russia), Maxime Euziere (France) and Jim Herrero (USA) had previously worked together on small golf projects. Well, they decided to cut the game together.

Neither the format of the game, nor the subject is known yet. It is only known that the game will be zipped, and the ZIP can be compressed better or worse. Were sluggish tests of the effectiveness of various ways of archiving, on which platforms and in which unpackers they are supported, and how to write code under zip in general. A summary of the work done is on gist.github.com (in English) . In short: Deflate compression is normally supported.

And now a little about the fact that the report did not fall (but lay on the surface). With a deflate, you can turn to the full and attach zopfli ! I generally like to apply zopfli to different things. As it turned out, I didn’t even have to write my bikes to optimize the zip (although I wrote them essno) - there is a chic, ready-made AdvZip repacker. Only ts-ss!

So, we armed ourselves in advance waiting for the start of the competition and the announcement of the topic.

"Elements: earth, water, air and fire"


That was the theme. In skype began brainstorm. Various options were offered - from running with obstacles ( Turtles Can't Skate is strikingly similar in mechanics to what was discussed) to a game like Lemming ( again, we were not alone with this idea ). But in the end (on day 3 or 4) it was decided to do something like Bejeweled.

And then the seams: Maxim decided to quit his job and move to another city. He could not take an active part in writing the code, and it was assumed that he would help in golfing - reducing the size of the code. Which, by the way, was not even useful. But his thoughts, suggestions and algorithms helped me a lot, and he fully deserves the mention in “credits”. All the same, one head is good, but two is better. “Two” - because Jim dumped immediately after the brainstorm in an unknown direction for reasons unknown to me.

Managing programmers is the same as feeding cats. And if they are on different sides of the globe - here finally can pipets.

Development


So, the idea is simple: there are tiles on the board (tiles) that can be combined. We unite Water and Earth, we receive Dirt. We collect at a tile with dirt in a row, they disappear, and points are added. Further, the mud can not be combined again with water and earth, otherwise the number of options just rolls over, and the player is unlikely to figure out sometime in this table of alchemical elements.

Tiles


To begin with, we generate the tiles ourselves: a form, a light sandy Math.random() from Math.random() out. For each tile, this texture is created anew so that there is no “battery effect” when the eye subconsciously begins to find similar elements. By the way, tiles and favykon after each page load are slightly different.

This all turned out to be much easier to do by manipulating the raw canvas data: createImageData / putImageData . True, the corners of the tiles are not completely rounded - it was an early basting, when I did not know that I fit into an accessible place with a margin. But later it seemed to me that it was even more interesting.

From above it is drawn by Bezier curves of the icon, already by the forces and means of the API of the canvas.

From the resulting image is created ( new Image() ). It would be possible to leave it as a canvas, but it is easier to debug, and the operation of copying pixels into the canvas is faster.

The backdrop is also generated by procedural manipulation of raw pixels. The backdrop is a little bluish, and the tiles are yellowish, this is an old joke, widely used in pixel art: yellow seems closer. This backdrop is placed on the canvas element as a background in css: there is no need to re-render the whole thing manually, let the browser suffer. By the way, the Chrome debugger just suffered. Tip: do not put in the background-image huge images in the data:image/* , debugger hangs well, just indecent.

Cursor Coordinate Processing


Well, everything is simple. If you have ever written drag-n-drop manually, then everything is the same. And because of the extreme dolbouty of the browser zoo, the coordinates have to be calculated as "coordinates relative to the screen minus the coordinates of the upper left corner of the element." And this is in 2014.

The handling of mouse and finger events is basically the same. The only thing was that I had to silence Pointer Events so that everything worked on Windows Phone. Yes, Windows Phone, the game was originally designed for mobile devices, and a mobile device is not just an iPad.

Render and animation


In simple toys, usually redrawing usually occurs via setTimeout / setInterval . With this approach, the frame rate is low and almost constant, and the “physics” is processed frame by frame. Yes, in js the timers can slip under load on the event cycle, but this can be ignored.

But I decided to make it “like big boys”, and use requestAnimationFrame (where it is). And this put the process of rendering and animation upside down - the frame worked at all anytime and as often as you like from 60 times per second to a couple of times per hour. But gentlemen, this is cool. The results are much nicer.

The engine (if you can call it that) works as follows: there is an array of the playing field, “model”. When something in the model changes, it happens instantly, but an object defining the animation is put into the blockingAnimations array. Until all the animations have completed, requestAnimationFrame is spinning constantly. When completed, it checks whether the tiles can fall under the action of gravity, explode or disappear. If not - do not request requestAnimationFrame, there is no need to burn the processor without the need.

Here I want to make a small remark (if someone else is reading me): the game was calculated as casual, and the case when the user played and left it open in another tab is more than real. And in some old Opera under Windows 98, a permanent re-render in the next tab does not cause anything but the desire to close the game. Take care of your users, even if they are not users, but players!

During the blocking animations, the user can not do anything - so that he does not suddenly change the state of the model. It's simple.

Beta. Rather, Alpha


Then I matured to the first public test. Got a lot of feedback and suggestions.

The first request is to enter points as soon as possible. That was done.

The second is to make all animations non-blocking. Then I had to refuse, I am not a magician, I just learn.

But the main thing that struck me was playing the game! Seriously! I gave the link to two people, and played five. It inspired me not to quit.

Added points, fixed terrible bugs when drawing. It's beta time. I spent it honestly - in people who had not seen it before. And they all said they did not understand what to do. I had to file "how to play" (which, it seems, was unintelligible). This in turn added bugs when drawing. In short, working out as it is.

But even after “how to play” there were questions: explanations in English! I had to go for a decisive step - localization. As a result, the game has three languages ​​- English, Russian and French.

Mobile devices


What was “originally laid” is time to pull out. With the code, everything is gorgeous, but with the display - not very.

First of all, meta-viewport. This bastard does not work. Rather, it works, but not at all as it should. To say with it that the eran should display an area of ​​at least 750 × 770 is possible, but no browser will do this. I had to specify width=device-width,height=device-height , and emulate it all via CSS transform (where it is supported).

And here I was pleased with the iPad - it turns out that document.documentElement.clientHeight / .clientWidth there is always 1024. That, you see, is absolutely useless. I had to tuck crutches specifically for the iPhone / iPad and determine through window.innerHeight .

Again, another problem has surfaced - while reducing the canvas, everything looks cool, but when you increase ... ghm ... at least, so-so. Everything is blurred and careless. Another crutch, and the increase is made only with a factor of a multiple of 2 (decrease - with any). And a note for the future: fixed-size canvass is the way to nowhere.

Sounds


A few days left. That time was already the limiting factor, not size. What if you add sounds?

I did not want to take the finished lib. Unfortunately, I am not able to do a trekker, and I am not particularly capable of writing music. And why background music on a web page?

Sounds are another matter. As a basis, I took the wav generator from p01 . So that the sounds were not like Dandy or special effects from Sci-Fi of the eighties, I modified it to 16 bits and 44100 Hz. And he began to experiment.

For the timbre to be natural, I used harmonics . It turned out to be boring, so for the amplitude of the overtones, I began to introduce hellish coefficients - sines, logarithms, exponents, and watch what comes of it. There were funny things - from deaf clicks to the bell. And sometimes this .

The result was something more or less adequate, and I stuck it into the game. According to the style, sounds are not very well combined with sand-stone graphics, but I confess honestly - to choose from tons of sounds that are suitable, you need a lot of patience, and most importantly, turn off the player. And as such a music lover, I just could not make such sacrifices.

Total


You know, gentlemen, indie-igrodel is interesting! As a result, a month spent with interest, a lot of experience (and basically the format “had to be done differently”) and, most importantly, a working game.

Here it is, by the way: Quintessence on js13kGames .

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


All Articles