Ludum Dare 46 in the bag...

Ludum Dare 46 in the bag...

Over a largely caffeine fuelled weekend in April, I embarked on my first non-solo game dev adventure, taking part in the Ludum Dare 46 with my long-time friend and collaborator, Lawrence Wong.

The theme for this jam was the somewhat vague phrase, ‘Keep it alive’. I mean… that’s kinda the theme of every videogame, right?

Game link: https://simiane.itch.io/for-ducks-sake
Jam link: https://ldjam.com/events/ludum-dare/46/for-ducks-sake

Ideas…

We had two main ideas at the outset, the first being a lemmings style puzzler whereby the main character would be walking around obliviously triggering death-causing traps at every turn, and it would be down to you as the player to run around disarming said traps.

The second idea was one involving a main character being followed around by slew of NPCs that were in constant peril from enemies and obstacles.

Once we’d decided to add a weapon to the mix, the second idea was the obvious choice since we wouldn’t have to worry about level mechanics, doors and the like. Our penchant for pixel art and animals dictated the theme of the game, and the delightfully titled For Ducks’ Sake was born.

Getting things working

I heart Phaser.
I’ve been messing around with game development for years and looked at countless frameworks, but they all seem very opinionated. I find the IDEs and workflow to be restrictive and confusing, and I’m generally happier with a black screen and some text. Phaser allows me to basically get my hands dirty.
It’s a mixed blessing – I have full control over every little thing, but at the same time, every little thing has to be created and tweaked. What this tends to mean is that rather than solve an immediate problem, I try to solve the type of problem with some encapsulated code that I can reuse again and again… with mixed results.

The player

With Phaser it’s very easy to get a sprite on the screen, animate it and move it around, but of course our player needs to do other things like fire a weapon, accept input, play sounds, etc. Sure, we could put this logic in the game scene itself, but if we create a dedicated player class that extends a Phaser ‘GameObject’, we can keep this functionality coupled to the player and that makes it much easier to drop the player class into this or any other game. In fact, the player class I used here is adapted from another game I’ve been working on, and thus when I dropped it into the game the movement logic was already working.

Rather than extend a sprite, I chose to extend a container. Why? Well, a container can contain multiple sprites, and also has a physics body to play with.

Aiming

And so we embarked upon the creation of some basic shoot-em-up mechanics and quickly decided that we’d want to allow directional shooting in a twin-stick arcade style.

Previously I’ve written a Phaser 3 plugin to merge keyboard and gameput input into a single object to be interrogated. The plugin only handled a single ‘direction’ input however, i.e. the left stick / d-pad / keyboard. Adding the right stick was simple enough, but what about mouse input? It was simple enough to add a function to track where the mouse was in relation to the player, but in order to convert that angle information into a direction for the player to face, requires engagement with my old adversary – circle maths.

Honestly, I remember learning about circles in maths at school and thinking I’d never need to use this as long as I lived. I was correct about that for over 2 decades – right up until I started making games.
The upshot is that I had to really delve into the merged input plugin and as a result a lot of great changes and additions have been made. It’s really solid now and I’d recommend checking it out if you’re making games with Phaser.

Shooting

Phaser has an unofficial Weapons plugin that handles grouping and reusing (i.e. ‘pooling’) of bullet sprites. It also has a number of methods that make it easy to indicate direction of shot, rate of fire, etc. Originally we had different sprites for each direction in which the bullet would travel, but I could find no easy way of changing the frame of a bullet on the fly like that. There’s almost certainly a way to do it, as the weapon plugin is designed to allow multiple weapon types, but we were already way behind schedule. Luckily the plugin does handle sprite rotation, so after creating a simple method to position the bullet at the edge of the gun depending on the direction of the player, we were good to go.

Explosions were added directly to each bullet object by looping through each bullet in the pool and extending the kill method. Before calling its parent method a new ‘explosion’ sprite is created, so for every bullet collision we get a lovely explosion animation too. In other games (with more time) I’ve used this technique to provide gunsmoke when a bullet is spawned too.

Followers

Next came the creation of our follower objects - the ducklings. These are classes that extend a ‘follower’ class, that in turn extend the sprite GameObject. The reason for three separate levels of extension (duckling > follower > sprite) is that we’d hoped to have other characters in the game and this level of abstraction allows for base functionality (following the player) to be extended with individual characteristics (speed, health, etc.)

Initially the ducklings were set to simply follow the player around, using the ‘moveToObject’ function, however this constant movement toward the player looked little unnatural, and so I implemented a short random delay, so that the ducklings would only note the player’s position every so often and move to where they last remember the player being. This seemed to give the ducklings a bit more autonomy – they seem a little bit more like individual entities than sprites that are set to follow the player. It’s really fun when such a simple (one line of code) change can really change the feel of an object. I love game dev.

Enemies

Enemies came next, and again for our Gnome enemies I chose to create a gnome class that extends a generic enemy class, which in turn extends a Phaser sprite GameObject. We of course, planned to have multiple enemy types, but alas, time was very much against us and it never came to pass. So the enemy (gnome) is set to randomly choose a single follower (duckling) when it spawns, and move toward it at a ranged random speed. If there are no followers in the scene, no gnome enemies will be spawned.

Collisions

Adding collisions makes the game feel real for the first time.
To begin with, a collision event between bullet objects and enemy objects so we can kill our gnomes. This event plays a death animation.

