Русский

Alexandr Lysenko

Baby Duck Syndrome

-

Introduction

Game jam is a few day event, in which developers create games on a particular theme. The word “jam” itself is related to musical improvisation of a few people, when they play musical instruments along themselves.[1] Programmers, designers, composers, writers and artists can gather in a team and create a game in a similar manner. There are games with a single developer. People learn, consolidate their skills through practice, and look at each other’s compositions.

In May 2024 there took place a Pixel Game Jam, the main theme of which was making a pixel art game.[2] By the start of the event, nine thousand people from around the world joined the list of participants. The event was to last ten days, in teams of up to three members, with any engines or technologies allowed. In this article, I’m going to describe the general course of making my game, which got a name “Baby Duck Syndrome.”

Day 1. Planning.

There were ten days at my disposal, and the first of them was spent contemplating on a kind of game I want to make.

An additional game jam theme was revealed, which turned out to be “Aqua,” and everything related to water.[3] The jam hosts hinted at sunny beach and ocean; but, practically, the game could be about ice, fish, or plumbing even. It wasn’t difficult to add some aquatic images and effects, rather it was difficult to make water to become the gameplay’s essence. For example, in a game about building sand castles during a rising tide, its theme would most likely be sand, not water. Or, in another game, where the rain is only briefly seen, or waves are only barely heard, water wouldn’t be the main theme either.

That day I thought through a number of options, but didn’t start the work.

Day 2. Pixelated canvas and two-dimensional water.

I decided to write my game in pure JavaScript, which could run in browser. Surprisingly, it’s quite easy to make a pixelated canvas using web developer tools:

<canvas id="canvas" width="128px" height="72px"></canvas>
<style>
    #canvas {
        image-rendering: pixelated;
    }
</style>

Since the game field consisted of a relatively few pixels (128*72=9216), I was tempted to add a dynamic water surface. At that moment I had no conception whether it would be a small puddle, or a large water reservoir. All I wanted was to see water ripples after a click, which would be calculated during the runtime. There was a two-dimensional water algorithm on the internet, mentioned in a several sources.[4] It seems, nobody knows, who was the inventor; and it passed from one generation of programmers to the next.

Fig. 1. Waves using 2D water algorithm on the second day of development.

This algorithm became the basis for my game, so I’m going to elaborate on its principle. The one-dimensional implementation (which is easier to understand) looks like the following:

for (let i=1; i<buffer1.length-1; i++){
    buffer2[i] = (buffer1[i-1] + buffer1[i+1])/2 - buffer2[i];
    buffer2[i] = buffer2[i] * 0.5;
}

for (let i=0; i<buffer1.length; i++)
    [buffer1[i], buffer2[i]] = [buffer2[i], buffer1[i]];

The array contains wave heights for each pixel on the canvas. When a player clicks on the canvas, a value is added to the respective element in the array, which disturbs the water. Wave heights might be transformed into a color brightness, transparency, or other graphical effects. The program runs through each piece of water and does three main operations:

  1. Smoothing: When the water state updates, the wave height at a given pixel is calculated as the mean of wave heights at its neighboring pixels. A high wave falls, is smoothed and blurred. This is how waves propagate, and travel from high points to low points (and vice versa).
  2. Fading (Damping): Each value in the array is multiplied by a coefficient lower than one (for example, 0.95), because we expect water waves to fade over time. Without the fading effect, the newly added waves accumulate and increase the overall water level. With the fading effect, wave heights eventually return to zero.
  3. Inverting: After smoothing, the new wave height at a given pixel, received from its neighbors, is reduced by the previous wave height. When we take into account and invert the previous (fading) wave height, the waves oscillate like a real water surface. In a few words, without this operation the “wave” on a two-dimensional field looks like a bright dot, which gradually spreads into a faded filled circle, then disappears.

The only difference of the two-dimensional implementation is that the smoothing happens in two axes. I changed the power with which waves run upward and downward; this made them look oval in the game and created a sense of perspective. That was the result of the second day of development.

Game version 2024-05-13.

Day 3. Pathfinding algorithm.

Fig. 2. Character movement to a mouse click.

A character, which now looked like a square, appeared on the canvas. I had no plans of adding obstacles, so a movement algorithm could be simple as well. I used the known Bresenham’s algorithm to make the character move from one point to another.[5] The program appends all the points on a line between the player and place that was clicked into an array. The player travels through these points, until it reaches the target.

Game version 2024-05-14.

Day 4. Main character and the game’s name.

