Post-mortem

Unexpectedly enough, I participated in another rapid gamejam. This time, it was Alakajam! (mind the !), a 48-hour event held over the September 22nd – 24th weekend. I participated in the Solo division and enjoyed the experience thoroughly.

AKJ (the 'official' abbreviation for the event) is a completely new gamejam, this weekend being its first iteration. Some people think of it as a reaction to the terrible management of LD, which is probably not entirely untrue for many participants, including me. But, this is not a post about that, and in any case, AKJ already has a great community and is very interesting in its own right.

Theme

Unlike LD, AKJ is more suited for us, jammers based in European timezone. There was a lot of people eagerly awaiting the theme announcement in the IRC channel. Around 8pm, the theme Space Station was announced with a video. Due to votes in the last hour, the actual theme chosen was Alchemy, however. This was communicated quickly enough, but some people went through with their Space Station games, and then probably submitted in the unranked division.

I was happy with both of the themes, Alchemy was my number one choice, Space Station number two. As the theme voting results reveal, people finally got tired of 'You are the bad guy' and its variation, which LD has seen plenty of.

Initial idea

My initial idea was something along these lines: The player is an alchemist, living alone in a desolate world. However, they can create and mix interesting potions. Using a special looking glass instrument, they can look into their potions and even enter them, finding an entire world inside. Depending on the elements / substances / characteristics of the potion, the world would be different – different terrain, objects, features, people, creatures, etc. (Thinking about it now, this idea seems a bit similar to the linking books in Myst.) The game would then consist of the player entering various worlds and solving problems therein by mixing them with other worlds.

I was aware that this was pretty ambitious for 48 hours, but I wanted to take my game into this direction, even if I did not make it as far. So I started, hoping to waste less time on overthinking my idea. Once again, in gamejams and indie games in general, I find it very important to not be afraid to change directions, even drastically, based on what is fun to develop, what seems feasible, whatever random ideas you find along the way, and so forth.

The journal

Here is the first page of my journal for AKJ:

A lot of it is sketches for page-flipping curves. One of the first things I was actually implementing was the neat page-flipping animation for the (in-game) journal. Here is how it works:

A basic page flip consists of a page moving 180 degrees from the left or the right to the opposite direction. Usually a person would start turning at the corner, creating a curve with an axis not identical to the axis of the book spine. For simplicity, however, I assumed the page is made out of individual 'columns' of pixels – like a bamboo mat. This way, we can model the page flip as a 2D curve animation (if looking at the book from the bottom).

An animation consists of moving the columns along a half-circle – at each point in time ($t \in [0; 1]$), each column ($x \in [0; 1]$ radii from the centre), is at a specific angular displacement ($a \in [0; \pi]$) from its starting point. We can say the angular displacement of any column is a function of its distance from the centre and the time – $a(x, t) \in [0; \pi]$.

A basic assumption to consider – the page will begin flat and will end up flat as well. So, $\forall x: a(x, 0) = 0$ and $\forall x: a(x, 1) = \pi$.

If the page was completely rigid, as is the case for a normal book cover, it would simply be a solid line tracing the radius of the half circle. In other words, $\forall x, t: a(x, t) = t \pi$.

Normal pages bend a little bit along the way though. For thinking about this, I imagined 4 points on the page curve (the one seen from the bottom). One quarter of the way along the page, one half, three quarters, and finally the tip of the page. Any points will essentially be interpolated. How do the four points behave? Parts of the page should lag behind, others should go faster than the 'rigid' cover. I imagined the halfway bit to stay exactly on the rigid cover curve, moving at a constant pace from beginning to end: $\forall t: a(0.5, t) = t \pi$. The point closer to the spine will go faster, points further from the spine should lag behind: $\forall t, \forall x < 0.5: a(x, t) > t \pi$ and $\forall t, \forall x > 0.5: a(x, t) < t \pi$ This gives it a nice progression.

