Postmortem


How it started vs how it went

The original design seemed simple enough: an eight-bit ghost busters rip-off with a monocolor tileset, split into two different phases, the day phase and the night phase. The day phase would see you moving around an abstract "world map" that was basically a small city, finding customers and talking to them to get clues on what kind of ghosts are haunting their house. You'd then equip the right kind of bait and equipment to lure and capture those ghosts, then hunt them during the night phase. Trade captured ghosts for better gear to get bigger and badder ghosts.

It all made sense and seemed do-able subconsciously, but if I had taken a deeper look I would have realized it was only possible if I actually had the entire jam period to work on the game. In reality, I could only spend time on it after work and on the weekend. That, combined with some of the game mechanics turning out to be not so great, foiled things.

I was actually going to throw in the towel, something I'm pretty good at, when a German-speaking rabbit -- or a German in a rabbit-suit, not sure which -- whispered in my ear and told me to finish anyway.

I only managed by throwing away almost everything but the tileset and the "ghosts hide in objects" concept and the idea of navigating the environment using a flashlight. Why's the guy a priest? I don't know, man. Probably because I watched someone play Faith recently.

Anyway, that's the story of how the game ended up being about a priest throwing crosses at ghosts while wandering around a house.

What went right

Planning things out ahead of time

Usually I don't make too much of a plan for game jams and as an end result the concept remains vague and hazy and it's hard to know what to cut and what to keep when the deadline gets close.

Since I planned things out this time, I had already built up a lot of art, mechanics and code that I was able to repurpose.

Copying some of the code patterns of legendary Ludum Dare entrant Sébastien Benard

Sébastien Benard is responsible for Nuclear Blaze, and probably some other games too. Actually, there's a list of them at https://deepnight.net/ and some of the game-jam games have a time-lapse video of the game being made, start to end. One thing I noticed in the games was this very versatile "cooldown" data structure, basically a collection of timers. It worked kind of like a dictionary on steroids: "add" a cooldown by passing in a name and a start time, and it would start counting down. You could then make queries like "what percent of this cooldown is done" and "is this cooldown running at all?" 

But as it turns out you can use this thing for all kinds of time-based, or time-gated, effects. Pause the AI by starting an "ai_paused" cooldown and abort AI processing if "ai_paused" is on cooldown. Don't want to attack the player again within a time period if we've already done that once? "attacked_player_recently" for however long.

Using strings as names makes it kind of sticky but also very fast to prototype code. The pattern simplifies a lot of time-based logic at the cost of readability / typos. In retrospect I could have used an enum instead of a string for the name, I guess?

The Monochrome Tileset

I've recently gotten back into pixel art after a near decade hiatus, and I've only been doing it for about 3 weeks or so. The abstract nature of low res (16x16 or lower) + the limitations of using single colors worked out great for me, because despite how bad the art is, I was able to more or less make what I needed by myself in the minimum of time needed. I normally do 3d stuff because I'm pretty bad at 2d, so this was a fantastic result for me.

Finishing and submitting a game

I finished, remarkably enough. I normally don't even _show_ people something of this level of quality, especially since it deviated so much from my original vision that it became another kind of game entirely. 

But, here we are, so I guess it worked out in the end.


What went wrong

Not validating game mechanics independently

I worked pretty linearly: make player, make camera for player, make player shoot weapons, ... so on and so forth. By the time I got to certain mechanics I realized that they weren't very good and would need a rework. But I had built other mechanics (shooting, movement) based on the premise that these mechanics WOULD work well.

I should have taken each major game mechanic and tested it in isolation.

Copying some of the code patterns of legendary Ludum Dare entrant Sébastien Benard

Another interesting pattern I noticed was the prevalence of an "Entity" class that was effectively the root class of all actor-type objects in the game. It always contained a set of components (health, cooldown) as well as certain helper functions that gave entities uniform behavior in games. This worked well for Mr. Benard because he writes his own engine using Haxe (or uses Heaps), so his "Entity" class wasn't necessarily a class that was rendered on screen, just an abstraction for other classes that were, kind of like a Unity GameObject.

Adapting the pattern to Godot meant that I ended up with an Entity class that was derived from a KinematicBody, so all entities ended up being KinematicBody by default. I still think that for game-jam games, the concept is sound, but I'm not sure if Godot's rigid way of working makes it workable with the same level of usability. I'll have to spend time thinking about how to implement that pattern better before the next gamejam.

That's all, folks

I started using headers to subdivide logical sections and that means I have to have one at the end to cap it all off.

Thanks for reading.

Leave a comment

Log in with itch.io to leave a comment.