2025 SNESDEV Game Jam Postmortem

This is part 2 of my Space Rescue Squad postmortem. Part 1 covers the game engine past-me created, this page covers my jam's timeline and my thoughts on the game's development.

Spoiler warning: This blog post contains spoilers and screenshots for my Space Rescue Squad demo.

Project goals

I started the game jam with 5 distinct goals:

  • Revive my abandoned untech-engine.
  • Avoid changes to untech-engine to see how well the original design works for a real game.
  • Create a platformer game with music and sound effects.
  • Improve my pixel-art skills.
  • Learn how to prioritise tasks so I can submit the game on time.

I ended the game jam succeeding in all of them.

The timeline

Prepwork

The game jam allowed for tool and engine development before the jam starts.

I spent my prep-work time converting untech-engine from HIROM memory mapping to LOROM memory mapping. This took a 2 attempts. The first attempt was to move all of the entity functions to a new code bank - it did not go well. I made too many changes too quickly, got confused and did not realise how tangled the entity-loop and entity-processing code was.

For my second attempt, I wrote a simple script to measure the size of my subroutines to find my 15 largest subroutines and manually moved them to a new code bank. The moved subroutines are now invoked with jsl (jump subroutine long). To catch any jsr/rtl mismatches (which would cause a crash) I renamed all of the moved subroutines to use a __far suffix (ie, MetaTiles.Process__far). This caused lots of "unrecognized variable" assembler errors and gave me the confidence I needed to confirm my changes would not crash.

I also removed a few unused 32-bit math functions.

After a few days, I went from 8.3KiB free in the code block to 18.7KiB free. Leaving me plenty of space for the game and entity code.

Address | Size    | Used    | Free    | Block Name
--------+---------+---------+---------+--------------------------
 00ff80 |      80 |      80 |       0 | SNES Header
 808000 |     128 |       0 |     128 | CopyrightHeader
 808080 |   32528 |   24014 |    8514 | code
 80ff90 |      32 |      29 |       3 | interrupt_code
 818000 |   20480 |       0 |   20480 | farCode
Address | Size    | Used    | Free    | Block Name
--------+---------+---------+---------+--------------------------
 00ff80 |      80 |      80 |       0 | SNES Header
 808000 |     128 |       0 |     128 | CopyrightHeader
 808080 |   32528 |   13333 |   19195 | code
 80ff90 |      32 |      29 |       3 | interrupt_code
 818000 |   20480 |   10020 |   10460 | farCode

In the end I moved the following to the farCode bank:

  • Reset code
  • VBlank code
  • Entity tile collisions
  • Metatile processing (scrolling and tile animations)
  • A ValidateEntityLists debugging subroutine that crashes if an entity linked-list has been corrupted
  • unused HDMA code
  • unused u16 * u8 = u16 multiplication

Once I had a comfortable amount of free code space, I ported my Terrific Audio Driver's 65816 API to bass (making this port number 5).

The game jam

June was full of procrastination. By the end of June I had player movement, ladders and a basic walk-and-turn enemy coded.

An orange rectangle climbing a ladder An orange rectangle, 2 blue cleaning bots and 1 red cleaning bot
The state of my game at the end of the first month.


July was unlucky. I started July by accidentally breaking the OS on my PC and in the last week of July my home Internet broke (taking 12 days to fix).

By the end of July I had:

  • Inverted gravity
  • Ledge detection
  • Fixed a few bugs in the engine
  • Player hurt by enemies state
  • Player melee and projectile attacks
  • Reordered the movementState bits to simplify metasprite animation selection
  • Rewrote how the engine handles gravity
    • Added a table of gravity acceleration and maximum Y-axis momentum constants
    • Player and enemies can change their gravity based on their state
  • Transparent water that slows down the player and enemies
  • A debug and release build of the game
An orange rectangle swimming underwater An orange rectangle attacking a red cleaning bot with a solid red rectange melee attack An orange rectangle shooting 2 blue balls at a red cleaning bot
The state of my game at the end of July.

In July, KungFuFurby took my (admittedly short) description of the game and created a wonderful soundtrack using my Terrific Audio Driver. I cannot describe how thankful I am to him for making Space Rescue Squad's music.

Motivation during the game jam was hard, I kept loosing focus. Not having home internet made my motivation issues worse. I was unable to lookup reference material for enemy shapes or behaviour. I was also without access to the snesdev discord, which has been a wonderful sounding board, a great place to share my progress and see other people's progress in return.

I spent some of my time without Internet playing the SNES version of Super Mario Bros 2 and taking notes of interesting behaviour and observations. I felt that level of complexity and polish in SMB2 was perfect for the game I had in my head.