To obtain this sort of function, I started with the basic linear one (rigid cover), then added a little bit of a quadratic: I've noticed $y = x^2$ is almost perfect for the lagging bits on $x \in [0; 1]$ – it develops slower, but at $x = 1$ it catches up to the linear formula. I thought it would be nice to be able to get the same curve with the opposite bias. On a graphing tool I came up with $y = (1 - t)x + tx^2$ – with $t \in [0; 1]$ this is essentially a linear interpolation between the quadratic and the linear formula. Even better – negative values of $t$ produce the opposite trend as expected.

And so, here is the final formula I used:

\begin{align*}x &\in [0; 1]\\ t &\in [0; 1]\\ a(x, t) &= \pi \left((1-t)(x * 2 - 1) + t(x * 2 - 1)^2\right)\end{align*}

And a visual representation of the full animation, with the four points highlighted in red:

Note that the point at the spine would have a curve similar to point 4, except with the opposite trend.

To decide which side of the page has to be rendered, it suffices to know whether the last column drawn was to the left or to the right of the current one. The page shading is a bit more messy, but it is basically calculating an offset in the page colour palette based on the distance between $\frac{\pi}{2}$ the half-way point and the current angle. I added some Bayer dithering to make it pretty, of course.

The animations led to some minor performance problems towards the very end of the 48 hours – the quest and recipe updates were not added until quite late, and as I was running out of time, I decided to rerender the full 40 frames of a page flip (20 each way) every single time that page changed. All at once! Post-jam I fixed this so the frames are rendered one at a time in the background, or in the worst case, just when they are needed on screen. Some people complained of their browsers messing up a lot and the audio freezing during the lag spikes, so I thought it was an acceptable fix.

Alchemy

Mostly happy with my journal but with little else to show, the first night was here. I started thinking about the gameplay and actual puzzle design. Some basic Wikipedia research led me to the seven planetary metals:

• Lead, dominated by Saturn ♄
• Tin, dominated by Jupiter ♃
• Iron, dominated by Mars ♂
• Gold, dominated by Sol ☉
• Copper, dominated by Venus ♀
• Mercury, (quicksilver) dominated by Mercury ☿
• Silver, dominated by Luna ☽

I thought this was a good place to start. If the seven metals had characteristics (primary, secondary, and tertiary), maybe mixing metals would result in substances with the primary characteristics kept? Maybe some characteristics eliminate one another? So I thought of seven vaguely appropriate characteristics and arranged them with the elements, until each metal had a unique primary characteristic, and each characteristic appeared exactly three times:

• Gold (Sun) – heavy, shiny, slow
• Iron (Mars) – slow, electric, deadly
• Mercury (Mercury) – deadly, soft, heavy
• Lead (Saturn) – soft, slow, heavy
• Tin (Jupiter) – light, shiny, electric
• Copper (Venus) – electric, light, soft
• Silver (Luna) – shiny, deadly, light

Clearly not all of them made sense, but this was alchemy in a jam game, so I was happy with this. To make the inconsistencies less obvious, I gave the characteristics pseudolatin codenames, and made a symbol for each (see previous journal page). I arranged them in a heptagram {7/3}, each characteristic having two vaguely-fitting opposites. Various tools / methods could then 'bring out' a characteristic (by removing its two opposites).

I also gave up on the idea of potions – substances with characteristics seemed a bit easier, since I was happy to draw pretty icons for items, not so much trying to make potions / flasks look unique enough depending on their elements. With this, I worked out a list of steps the player would have to follow to ultimately obtain the Philosopher's Stone, starting with just a couple of substances. Finding pure (containing a single characteristic only) substances would allow the player to fix tools, usually the tool two steps down on the heptagram.

The list was somewhat tedious to create, since I was worried that players could skip steps by using the right tools. At 08:00 in the morning, I was mostly happy with the steps, had the items (20 at the time) all drawn, and decided it was time to get some sleep!

I slept for 3 hours …

All fresh and full of energy (ugh), I started my second day.

I sketched a hub world in my journal with various features, then started recreating it in pixels. Happy as I was with how it looked, I realised it will take some work to make it work – pathfinding, objects in front of the player, interactive objects, and so forth.

Somewhat unhappy that I did not have a better (custom) tool for this, I resorted to having a number of layers in Photoshop: the graphics, the walkmap, the interaction map, the overlay map.

