mitchell vitez blog music art media dark mode

Redbeard’s Recoil

I made a video game. This was easily the biggest creative project I’ve ever done. There’s a gameplay trailer and some screenshots on the Steam page.

After the overview, the rest of this post will get into some of the behind-the-scenes details of making the game. I learned a huge amount while making it.

Game overview

Redbeard’s Recoil is a grid-based puzzle adventure game where you play as a pirate named Redbeard. When you shoot forwards, Redbeard moves three squares backwards, unless he bumps into something. This motion-by-threes sets up three overlapping lattices of places he can stand, which can be moved between by bumping into the appropriate walls.

For example, in the image below, if Redbeard shoots up or down, he will fall into the ocean and reset. If he shoots to the left, he’ll move right three squares, and be only two squares away from that upwards path leading to the rowboat, so will overshoot it if he shoots left again. The correct thing to do is to shoot to the right, bumping into the wall, but aligning to be 6 squares away from the rowboat’s path. Then he can move back to the right and reach his goal.

Redbeard recoils not only from his cannon shots, but also in horror at the condition of the world around him. He’s largely driven by a vague sense of escape, which he aims to fulfill by amassing treasure in the form of golden coins.

After beating each level, Redbeard can engage in dialogue with a nihilistic seagull named Gulliver, who doesn’t understand your desire to amass treasure. However, Gulliver comes with some philosophical biases of his own.

Gameplay

Movement

The game mechanic at the core of Redbeard’s Recoil is its movement. This is how you shoot things, get around, and in most cases it’s how you run into failure. Moving three tiles at a time was an important choice made very early on. Movement handling is the single biggest chunk of gameplay code.

One common complaint from about half of early testers was that movement felt backwards, which makes some sense—you are recoiling after all. This was eventually fixed by adding some input options to the settings menu, including inversion, and a different scheme where firing and aiming were two different controls. I was probably a bit too stubborn about this, and should have added these settings sooner.

It worked out well that instead of one simple list of interaction pairings (what happens when a cannonball hits a wall…hits an arrow…hits a crab), the nature of the movement means you have to consider both where Redbeard is going, and where the cannonball is going. This might have been even more interesting if there were more object types you’re never supposed to shoot at. I still think the “one action, two consequences” thing was pretty nifty though.

Game objects

A big chunk of making tile-based gameplay is deciding which kinds of tiles to make, and how other objects (like cannonballs and the player) interact with them. Besides Redbeard, Gulliver, cannonballs, and floor tiles, here are some other interactive elements in the game:

There are also some other specialty objects (like the gates and archways in between groups of levels) plus another late-game enemy I don’t want to spoil here.

Level design

Designing fun puzzles was a lot harder than I expected it was going to be. I wanted the game to be easy enough that my (non-gamer) mom could play it, but hard enough that my game-playing friends wouldn’t be bored. The first five-or-so levels are probably a bit too easy because of this, but in playtesting good players tended to fly through them anyways.

Having the spark of a good level idea is something I still don’t fully understand. During development, there would often be gaps of several days in a row where I didn’t really have a conceptually interesting level idea spring to mind, so I would just work on other things (like bugfixes). I think spending a lot more time on the game (i.e. letting the level design stage simmer for over a year) could alleviate this. All my best level interaction ideas seemingly occurred to me out of nowhere.

There was often a temptation to just add more tile and enemy types, instead of working with what I had. In general, the more interesting puzzle ideas seemed to come from considering the interactions between existing features rather than adding in more features. Ideally, if the game were twice as big, it would have less than twice as many objects, and would lean more on these interactions. This means there is some pressure to make sure the objects you do have will interact with each other in interesting ways.

Art

3d models

Almost everything in the game is textureless except the ocean’s surface, which is generated by a programmatic shader. While I think the lack of textures works out fine with a simplistic cartoony aesthetic, I do want to try getting more practice with texturing in the future.

All models were made in Blender. As with many other aspects of the game, Redbeard’s is the most complex. The mesh is probably a bit too dense for how small he usually is on screen, but given that I didn’t have trouble running the game on even a fairly underpowered laptop, I never bothered to reduce its vertex count. Other meshes have very reasonable numbers of vertices, so there wasn’t any reason to adjust them.

Along with the pause menu, certain particles, and dialogue visuals, Gulliver’s speech bubble is one of the few non-diegetic visual elements in the whole game. I debated whether to include it for a while, but eventually decided it provides more clarity on whether you’re close enough to talk than I was able to provide via other means, like animation.

Animations

Rigging and animation was also done in Blender, with Redbeard having the most complex rig. Gulliver and a few other things have rigs, but only for a single animation each (Gulliver’s is a 5-second idle loop). A few objects, like the rowboats, have animations done without any rig.

Redbeard’s animation rig includes a look-at eyeball control, as well as foot controls, and most of the bones you’d expect for a simple humanoid character. Blinking and mouth movements were handled in code by setting morph targets (in Godot they’re called “blend shapes”). He has four animations: idle, aiming, moving, and ouch. The ouch animation is used when he bumps into an enemy, falls into the ocean, etc.

