How I participated in the js13kGames little games contest
In programming competitions, post-mortem writing is widely accepted and encouraged. No funeral: in fact, this is an essay on the topic "What I learned by participating in the competition."
(For those who want to play, but do not want to read revelations, here is a link to the game .)
To start about the competition itself
The printf habruiser in his post-mortem already described it, but for the sake of completeness I will repeat:
I need to write a game on js, which, after packing in zip, will occupy no more than 13 kilobytes (more precisely, it will be “kibibytes”, but not everyone is still 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. Getting ahead of myself I say that out of 13 kb was able to use only a little more than 8 why I was very nepryatno sediment - game left unfinished, it was necessary to try to better place somehow allows, Me-me-me! .. . But that was later, and now to the very beginning.
A month before the contest
Yes, work began already then. But rather, research and coordination. I (Russia), Maxime Euziere (France) and Jim Herrero (USA) previously worked together on small golf projects. Well, we decided to cut the game together.
So far, neither the format of the game, nor even the subject matter is known. It is only known that the game will be zipped, and ZIP can be compressed better or worse. Slow current tests of the effectiveness of various archiving methods were carried out, on which platforms and in which unpackers they are supported, and how to write code for zip in general. A brief progress report is available at gist.github.com (in English) . In short: only Deflate compression is normally supported.
And now a little about what did not get into the report (but lay on the surface). With deflate, you can turn around 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 own bicycles to optimize zip (although I wrote them essno) - there is a smart ready-made repacker AdvZip. Only ts-ss!
So, arming ourselves in advance, we began to wait for the start of the competition and announcement of the topic.
“Elements: earth, water, air and fire”
That is what the theme read. Brainstorm started on skype. 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 here is the seam: 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 outlines of the algorithms helped me a lot, and he fully deserved the mention of “credits”. All the same, one head is good, and two is better. “Two” - because Jim dumped right after the brainstorm in an unknown direction for reasons unknown to me.
Managing programmers is like grazing cats. And if they are on different sides of the globe - then finally pipets.
So, the idea is simple: on the board there are tiles (tiles) that can be combined. Combine Water and Earth, get Dirt. When collecting tiles with dirt in a row, they disappear, and points are awarded. Further, the dirt can not be combined once again with water and earth, otherwise the number of options just went through the roof, and the player is unlikely to ever figure out this table of alchemical elements.
To begin with, we generate the tiles themselves: shape, convex and lightweight sandy texture from
Math.random(). For each tile, this texture is recreated so that there is no “battery effect” when the eye subconsciously begins to find similar elements. By the way, the tiles and the favicon are slightly different after each page load.
It all turned out to be much easier to do by manipulating the raw canvas data:
putImageData. True, the corners of the tiles are not quite rounded - this was an early outline when I still 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.
Bezier curves of the icon are drawn from above, already by the forces and means of the canvas API.
From the resulting image is created (
new Image()) It could be left in the form of canvas, but debugging is easier, and the operation of copying pixels to 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 set to the canvas element as a background in css: there is no need to manually re-render the whole thing, let the browser suffer. By the way, the Chrome debugger just suffered. Tip: do not put hefty pictures in the background-image
data:image/*, the debugger hangs well, just indecently.
Handling cursor coordinates
Well, everything is simple. If you ever wrote drag and drop manually, then everything is the same. And because of the extreme fucking up 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 processing of mouse and finger events is basically the same. The only thing was that I had to drown Pointer Events so that everything worked in Windows Phone. Yes, yes, Windows Phone, the game was originally designed for mobile devices, and a mobile device is not only an iPad.
Render and animation
In unpretentious toys, redrawing usually occurs on
setInterval. With this approach, the frame rate is low and almost constant, and the "physics" is processed frame by frame. Yes, in js timers can slip when loading on the event loop, but this can be neglected.
But I decided to make it "like big boys", and use it
requestAnimationFrame(where it is). And this put the rendering and animation process upside down - the frame worked at all whenever and anytime often 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, the “model”. When something in the model changes, it happens instantly, but into an array
blockingAnimationsan object that defines the animation is placed. Until all animations have completed, requestAnimationFrame spins continuously. When finished, a check is made to see if the tiles can fall under the influence of gravity, explode or disappear. If not, we do not request requestAnimationFrame, there is no need to burn the processor unnecessarily.
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 renderer in an adjacent tab causes nothing but the desire to close the game. Take care of your users, even if they are not users, but players!
During blocking animations, the user can not do anything - so that he does not suddenly change the state of the model. Everything is simple.
Beta. Or rather, Alpha
Then I matured for the first public test. Received a lot of reviews 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’m not a magician, I’m just learning.
But the main thing that struck me was playing the game! Seriously! I gave the link to two people, and five played. It inspired me not to quit what was started.
Points added, scary bugs fixed during rendering. It’s time for beta. I conducted it honestly - on people who had not seen her before. And they all said that they did not understand what to do. I had to gag "how to play" (which, it seems, was slurred). This in turn added bugs when rendering. In short, development as it is.
But even after “how to play” there were questions: explanations in English! I had to take a decisive step - localization. As a result, the game has three languages - English, Russian and French.
What was "originally laid down" it was time to pull out. Everything is gorgeous with the code, but not with the display.
First of all, meta-viewport. This bastard is not working. Rather, it works, but not at all as it should. It is possible to say with the help of it that an area of at least 750 × 770 can be displayed on the screen, but no browser will do this. I had to point
width=device-width,height=device-heightout and emulate all this through CSS transform (where it is supported).
And here the iPad pleased - it turns out,
.clientWidththere are always equal to 1024. That, you see, is absolutely useless. I had to poke crutches specifically for the iPhone / iPad and determine through
Again, another problem surfaced - with a decrease in canvas, everything looks cool, but with an increase in ... ghm ... at least so-so. Everything is messy and messy. Another crutch, and the increase is made only with a factor of 2 (reduction - with any). And a note for the future: fixed-size canvas is the way to go.
A few days left. That time was already a limiting factor, not size. What if you add sounds?
I did not want to take the finished lib. Unfortunately, I don’t know the tracker, and I'm not particularly capable of writing music. Yes, 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 did not look like Dandy or special effects from Sci-Fi of the eighties, he modified it to 16 bits and 44100 Hz. And he began to experiment.
To make the tone more natural, I used harmonics . It turned out boring, so for the amplitude of the overtones I began to introduce hellish coefficients - sines, logarithms, exponents, and see what came of it. Funny things came out - from deaf clicks to a bell. And sometimeshere it is .
As a result, something more or less adequate turned out, and I stuck it in the game. In terms of style, sounds are not very combined with sand-stone graphics, but I admit honestly - to choose from tons of sounds suitable, you need a lot of patience, and most importantly, turn off the player. And I simply could not make such sacrifices as a music lover.
And you know, gentlemen, an indie igrodel is interesting! As a result - a month spent with interest, a lot of experience (and mostly of the “had to do it differently” format) and, most importantly, a working game.
Here it is, by the way: Quintessence on js13kGames .