On the first week of August, I gave myself a deadline - Get the first level completed and ready for external play-testing by the end of the week. I did not meet this deadline. In retrospect, I mis-prioritised my goals and focused on completing the tiles instead of creating a play-test level with basic blocky tiles.

Mid way through, I reduced the scope of my game to a short 3 or 4 level game with a single boss and rebuilt my todo lists with priority tags. I was forced to choose what to prioritise and I prioritised game-play over polish like particle effects, smooth animations or a second background layer.

Once the tiles were drawn, I worked on the first level while also focusing player and enemy movement. I had to do a lot of minor tweaking to the player movement to prevent myself from jumping over all of the obstacles.

15 days before the end of the jam I gave the first level to Catyak for review. The feedback I got was encouraging and detailed. I implemented most of the suggestions in the review. I ran out of time to address them all.

An orange rectange standing on large wooden crates An orange rectange walking underwater An orange rectangle standing on a shipping container The end room, with "END" written in tiles
Scenes from the build I sent Catyak for review.


The final week of the jam was a mad rush. During the week I:

  • Drew the player spites
  • Added the animated change gravity tiles
  • Created the second level
  • Coded the boss
  • Drew and coded the title screen

The final day was even worse, I:

  • Added a scripting command to advance to the next level
  • Drew the credits screen
  • Drew the scientist sprites
  • Added the "CREW RESCUED" sprites
  • Redrew the organic enemy frames to look passed out instead of surprised/hurt
  • A ton of graphical cleanup
  • Created the third level
  • Added a "no crates" sign next to a pile of boxes
  • Lots of personal playtesting and quick bug fixes
  • Published the game to itch.io
Space Rescue Squad title screen The updated version of the player standing on a shipping container CREW RESCUED level end screen The player is leaving a broken glass animal enclosure The player is standing upside down on a platform and being attacked by a purple jumping enemy with regular gravity The boss of the game shooting a fireball at cowering scientists
The Space Rescue Squad.

I woke the next day to discover that KungFuFurby and Nova both encountered the same softlock in the second level. I quickly submitted a patch nearly 3 hours after the jam's deadline. In retrospect, I should have had someone test the second and third levels before the previous day.

The debug build

Midway through the jam I built a specialised slowrom debug build of the game that adds debugging commands on the second controller:

  • left - go to the first entrance of the previous room id
  • right - go to the first entrance of the next room id
  • up - load the scratchpad debug room
  • down - go to the first entrance of the current room
  • Select - load next level
  • X - invert player gravity
  • Y - heal
  • L + R - goto the title screen

More importantly, the debug build contains Save-RAM to allow me to quickly resume playback whenever the debug build is recompiled and restarted.

Before every room load the gamestate, containing all variables that persist across room loads, is saved to save-RAM1. When the debug build boots it will try to load the gamestate from save-RAM and then restart the gameloop. This creates a simple semi-hot-loading system that quickly restores my game whenever I recompile and restart the debug build.

I then used entr to write a script that would recompile the debug build and restart it (in Mesen emulator or my Super Famicom Console via usb2snes) whenever a resource file changed. Together, these three features allowed me to to get into a quick change-test-review loop when I was editing rooms, tiles, graphics or enemy behaviour.

Personal playtesting

Most of the play-testing was done by myself. In retrospect, I would have made a better game if I had more external reviews of my game before the release. Looking back I feel I wanted the second level (the gravity labs) to be a surprise - I do not think I revealed or teased the up-gravity movement on Social Media or Discord.

I played the game in multiple different configurations to ensure I had good level design and enemy placement:

  • On my 1-chip SFC console with a wireless SNES controller
  • On emulator with keyboard
  • On emulator with an xbox controller
  • Mesen emulator with custom overscan to remove 10 dots from the left+right and 16 scanlines the top+bottom to ensure no critical details were hidden for anyone playing on a CRT TV.

I also did a few playthroughs where I held the run button the entire session and playthroughs where I only walked and never touched to run button to ensure none of the jumps were too difficult.

Looking at the some of the playthroughs of my game on YouTube, I forgot to play-test a "defeat every enemy you see" style of game-play.

In hindsight, I should have had someone play-test the boss. On the final day of the jam I thought a 4-health boss way too easy and decided a 6-health boss was a good idea. After the jam some people found the boss difficult. It took a while to figure out the source of this difficulty - the boss is a lot easier to fight when the player is 4 tiles away from the walls. I'm not sure if I should fix this by shrinking the boss's hitbox or changing the boss's behaviour. External play-testing will be required to determine the better solution.