There’s a late-game enemy with gameplay interactions driven by animation rather than the other way around. While this was an interesting thing to try, it ended up not being as different as I expected—the order of some events was flipped and that’s about it.

Animation blending was done by simple set_blend_time calls in some game object _ready functions:

$Redbeard/AnimationPlayer.set_blend_time("moving", "idle", 0.15)

Particles

One bit of advice I got was that you can basically never have too many particle effects. It’s an easy way to add visual interest to all kinds of interactions. Most of my particle effects were very simple, like spheres shooting outwards in all directions before shrinking down to nothing. A few had more interesting shapes (like the feathers for Gulliver’s arrival) or behaviors (turbulence on the particles when you collect a coin). Generally I treated particles the same way I treated one-off sound effects—a way to indicate something has happened, and make the game feel more alive.

Rating for Godot

As part of this project I learned how to use the Godot game engine. I knew hardly anything about it ahead of time, except that it was open source and its scripting language was kind of like Python (so I figured it’d be easy enough to learn).

In retrospect, the amount of organizational freedom Godot gives you is a lot. Everything is a node, and it’s completely up to you how to arrange things. After figuring out a basic node structure that worked for my game though, it was a pretty nice engine for this project. I especially appreciated the ability to import .blend files so directly and smoothly, which I haven’t seen other engines do.

I enjoyed using the profiler, though I really only had one performance-driven issue during all of development. I had been making a new material for each floor tile upon level load, so I could randomize the colors. Material creation is really slow and this led to level loads being slower the bigger the level was. After using the profiler to detect where all that time was being spent, I was able to switch to using a small pre-built pool of materials and then randomly picking from among that pool of available materials, which eliminated the issue.

Godot’s update from 4.3 to 4.4 came at an incredibly good time. The biggest difference for me was 3d physics interpolation. Before, the physics-based movements in my game were fairly jittery and uneven (unless I’ve updated the trailer since I wrote this, you can probably still see some of this jitter in there). Afterwards, all those movements were buttery smooth.

The other runtime speed improvements and editor changes (i.e. hot editing) were nice too. At one point I was getting 50 frames per second on a laptop in 4.3, and 145fps in 4.4.

Very early on, I made the mistake of using AudioStreamPlayer3D for several sounds which shouldn’t have had an actual 3d position relative to the AudioListener3D that was hearing them. The most notable example was Gulliver’s voice. When Redbeard was facing left or right, Gulliver’s voice would seem to be coming from only one speaker, which sounded very odd.

I didn’t make super heavy use of the level editor tools, but obviously in a tile-based game snapping (both rotation and translation) was nice to have.

Overall, Godot was a great engine for learning more about game dev with a first project, but I’m also excited to explore other engines in the future. GDScript especially stands out as a nice feature. I would recommend Godot for anyone getting their feet wet—it seemed easier to get started with than Unity or Unreal, especially with features like the built-in code editor and .blend file imports without intermediate steps.

Programming

All gameplay code was written in GDScript, with a few external shell scripts for packaging, a small Python object overlap checker to make level design smoother, and a moderately-sized level feasibility checker written in Haskell.

None of the gameplay object ideas were super difficult to program. The elevator was probably the trickiest one, but was worth it since it opened up so many more gameplay ideas and gave the game a reason to be 3d.

One of my funniest rookie mistakes was detecting node types with String comparisons on the node names. For example, sand101 and sand2 would both be sand tiles, but every sand tile name had to start with sand for this to work. After switching to marking the sand tile node with class_name Sand and using is Sand for comparison, this felt quite a bit less janky.

Classes and organization

While I appreciated GDScript’s $ node path syntax, I found myself using absolute paths (starting with $/root/Main) a bit too often for my liking. The biggest coding difficulty I faced was definitely in node organization, or to be precise, in matching up my code with changes I’d just made to the organization of nodes in some part of the tree. The ability to define unique names to use with %UniqueNodeName was also nice.

I managed to avoid what I’m told is a fairly common trap for new gameplay programmers, and didn’t put all my functionality in one giant class. The biggest class I had was Player at just a smidge over 500 lines. I think because the game is so clearly based on tiles with different abilities, there was an obvious natural way to split out the behaviors amongst objects.

My class hierarchies were extremely shallow. I only added one very small intermediate class, to track the fact that multiple objects are “killable”, and can be marked as currently dead, but can still be resurrected unless Redbeard makes it to the next coin which acts as a checkpoint.

class_name Killable extends Node3D

var is_dead : bool = false

func die(_angle: float):
    add_to_group("resurrect")
    is_dead = true

func resurrect():
    remove_from_group("resurrect")
    is_dead = false

There are 7 classes that extend Killable in the game.

I learned about autoloading singletons a bit late in the process, so there are a few objects in the game that my code expects to always exist (like the dialogue system manager) that don’t have any actual enforcement of that fact.

Dialogue system

The dialogue system is basically a direct port of two blog posts I wrote a while ago: Character Voicing for picking which syllables each character should say, and Mouth Controls to set up mouth shape morph targets. I recorded 70 syllables myself and both characters use them. Gulliver’s pitch is about 1.58 times higher so they sound different.