It was time to decide on the main character’s appearance and to draw its sprites. On that day I finally figured it should be a duckling, and the game got a name “Baby Duck Syndrome.” I thought the duckling would express the water theme especially well, and it could be an interesting game. In nature, baby ducks imprint on the first “mother” they see after hatching, and follow her wherever.[6] Because the duckling swam after the cursor, this became the reasoning behind the game’s name.

Fig. 3. Duckling sprite sheet on the fourth day of development.

I used Krita to draw the duckling facing eight directions. Sprites had to change depending on the direction to a mouse click. It was achieved using the atan2 function, which returns the angle measure between the x-axis and the ray from the origin to the point.[7] In my case, the origin was the duckling’s location, and the other point had the coordinates of a click. I’m getting ahead here, but during the fourth and fifth days I also changed my view on the units of angle measurement. My first solution, out of habit, relied on unnecessary degree transformations, but in the end I accepted the convenience of radians.

Game version 2024-05-15.

Day 5. Finite state machine of the character’s behavior.

Fig. 4. Diagram of the duckling’s switching from one state to another.

Previously, when a player clicked on the water, the duckling turned and rushed toward it instantly. To make the character’s behavior seem more realistic, the duckling started turning to its target gradually. It became possible after dividing the character’s behavior into three states: Idle, Rotating, and Swimming.[8]

Being idle, the duckling went up and down with the waves, and slowly drifted to the side. After a click, the duckling switched to the rotation state, and kept turning in place until the target entered its line of sight. Having the target in front of itself, the duckling swam toward it. Being near the target, the duckling returned to the idle state. That same day, the duckling learned whether it’s faster to turn clockwise or counterclockwise.

Game version 2024-05-16.

Day 6. Caustics in Blender and boundaries.

Fig. 5. Water bounding lines and their ends’ coordinates.

On the sixth day I modelled a water container in Blender, and added light rays on the pool’s bottom. These rippling shapes are called caustics, and formed when light is refracted by waves on a water surface.[9] There are many videos on the internet explaining how to recreate such an effect with Voronoi diagram.[10] It took time to find a method to make a seamless looping animation.[11] In my game, ten images with caustics change with the speed of thirty frames per second, which creates an illusion of continuous water movement.

I found the functions of two lines by the coordinates, above which is the place beyond water, and where the duckling can’t swim. There is a round shadow on the pool’s bottom, prepared for a ball in the next game versions.

Game version 2024-05-17.

Day 7. Sound effects.

By the seventh day the game remained without a sound. I made a few attempts to record a lounge or bossa-nova piece, which is associated with summer beach music sometimes. It took a great deal of time, even though it wasn’t the main part of the game design. At the end, I abandoned the idea of having background music.

The first sound in the game was a duckling sound, which I could barely find in an online library of royalty free sound effects.[12] Unfortunately, there was only the shrill of a duckling, who probably got lost. When baby ducks lose their parents from sight, they become anxious and start calling for help, which is very different from their relaxed chirping. The second sound was a splash, that I recorded in a bowl of water in bathroom; although it appeared in the game only on the tenth day.

Game version 2024-05-18.

Day 8-9.

Almost no progress.

Day 10. Completing the project.

Fig. 6. The game version on the tenth day.

The development time was soon to be over, but many parts of my project were yet to be done. I spent the last hours drawing additional sprites and shading. The duckling started turning as it moved, and showed more actions visually when swam toward its target. I hurried to make the last changes in code. Now there was a green grass on the background, and, as a memento, a ball with my initials in the water.

When the timer for submissions stopped, there were more than 700 projects sent by other developers![13] Some fully dedicated to water, and some with no connections to water at all. Compared with many other works, my game had clear shortcomings, because it didn’t have levels, or winning and losing conditions. Still, I got many positive reviews and ideas, how the game can be improved. The development of this game brought me joy, because it’s pleasant to see how something is being made out of nothing. This is not exactly true, since I had my previous experience, and notes, left by other enthusiasts on the internet.

Game version 2024-05-21.

Update. Improving drawing speed.

The game worked on my desktop, but I noticed visual freezes on budget mobile devices. There are recommendations for improving drawing speed on canvas.[14] Instead of drawing water pixels one by one < 9216 times, I used another method, in which computer changes the data of pixels first, then draws all pixels at once.[15] Browsers also provide a function requestAnimationFrame() for synchronizing draw requests with screen refresh rate.[16]

Game version 2025-01-05.