Next, our gnomes need to be able to capture our ducklings.
Initially I’d planned to have the collision event change the properties of the duckling so that it could no longer move and attach it to the gnome object so that as the gnome tried to get away, the duckling sprite followed it at a fixed point, making it appear to be in the hands of the gnome, then if the gnome is killed, the duckling sprite is dropped, decouples from the gnome and regains the ability to move… However, one of the things I’ve learned about game dev is that ‘cheating’ is often the best way to go – it’s a difficult coding style for me to get my head around coming from a web dev background where code elegance is important – so I quickly realised that it would be much easier to simply ‘kill’ the duckling object and change the animation of the gnome sprite to make it look like it was carrying a duckling. Then if the gnome is killed, we can simply spawn a new duckling object at that position. Probably saved about a hundred lines of code there and made life much easier!

Finally we need to allow collision between the player and both ducklings and gnomes so that you can push them away from each other to give you room to breathe/shoot… just to add another dimension to the gameplay. This also has the added effect that if a gnome isn't actively chasing a duckling (i.e. when the gnome has caught a duckling) and you push him away, he'll run off screen at an increased speed.

You may notice that the gnome running animation goes faster if the gnome is moving faster. This is due to a little bit of code that dynamically adjusts the framerate of an animation based on the velocity of the object. It's a nice little effect I tend to use everywhere.

Gameplay & polish

At this point, the project really started to feel like a game. All we needed to do was add some scoring and a bit of a difficulty curve.

We had of course originally planned to flesh this out into a much bigger game, incorporating waves of enemies, power-ups including new weapons & extra lives - which in this case were to be little eggs that you’d need to sit on for a few seconds to hatch into ducklings… oh, it would have been so cute!

But fleshing out a game isn’t really what a game jam is about… stripping everything down into something playable and polishing it up in the last few hours on zero sleep and enough redbull to floor an elephant… is what a game jam is about.

So the last few hours were spent adding music and sounds, menu and gameover screens, storing scores & timers in local storage and generally getting the game ready to publish.

I particularly love the little giggle the gnomes make when they steal a duckling.


Bugs, bugs, more damned bugs

It was of course at this last second, that we noticed some bugs in movement and collision.
It seems that the gamepad support would simply fail to work maybe 20% of the time.
Occasionally collision between bullets and gnomes failed to work too.

We had to publish the game before fixing, and in fact it took a long time to get to the bottom of the bugs. What I found is that gamepad support on browsers can be pretty flaky, and for various reasons there were knock on effects that caused problems elsewhere.

At the heart of the problem is our reliance on the ‘connected’ event of the gamepad. If the browser has registered the gamepad’s existence before Phaser loads up (i.e. if you’re pressing a button when the game loads) then the ‘connected’ event will never fire and this the game logic won’t connect the player and the pad. I ended up interrogating the browser’s gamepad API directly to manually fire the connected event in the case that a gamepad was already registered.
I updated my input plugin to handle this and pushed it up to Github and NPM.

This worked great for me, but a couple of days later when I came to use the input plugin on another game I was working on, I found that the gamepad would no longer work at all on Firefox. It still worked on Chrome. We were still getting occasional reports of gamepad problems on the game, so I figured this must be the same issue. On further investigation I found that sometimes, for reasons unknown, the browser’s gamepad API will register null gamepad objects in addition to the actual gamepads! So in this case I would have two gamepads, a null at index 0 and the real gamepad at index 1.
Fixing this is easy enough, but it’s just another little inconsistency we have to deal with.

Still heart Phaser?

The whole experience has made me question my use of Phaser as a framework for more serious releases. The ‘blank slate’ nature of Phaser development makes things like UI long winded to create and I’m sure other frameworks will do a bit more of the heavy lifting there.
What really worried me though, is the bugs in the game and how difficult it was to get to the bottom of them… however I had some great support from the community and although it took a long time to work through what appeared to be bizarre and inexplicable issues, there was always an underlying cause and the issue has always been fixable.

Importantly and somewhat expectedly, the bugs are rarely anything to do with Phaser, but with my implementation and expectations. It’s reasonable to assume I’ll have a similar amount of issues and difficulties with any other framework.

In all likelihood, Phaser isn’t the best tool for the job when considering a larger game for a Steam release, but it’s still the framework I feel most comfortable with and least constrained by.

The mantra for game development remains ‘Do whatever works for you’.
So, yes. I still heart Phaser.
Heart heart heart.

Feedback

At the time of writing, the Ludum Dare 46 rankings period is almost at an end. So far we’ve had almost 300 ratings submitted, which puts us in the top ten most ranked games of the entire competition.

The feedback has been great – in opposition to my own sense of disappointment that the game couldn’t achieve all the lofty goals we set out to, it seems people have largely enjoyed playing it. For the first time, I’ve seen people playing the game on a live stream and chatted to them as they’ve done so. It’s been awesome.

I think polish accounts for a lot more in a game than you’d imagine… the gameplay is very basic, but while getting things like the physics and feel of a game right is probably the most important thing, you can cover a lot of issues with good graphics and little extra touches. For this I really have to hand it to Lawrence – the pixel art and animation style is nothing short of beautiful, and little touches like the duck crying animation and the gleeful smile of the gnomes are unexpected delights that make the player laugh and provide a subtle affinity with the game.

Without wishing to gush too much, Lawrence is a fantastic designer who can not only produce wonderful artwork, but also has a great understanding of what makes an experience fun. It’s always an enormous pleasure to work with him and anyone reading this should hire him immediately.

In a little over 24 hours, we’ll have the results of the ranking period. It’s always been my ambition to rank in the top 100 for a game on Ludum Dare.
This competition has seen around double the number of entries at around 5,000 games, so top 100 is very unlikely – but I’ll be back to report on the results in a few days.

Finally

Making games is more fun than playing them.
It’s more fun than pretty much any other kind of development I’ve done throughout my career.

With frameworks like Phaser, it’s easy to get started and with communities like the one surrounding Ludum Dare, game dev is accessible and fun.

If you’ve ever felt even the slightest inkling that you’d like to make a game, stop thinking about it and get started.

GLHF.

Comments