Hey everyone! Recently, I participated in
my very first solo game jam, Acerola Jam 0. It was the perfect opportunity to take a step
back from my main project and see if I could create a full game based around a theme in just
two weeks. I wanted to make sure to include all the little details like music, sound effects,
menus, and an ending so that it feels polished. By publishing a small game, I would finally
be able to get a feel for the later parts of a game dev cycle and I hope to be able to apply
those learnings to my main project in the future! So today, let me take you through the full start-to-finish process of
making “A Gnormal Journey”. It all started with the announcement of the
theme: Aberration. The full definition provided was “a departure from what is normal, usual,
or expected, typically one that is unwelcome.” I thought this theme was really
interesting and for some reason the first thing that popped into my head was
to make a game about conforming to a society. After a long think, the best idea I could come
up with was actually a local multiplayer game. You would control a character trying to
blend in with a crowd using the keyboard, while your friend has to guess which
character you are using the mouse. I guess I was kind of inspired by that one Wii
game where you have to find the odd person out. But there was just one tiny problem with my
idea. After checking the rules, I realised that the games needed to prioritize single-player
gameplay. Which… made a lot of sense, actually. But since I liked my idea so much, I decided to stick with it and try to make
both single player and multiplayer modes. As I’m sure you can imagine, this was not really feasible for a two-week game jam
so I had to scrap multiplayer. But anyway, I didn’t realise that at the
time, so as soon as I got back home from work, I excitedly set up a new git repository
and created a blank 2D Godot project. I decided the first step would be to
create some placeholder art for the characters in Photoshop. Well at least
it was meant to be placeholder art, but I never felt the need to
change them, so they stayed! I kept the design suuper simple because
eventually, there would be large numbers of them on screen. For the overall aesthetic
of the game, I was really inspired by a game called The Gnorp Apologue, which has such
charming monochrome pixel art. It’s so fun watching these little guys go, and I
was hoping to create a similar feeling. It was also around this point where I had
the idea to name these creatures “Gnorms,” which is not only inspired by the Gnorps
but is also obviously a play on the word “Normal.” Which as you will come to
see, reflects their societal values. Next, I slapped together a quick character
controller script. It’s definitely not the prettiest code…but essentially, all it's doing
is picking one of these 8 directions to go, normalising that vector, and then multiplying
by a speed. So, very basic movement. With Godot’s input map system,
it was also very easy to hook up the movement to both the arrow keys and WASD keys. I also had a think about the lore of the
Gnorms, and why the player would be trying to blend in with them in the first place.
So I came up with the logical conclusion that the player is actually a monster in
disguise, with unknown but questionable intentions. To implement this I drew up
a quick monster shape-shifting animation. Not gonna lie it looked pretty silly,
but was a good placeholder to start with. I was still thinking about multiplayer at this
point so next I did some experimenting with an outline shader. I made it so that when you
hover over a Gnorm, the outline shows up, and when you eventually click on the
correct one, they turn into a monster. Since I scrapped multiplayer this functionality
was never used in the end, but the outline shader turned out to be quite helpful for
something else which you will see later on. At this point, it was only ~8:30
pm , so I still had a bit of time to add more things. I decided next
I would add the rest of the Gnorms into the scene so I could actually
start testing my idea properly, and I really start thinking about how to translate
this multiplayer idea I had into single-player. With the kind of random crowd movement
I had in mind, I wasn’t sure how to make the computer detect when the player is
actually acting differently from the rest. It was at this point I realised it would
be so much easier to have the Gnorms follow a linear path instead. If the player
steps off the path, then they would be caught. So then, how do we implement a path? Well, Godot
has some nodes for this exact use case. The Path2D node lets you define a path by adding some points.
Then if you add a PathFollow2D node as a child, you can modify the progress or progress ratio
fields to automatically move it along the path. There’s also a rotate option which I disabled
since I didn’t want them upside down. Next, all I needed to do was add one line of code
to get them moving along the path at runtime. As you can see though, they weren't facing the
right direction. So I also added some code to flip the sprite depending on whether it is moving in
the positive x direction or negative x direction. Now I had the player movement and Gnorm movement
complete, here's what they looked like together. And not that it matters anymore, but I
think it is pretty obvious which one I am controlling. Next I needed a way to detect
when the player strays too far out of line. To do this I simply added an Area2D node that
follows along the path where the player should be. So now, if the player no longer overlaps with this
detection circle, they return to being a monster. And at this point it was now
midnight and I was exhausted, but I thought that was pretty
good progress for day 1! The next day my goal was to finish the gameplay
loop and prototype a level. First thing I wanted to fix was that it was way too difficult
to have to move as soon as the game starts. So, I decided to add a countdown. I
experimented with pausing all movement during the countdown, but the timing
was still difficult once it resumes. Eventually I made it so that everyone still
moves during the countdown like before, but this time the player’s controls are
disabled. This means you automatically follow along for a bit before controls
are handed to you, which felt much better. Next I wanted to get started
on the background visuals. So using this image as a reference, I drafted
up some simple tiles in photoshop. Then back in Godot, all I had to do was draw in this bitmask
to indicate where the tiles should connect. I also thought it would look nice to scatter
these detail tiles randomly around the floor. Instead of placing them manually though,
I simply just modified their probability of showing up. So now when I go to fill in
an area, it automatically randomises their appearance. Then I was able to go in by hand and
add in some extra tiles to indicate the path. Now that the visuals were starting to come
together, I wanted to improve the monster transformation animation. To create some
more suspense I added this shaking effect, and updated the sprites for more contrast.
The shaking effect was achieved using code, where the position is offset
by a random amount each frame. Next I added in a retry button that appears when
you lose, and when clicked it reloads the scene. I put in some extra effort here to have the UI fade
in and out. That way the game feels more polished. In order to implement these fades I decided to try
using Tweens. For those who don’t know, tweens in Godot provide a way to smoothly transition
from one value to another by using code. I was also very excited to discover that there
are many types of transitions to choose from. I ended up using the Sine one, which looked like
this in the code. This code was then applied to a Polygon2D node which I extended to cover the
whole screen. Now as you can see, it fades nicely. Moving on, you may have also noticed that
at the start of the level it is visually impossible to distinguish who you are. That
was the point for the multiplayer idea i had, but since now that’s out the window I
decided to at least add an arrow indicator. In fact I went ahead and drew a bunch of
icons because I also wanted to give the Gnorms some personality. At
first I had them run away... But it wasn’t really clear why stepping out
of line causes you to turn into a monster. So instead I reworked it slightly so that if you
step out of line, the other Gnorms will come and attack you with little swords. Now I guess
the monster form is more an act of self-defence. I thought it turned out pretty hilarious and so
I definitely wanted to keep it. I even updated the monster sprites further, making
it look sad and defeated. The only problem is that the Gnorms do bunch
up in the same spot. But I ended up just leaving them be like that because
it would be kind of complicated to fix. Also since I now had the code
for both attacking and fleeing, I left one of the Gnorms to
flee as a little easter egg. The Gnorms also get suspicious of
you when you when start straying, indicated by these question marks. I
thought it was a neat little way to add a bit of personality and also
warn the player at the same time. At this point I felt like I had the fundamentals
all figured out, so I spent the rest of the evening planning and prototyping some basic level
designs. The levels would start very simple like the straight line, but have the potential to get
really complicated which I was excited about. Day three ended up being entirely
focused on sound effects and music, which was one of my favourite parts of the jam. Firstly, let me introduce you to
this amazing website called sfxr, which can generate really cute 8-bit
sounds by tweaking some parameters. Using this website, I made some countdown
sounds, a button hover sound effect, button press sound effect, alert sound
effect, and a level complete sound effect. The death sound effect was a bit more
complicated, but essentially I layered a few different static-y sounds together and
then added this hit sound that plays on repeat. And with that, the whole morning
had already gone and I was going to move on to something else, but then an
interesting melody idea popped into my head. I thought it would fit the Gnorms quite well so I decided to build upon this
idea and see where it goes. But since I obviously don’t have the
rights to use this Undertale sound, I decided to have a go at creating
my own by configuring a synthesizer. Now I am by no means an expert but I
did wanna share what I know about synths anyway, cuz I think it's super interesting. As you may know, all sounds are made of waves
which can be visualised like this. For computers, some of the simplest waves to
generate include sine waves, triangle waves, sawtooth waves and square waves. So when looking a synthesizer, you’ll often
find a section called “oscillators” that allow you to select the waveform you want. With
multiple oscillators it is easy to layer different waveforms together, in order
to create unique combinations of sounds. Different synths have different
layouts, but the same principles apply. From there you can shape the sound
further by applying an envelope, with parameters called attack, decay
sustain and release. The envelope can be visualised like this, and watch
how the shape affects the final sound. Anyway I could go on and on, but let me
show you how I configured the synth for the game jam because it was super
easy. Basically I searched through the default logic patches for a synth
that had a nice vibrato. Then I simply switched it’s oscillators to use a square wave
instead. So now, this is what it sounds like. And this is what it sounds like in context. If you wanna check out the full version
I will add a link in the description. After importing my music into the game and
playtesting though, one thing that stuck out to me was when the player gets caught. Instead
of writing extra music for the retry menu though, I decided on this hacky fix of adjusting the
“pitch scale” down. So now, it sounds like this. After spending the entirety of the previous
day on sounds, it was time to get back into some actual feature work. One thing the game
definitely needed was a main menu, so I added that first. With this level select button you are
easily able to jump ahead in case you get stuck. Next, I had a go at implementing a pause
menu. This had always been a daunting task for me because I assumed it would be really
complicated. I spent like an hour coding this absolute mess before realising that I only really
need one line of code: GetTree().Paused = true. This pauses everything - however that includes
the pause menu buttons too! To fix this, I learnt that all I needed to do was
change their “Process mode” property. “Pausable” mode means the node will only process
if the game is not paused. “WhenPaused” is the opposite - the node will only process when the
game is paused. That might sound counterintuitive at first but it is surprisingly useful,
and this was what I used for the pause menu buttons. Another useful one is the “Always”
mode, which means the node will always be processed no matter if the game is paused. This
would be applicable to the music, for example. And to finish off the pause menu, I applied a
low pass filter which creates a muffled sound. Sometime during the evening I also went ahead and
designed a new character that would co-exist with the Gnorms. As you can see I struggled with the
design initially before making it much bigger. I think I just called them “Big Gnorms”
in the game but during the making of this video I realised they should have been
called…”Enorms” (because enorm…enormous…get it?). Anyway, their job is to look cute but
actually be a kind of nuisance. In some of the earlier levels they will just sleep
out in the open, so you gotta be careful not to wake them up. This also means the rest of
the Gnorms will move much slower than before. Then in a later level, a bunch of them group up
in the centre, forcing everyone to take the long way around. I had some more ideas which I’ll get
to later, but that brings us to the end of day 4. During the next few days I worked on the
game for small amounts during the evenings, mostly spending that time making more levels. I had this incident on level 9 where I kept
getting knocked back while playtesting. I checked the collider debug view…and nothing
was there. I spent ages going through my code, trying to figure out what was wrong, but
couldn't find anything. Tired and confused, I decided to sleep on it and
continue looking the next day. And let me tell you, sleep really
does wonders because the next day, I realised that the issue was actually due
to this invisible Enorm lying on the track. I forgot it was there after copy-pasting
a previous level. So yeah lesson learned, colliders can be active even when invisible. At this point on day 9, I had 9 levels so far. But
I didn’t want the entire game to just consist of walking along a path, because that would get kinda
boring. So here’s what I came up with instead. On level 10, the Gnorms harvest some crops.
You have to make sure to pick the correct plant - else the Gnorms will be on to ya.
I also made sure to consider what happens if you don’t pick up a plant…and yeah, the
Gnorms don’t take kindly to that either. Levels 11 and 12 were themed around traffic, so
you sometimes have to give way. It’s actually quite challenging to get the timing right, and
I definitely made level 12 a bit too difficult. Level 13 however, is probably my
favourite. You have to feed the Enorms, like this. Then they spit out a blob of
honeydew which you can pick up on your second lap. However due to this, there are now many
ways to fail this level. You could throw the food too early or late, you
could not throw the food at all, you could pick up the wrong honeydew or you
could forget to pick up the honeydew completely. Moving on to level 14, I think it
speaks for itself. I wanted to make it the hardest level in the game and I
can safely say that has been achieved. The last level, level 15, is a nice relaxing walk
into the center where the Gnorms go to deposit their honeydew. I thought it would make sense
for the Gnorms to store their food in a nest, kinda like ants. Most importantly,
their gnest would be spelt with a g. Now, all that was left was to add
some intro and ending cutscenes. I kept the intro cutscene really short and simple
with just a basic explanation of the premise. I must say, the game turned out very
different to how I was expecting, but I guess that’s not necessarily a
bad thing. Anyway, thanks to the heavy usage of timers and tweens, this
cutscene was really quick to make. The ending scene took me much longer
however, probably because I went a bit overboard and painstakingly painted in
a huge area of tiles to depict the nest. Finally, it was time for some playtesting!
The first bit of feedback I got was it is quite hard to keep track of who you are.
So this is where I added a small outline to the player - using that outline shader
that I already imported back on day 1. The other feedback was that some of
the levels were too difficult. So I went ahead and doubled the radius of the
detection area, which I am very glad I did. At this point I was quite exhausted with
this project, but the finish line was really close! Exporting should be super easy,
just gotta add this setting here and - Yup, after all this time I learnt the hard
way that Web export is not available for games built with C# in Godot 4. I would still be
able to export a Windows build, but I was really devastated because people are far less willing to
download games as opposed to play in the browser. All I could do at this point was
upload my builds for windows and mac and move on - accepting the fact that
the game probably wouldn’t do very well. After a month passed, the judges played
through all 945 games and it was time for the results to be announced. So,
how did I go? Well…I made the top 5! I cannot even begin to tell you
how amazed I am with this result, especially after I genuinely thought I had
no chance. There were so many awesome games submitted and I would definitely recommend to
watch the results stream if you haven’t already. So there you go, that concludes the story
of how I made “A Gnormal Journey” and I hope you enjoyed following the full process.
It was a great experience to finally make an entire game from start to finish and I learnt
many, many things along the way (tier list?). But anyway, it’s time to get back to my
other projects now, so see you later!