Screenshot of the boss room
Where I was when I was play-testing the boss.
Screenshot of the boss room
If the player is standing near the walls, the boss is significantly harder to beat.

The play-testing by Catyak was invaluable for verifying the first level was good. Watching YouTube recordings of Space Rescue Squad playthroughs were also valuable as they showed me a play-style I never considered. In the future I will commit to asking for and watching a blind playthrough of my games before I release them.

Graphics

Sprites

I originally chose a space-station theme because I believed a space-suited player and robots would be easier to draw than humans and monsters. I sketched a number of enemy ideas in a notebook and quickly realised I need organic enemies and environments to break up the monotony.

Scan of a pencil drawn spiral notebook
2 unused robot ideas.

The first enemy I drew was the cleaning robot. I started the bot mostly drawn (lacking the turn animation) before coding the walk and turn enemy behaviour. In retrospect, this shoehorned me into a specific cleaning-bot size before I committed to the player size.

spritesheet
The cleaning bot as I first drew it.

The second enemy I made was the security bot. That time I stuck with a basic outline of the major features of the bot and only focused on the security bot's size and shield position.

The remaining enemies started out as extremely basic single coloured outlines. I made sure to give each enemy state a different sprite frame (either by rotating their shape or drawing basic symbols) to make it easy to visually confirm the enemy code is doing the right thing without opening a debugger or staring at a hex-editor.

8 sprites, most of them are plain rectangles
The early prototype sprites.

After I was happy with the enemy movement and the first stage had been play-tested by Catyak, I redrew all of the enemy graphics.

8 sprites
The final version of the above prototype sprites.

I was never able to draw the underwater fish enemy in a satisfactory manner and simply kept the prototype black graphics as I preferred it to what I had tried to draw.

The boss was a rush-job (drawn and coded on the second last day). I took the quadruped enemy, scaled it up and repeatedly cleaned up the lines until it looked OK. The cowering scientists were supposed to be animated but it looked wrong and I didn't have the time to fix it, so I turned them into a static frame.

Scan of a pencil drawn spiral notebook
Original boss design.

On the final day of the jam, I realised I didn't like how the organic enemies looked when they were defeated. It looked cruel. I did not have time to add a poof animation and I liked how the robot enemies remained onscreen when defeated, so I redrew their faces so they looked stunned. This quick change had a major and positive impact in how I see combat in the game, the player is now armed with "stun weaponry" and not a leathal shooter.

Level tiles

When I built the engine I hard-coded it to use 256 16px tiles for the map's tileset. The engine was designed with lots of small rooms with a large of variety of tilesets.

I originally wanted different tilesets for the labs, maintenance tunnels and animal enclosure rooms. I quickly realised I didn't have the time for that and was forced to use the same tileset for all 3 areas.

The station tileset
The only tileset in the game.
Pink is transparent. The dark purple tiles are unused.

256 tiles is not a lot space for a tileset that has angled floor and ceiling tiles. I had hoped I could fit 2 different ceiling and wall styles but it would have significantly reduced the amount of decorative and obstacle tiles I had to work with.

I ran out of time when drawing the tileset. I was not able to come up with a good left/right wall design and ended up using a 2 pixel back border. I was also unable to draw a good underwater coral tile and ended up reusing the rock tiles.

In hindsight, I'm glad I didn't draw too many decoration tiles and focused my efforts on other parts of the game. The limited obstacle tiles turned out to be good for environmental storytelling reasons. The station's scientists are obsessed with crates (why not) and rocks (because they are studying them).

Speaking of environmental story telling - On the final day of the jam I was working on the final level and felt like the 2nd room was a bit bare and quickly drew a no-crates sign. This provided a good explanation for why the security robots were there and I quickly backported the idea into the second level.

Three large compressed stone tiles, two of the floor tiles are cracked A bunch of wooden crates below a down-gravity emitter
Large crates near two no-crates signs with security bots nearby.

Enemy and player behaviour

The player and enemy behaviour were the most successful part of the game. I drew from my experience from creating unnamed-snes-game and used state machines and function tables for the entity code.

Starting with rectangular sprites defiantly improved how I coded the enemy behaviour. It was a lot easier to resize a simple rectangle if I felt the enemy's proportions or movement speed do not look right against the player rectangle.

All enemies (except the fish and boss) had to handle inverted gravity and underwater movement. It was surprisingly easy. Inverted gravity is a single movementState bit and the inverted sprite animations are easily handled by careful arrangement of the animation order.

For underwater movement, all rooms in my game had waterline (even when no water graphics are loaded). On every frame the entity's y-position is compared with the water position and the entity's state and/or physics-values as required. I had to copy the water-position test to every enemy as they all have different behaviour when underwater. The water position is a gamestate room variable and can be set or changed via a bytecode room script, but I never made a room with a moving waterline.