The dialogue for every level is in one big file with this custom format parsed by the dialogue manager on startup:

levelname
G: I'm Gulliver.
R: I'm Redbeard.
R: Nice to meet you.

anotherlevelname
R: What a level!
G: Good job. If only our work mattered in the end.

State machines

Far and away the best tool I found for structuring gameplay code was the finite state machine. I think a few people recommended this to me independently early on when I was asking for code structure advice. In GDScript, enums and pattern matching are the strongest tools for the job.

Here is the set of top-level states Redbeard can be in. There are only a few higher-level concerns that ever override these. For example, when the game is paused, Redbeard isn’t actually executing any of these states.

enum RedbeardState {
  IDLE,
  AIMING,
  MOVING,
  BONKING,
  FALLING,
  DYING,
  IN_DIALOGUE
}

The default state is IDLE and there’s a setter function for easy access to make changes.

var redbeard_state: RedbeardState = \
  RedbeardState.IDLE: set = set_redbeard_state

This is what Redbeard’s state transition graph looks like. Every state except for IN_DIALOGUE can also lead to DYING, so it isn’t shown. I found it pretty helpful to have this kind of thing in mind while programming—it makes it obvious that e.g. ever transitioning directly from BONKING to AIMING is a bug.

These states can manage basically the entire lifetime of game objects in simple cases. For example, a big chunk of the Elevator code happens in state transitions as tracked by calls to the setter…

enum ElevatorState {IDLE, LIFTING, WAITING, RETURNING}

...

func set_elevator_state(new_state: ElevatorState):
    var old_state := elevator_state

    match [old_state, new_state]:
        [ElevatorState.IDLE, ElevatorState.LIFTING]:
            elevator_state = ElevatorState.LIFTING
            %ElevatorActivate.play()
            ...

…with the other big chunk (steady state) being managed in _physics_process by a big match on each of the possible states.

func _physics_process(delta: float) -> void:
    match elevator_state:
        ElevatorState.IDLE:
            return
        ElevatorState.LIFTING:
            $AnimatableBody3D.global_position.y += delta * LIFT_SPEED
            ...

Writing

The writing is subtextually somewhat personal, since it’s about dealing with death anxiety and the seeming meaninglessness of striving in the face of it—one of the biggest internal battles of my late teens and early twenties.

Gulliver and Redbeard represent two aspects of this internal argument, and two opposite ways to live. Gulliver is nihilistic, depressive, and generally despondent. This can come off as laziness—he doesn’t really do anything for long stretches—but he’s willing to admit certain truths (namely, the certainty of death) in a more honest way than Redbeard ever does. On the other hand, Redbeard outwardly seems to be having more fun. He’s mostly driven by learning new things, and figures he might as well strive for financial security along the way, but doesn’t really have an idea where he’s heading. His thinking lacks the sort of foundational philosophical basis Gulliver’s seems to have.

I haven’t written a significant amount of fiction before, so I don’t expect my first try to come off super great. It’s hard to write about nihilism without sounding overly edgy. Hopefully the setup is interesting enough? Dialogue is sprinkled into the game after beating each level, and is all skippable, so even if it’s awful maybe the gameplay can stand on its own.

Plenty of media deals with these themes of purposelessness, but normally it’s in the subtext, and not quite so straightforwardly out in the open. My characters literally quote Russian authors back and forth at each other. Again, maybe it comes off as simply the product of a novice writer. I hope it reads as more of an interesting inversion, where the story is so direct that it projects its meaning back onto the player. Or something.

Music

Making the music was one of the most fun parts of the whole process for me. It’s mostly somewhat-jaunty “pirate music”. All of it is orchestral, with sounds from BBC Symphony Orchestra Core, a plugin I really enjoy using and have repeatedly recommended to people. There are eight tracks, which by itself is more songs than I’d usually make in several months.

For a few of the songs, I worked out the main ideas by messing around on the piano first, before switching over to my DAW for orchestration. For example, here’s the track “Pirate Vivacity” in both piano (quantized/flattened) and orchestral forms:

The oldest file in the whole game is a bit of music I made three years ago, long before I ever had the idea for the game. I think I picked the name “Redbeard” as a contrast to “Blackbeard” to maintain a pirate-sounding theme. I had no idea that name would later be used for a game’s main character.

Mitchell Vitez · Redbeard's Theme

Everything else was made alongside the game.

Sound effects

The raw material for sound effects was one of the few things I didn’t make (or record) myself from scratch, instead using material from freesound.org. I did edit every sound, but in some cases these edits are much lighter than in others, and the original sound shines through.

As with music, I find sound design pretty fun, but the challenges it presents are pretty different. I’m often thinking about how to use more of the full spectrum of frequencies, as with the little “highend” bits mixed into the sound that plays when you hit a tentacle with a cannonball.

A lot of this work involved EQing, filtering, and messing with reverb, beyond just picking the right set of samples to use in the first place.

Certain sounds like the coin pickup, and pausing/unpausing the game were made mostly or entirely with sounds from BBCSO, typically on percussion instruments.

Get the game

If you’d like to support me, you can wishlist Redbeard’s Recoil on Steam and tell all your friends about it.