The graphics layer is self-explanatory, I hope. It may be worth mentioning that at this time my palette was finalised, with 19 colours total – most for the journal shading. I reused them for the terrain, and that seemed to work.

The walkmap layer was mostly transparent, except for single coloured pixels at places where the player can stand. I differentiated three basic colours – green for 'normal', yellow for 'transition', and orange for 'behind'. Whenever the player is standing on one of the latter two, the overlay map is rendered. This was a surprisingly painless way to give a 3D aspect to the map. For finding out which walkmap node is connected to which, it was enough to ensure that their distance was below a certain threshold (roughly 17 pixels, with some scaling of x axis). Green nodes cannot connect to orange ones. And finally, A* to find paths. Yet again, surprisingly painless. Some pixels had colours like 0xFF111111 (a very dark gray), 0xFF222222, etc – these serves as markers, their true type was hardcoded. Very ugly, but it worked and allowed me to refer to certain places in the map by a number.

The interaction map was the least elegant solution. There are pure black areas over objects the player can interact with. To identify them in code, I placed a marker pixel somewhere inside the area, and to the bottom and right of it a pattern, usually of 'letters'. In code I duplicated these patterns manually and this is how objects were paired with their scripted interactions.

The desk was mostly the same, except that various objects had to be separable from the graphic, because they had multiple states (i.e. the magnet could be broken or fixed). In the interaction map I placed markers with a pattern at the top-left of the object bounding boxes. The same pattern was then somewhere else in the bitmap (not visible), followed by the sprites for that particular object.

In the future I would very much like to be able to create 'tagged' image files (maybe using PNG metadata?) without having to do all of this during the jam. This would help tremendously when creating GUIs, frame-by-frame animations, tilesets, spritesheets, etc – I will see about integrating it into plustd.

The second night started and I was putting the alchemist's desk / workspace together for a lot of it. I made the crank work. Making the tip of the crank follow the mouse with some momentum was not happening easily with the amount of energy I had (none).

For anyone interested, the angle between the center of the crank and the mouse is calculated using $atan_2(y_\text{crank} - y_\text{mouse}, x_\text{crank} - x_\text{mouse})$. This 'target' angle, as well as the current angle of the crank are normalised to $[0; 2\pi]$. If the difference between the target angle and the current angle is more than $\pi$ (180 degrees), we need to turn in the opposite direction.

Final day

After the crank, it was the morning of the third and final day. I was drawing the Looking Glass (the blue device you see on screen when warping). At some point I realised I was not fully aware of what I was drawing – the device ended up looking weird enough for it, so I cannot complain. Morning is always the critical point for me when doing any long stretch of work without sleep. But, seeing as AKJ was ending in about 12 hours, I decided to keep going. In a previous LD, this has not been a good decision, but this time it worked out somehow.

I was also entering panic mode. I had the hub world and the desk, but I had not tested any of the puzzle progression, there was no story to speak of, I wanted to have multiple worlds to warp into, I needed an ending, more music (I have played the piano and transcribed a melody or two into the game at some point), and so on …

So I tried to make it quick – neither the Mars nor the Luna worlds are big, and the Mercury 'world' is … not really a world. I put all the important plot exposition in the final dialogue of the game, but oh well. I had a game that was playable from beginning to end. During the submission hour I packaged the game, added a simple title card, and waited until the AKJ website wasn't crashing due to people submitting all at once.

Conclusion

And … that was it! I made a game. Post-jam I added some prettified instructions to the submission page about the characteristics and the heptagram. It was overall a very satisfying experience for me, despite all the flaws in the game that I am aware of, some of which are:

• The alchemy mechanics are not super clear and the journal provides little to no assistance. A lot of it is too trial and errory.
• There are no sounds.
• The ending looks pretty (I think), but it is still quite lazy in its execution.
• There is a lack of good user feedback – sounds, visual cues (e.g. that a machine just did something), items (e.g. the funghi in Mars world are hard to find), slow journal …
• And much more!

I must say this jam was well organised, and its website a pleasure to use compared to LD. The next iteration is scheduled for February, looking forward to that! Thank you for reading, and I hope you found at least a little bit of this interesting!