I deliberately prevented the player from crouch-shooting to force the player to dodge the cleaning robots in the collapsed bridge sections. In retrospect, this makes sense since the player is in a space suit. To keep things fair, I added a melee slash attack that can hit the shorter cleaning bots.

Major bugs

4 major bugs exist in the game. I have only fixed one of them.

The softlock

On the day the game jam ended, KungFuFurby and Nova had both encountered a softlock in the 5th room in the gravity labs. If the player jumped across the gap in the bridge and over the large compressed rock they end up stuck and cannot reach the up-gravity emitter. If the 2 enemies are defeated the game is softlocked and must be reset.

I quickly updated the room and posted a patch 3 hours after the jam's deadline. I made it impossible to jump over the large obstacle and placed few rock tiles so the player could reach the up-gravity emitter if they (somehow) got stuck in that spot with down-gravity.

In the full game I should add a way for the player to restart the level from the pause screen, just in case I miss another game-breaking softlock.

The player can jump onto a ledge of the large rock The player failing to jump onto the large rock
The large rock I forgot to check if the player could jump over and the rock after I fixed the softlock.

The player clings to angled ceilings

It's possible for the player to stick and slide along an angled ceiling when holding the left or right button.

The player is sliding along an angled ceiling in the staircase room.

I noticed this bug when I was building the level but never devoted the time to investigate or fix it. Instead I minimised my use of angled ceiling tiles in the second and third level.

Gravity labs ending

Jumping onto the table to reach the flashing button does not end the level.

Two scientists are suspended mid-air, the player is standing on a table next to a big red button
The big red button at the end of the second level.

To end the level the player has to walk below the table towards the button.

I blame tiredness and a lack of external play-testing for this one. I added the flashing button tile the day before the jam ended.

The boss

As I mentioned earlier, some people reported that the boss is too difficult. I am unsure if this would have been caught with external play-testing.

Retrospective

What went well:

  • The engine. It met my expectations well.

  • The debug build. Being able to recompile the entire game and have the game quickly reload the level I was play-testing in under 3 seconds was invaluable when manipulating enemy and tile placements.

  • Player movement. I spent a lot of time on it and it shows. I'm proud of how it turned out.

  • Enemy behaviour. It was simple, effective and had a nice variety.

  • Placeholder enemy and player graphics. Focusing on player/enemy behaviour and sizes before drawing the sprites was the right call.

  • The 3 levels in the game. I liked how they turned out.

  • Prioritising what to work on next. I knew when to say enough was enough and move onto the next major task to complete. Without it I would have never completed the game in time, or worse released a game with only a single level.


Areas for improvement:

  • I has difficulty discussing and promoting the project online.

  • Designing and drawing the tileset. This will get better with practice.

  • Designing a second background layer. (It's been nearly a year later and I still haven't drawn one.)

  • I need more external play-testing before release. I feel like some of my bugs would have been caught earlier if I had someone with a fresh set of eyes do a few playthoughs of the game.

  • Time management. I procrastinated a lot at the start of the jam and the remaining items in this list exist because I ran out of time.

  • Graphical effects. The game would look a lot better with a second background layer and particle effects.

  • Sound effects. I wrote 22 simple placeholder sound effects and never had the time to learn how to improve them.

  • The title screen. It needs to show the rescue squad flying towards the damaged ship at minimum.

  • The credits screen. I did the bare minimum and it shows.

What's next?

I want to continue working on Space Rescue Squad and turn it into a full game covering (at minimum) 4 different areas of the ship.

I had intended to work on it earlier in the year but between motivational issues, artists-block with the second background layer and the audio driver taking priority, I haven't made any changes to the game since I released it.

At the moment I am working on adding sample swapping to the Terrific Audio Driver and during August to October I will be participating in the 2026 SNES DEV game jam.

I will return to the game in November.

Special Thanks

Space Rescue Squad would not exist without the following people:

  • KungFuFurby for creating the music and beta-testing the Terrific Audio Driver
  • Catyak for playtesting the first level
  • Past-Me for writing a complex but useful game engine
  • Everyone on the SNESdev discord who offered words of encouragement
  • Kannagichan, Svambo and SNES-Testberichte for organising the game jam
  • Goldlocke for making a physical cart with one of my games on it

  1. The Save-RAM format is extremely basic: a copy of the ROM's SNES Header excluding the checksum bytes and 2 copies of the GameStateData struct. The save is considered valid when the header matches and the two GameStateData copies are byte identical.