Horseshoe Crab Rescue! 2D - Postmortem, Part 1
Horseshoe Crab Rescue! 2D is my first game for the Nintendo Game Boy and Analogue Pocket. I developed the game in GB Studio 3.2. In this postmortem, I will go into entirely too much detail on the ups and downs of developing the game, with a particular emphasis on dealing with and working around the challenges and limitations of GB Studio.
Note: Naturally, the GB Studio 4.0 beta went live on the same day that I released HCR2D. Please bear in mind this postmortem is talking about GB Studio 3.2.
Background and Concept
I first became interested in making a Game Boy game when I learned about GB Studio after purchasing an Analogue Pocket. I've been working as a freelance game developer for ten years and have a wide range of skills, so I am comfortable developing games solo and decided to make this another solo project.
When I decided to make a Game Boy game, I had three basic criteria for the project:
- The scope of the game should be limited enough that I could develop it in my spare time, without it impacting my full-time freelance work.
- The game should be fully compatible with the original Nintendo Game Boy (DMG).
- The game should be somewhat unique. I didn't want to make just another platformer or Zelda game.
I've always found demakes fascinating, and almost immediately I thought of doing a demake my own game, Horseshoe Crab Rescue! for the New Nintendo 3DS. The original HCR was a relatively simple game in which you run around the beach, rescuing horseshoe crabs that have gotten stuck on their backs (by the way, this is a real thing that people actually do in real life). I feel pretty confident in saying that it's also one of the only video games that's ever been played by a chicken:
The original HCR was also a spare-time project, so it's a relatively simple game. As soon as I thought of doing a demake, I could picture the top-down adaptation, and it seemed like a great fit for the Game Boy. So I downloaded GB Studio 3.2 and got started.
Crashing into the performance limitations of GBVM
Before I started learning GB Studio, I did some basic research on the underlying tech, as well as the capabilities and limitations of the engine. One of the most obvious limitations was the hard limit of 40 actors in a scene, and soft limit of 10 actors on-screen at once (in practice, you can actually have around 12). These seemed like reasonable limitations, and I felt confident I could work with them when designing levels.
I also read that the engine runs user scripts run in a virtual machine called GBVM. Uh oh. More on that later...
I downloaded GB Studio 3.2 and started exploring the scripting system. I began by trying to make a character wander around randomly, since the horseshoe crabs would do this after being rescued in the original 3DS game. It was easy to whip up the script and it performed perfectly when there was only one actor moving. When I was happy with the script, I made a half-dozen copies of the actor and ran the scene again.
This was my first crash-course into the severe performance limitations of GBVM. When all 6 actors were wandering around at the same time, performance slowed to a crawl. Then I realized that it was even worse - I still had Color Mode enabled, which lets your game take advantage of the double processing speed available on a Game Boy Color (CGB), but I wanted my game to run smoothly on the original Game Boy (DMG). As expected, when I turned off Color Mode, the performance nosedived if more than two actors were moving around simultaneously. The game would be totally unplayable.
If you're not familiar with virtual machines - a VM is software that does some or all of the things that are normally done by hardware. The problem with VMs is that they are slow, since our real CPU is having to run a ton of extra code to simulate the virtual hardware. Put another way - our real CPU might have to execute tens, hundreds, or even thousands of real instructions to run one virtual instruction.
GBVM is simple and relatively lightweight VM, but even then, its command processor function (which is written in native SM83 assembly) is around 85 lines of assembly. In other words, it takes around 85 real instructions to run one virtual instruction.
Looking for solutions
I knew I couldn't make the game I wanted to make if I was limited to only moving two actors simultaneously, so I began searching for possible solutions. My first thought was that I could simplify the movement logic by simply having the horseshoe crabs flee straight off of the screen after you rescue them. Then their movement logic would simply be "move up each frame" instead of the much more complex "wander randomly" logic. This was better, but still performed too poorly when more than a few actors were moving at once, even with collision checks disabled.
There's a lot of overhead in running multiple update scripts at once, so I tried having one actor control the movement for every horseshoe crab. This eliminated the performance issue, but in a different way than I wanted - a single script can only run one movement operation at a time, so the horseshoe crabs would take turns moving. This looked fine with a few of them, but was way too slow with 8 or more waiting to move.
At this point, I debated abandoning GB Studio and building the game in native SM83 assembly, or in C with GBDK. However, either of these approaches would have increased the scope of the project beyond what I was comfortable with for a spare-time solo project.
I then started exploring modifying the GB Studio engine. Thankfully, GB Studio has an "Eject Engine" option which dumps the C source code for the engine directly into your project, allowing you to modify the engine (GB Studio will then recompile the engine from your modified source when you test or build your game). The first thing I tried was writing some movement logic in C by modifiying the actors_update() function. Success! The performance was far better; with simple "move continuously in one direction" logic, I could have 10 actors running across the scene with no performance loss! So now I had a path forward.
Making engine modifications work with the Project Editor
While modifying the engine code allows us to create high-performance game logic, it's not trivial to coordinate what we're doing in C with the scenes we've set up in the GB Studio Project Editor, since our C code doesn't have direct references to any specific assets in our project. The first challenge I tackled was coordinating actor state. The horseshoe crabs would need to have a minimum of two states, "flipped over" and "upright". GB Studio gives us a few local variables for each actor, but these are not really accessible outside of script events, which I was specifically avoiding. So I would need to synchronize state using global variables, which are accessible in the script_memory array in C.
The implementation sounded easy. Each actor has an index in the scene, and the player is always at index 0. I could store the state variable for each horseshoe crab into the global variable with the same index. So for example, when it was time to update horseshoe crab 5, I could check the state in script_memory[5] and then behave accordingly.
The problem is that GB Studio doesn't actually give us a way to check the index of an actor, either in the Editor or in the engine. This was a very frustrating limitation. I tried dynamically calculating the index using simple pointer math, but for some reason (possibly a bug in the compiler) this compiled into an extremely complex function that cut performance in half. I ended up adding a new "index" field to the actor data structure and setting the value for each actor when the scene loads. This way, I could access the state variable with UINT8 state = script_memory[actor->index], which performs very well.
Later, I tried adding a third horseshoe crab state, "wander", which runs the wander movement logic in C. This performs fine as long as the actors aren't doing collision checks. This let me using wandering horseshoe crabs as obstacles in some of the stages. Because they don't do collision checks, the wandering horseshoe crabs sometimes move through something that should be solid, but this happens infrequently and I decided that it was an acceptable compromise.
I did still create some scripting logic with scripting events or GBVM scripts. For example, when you interact with a crab that's on its back, it gets flipped over, displays an emote, plays a sound effect, and you earn a point. This logic is all in a custom script in the Project Editor, since it doesn't need to run constantly and the performance hit isn't noticeable.
-----
Well, I think that's enough for today. Stay tuned for Part 2, where I'll discuss the HUD, background animations, music, and more!
Get Horseshoe Crab Rescue! 2D
Horseshoe Crab Rescue! 2D
Run around the beach and try to rescue every horseshoe crab before time runs out!
Status | Released |
Author | Kevin Foley |
Genre | Action |
Tags | Casual, Retro |
Languages | English |
Accessibility | Color-blind friendly |
More posts
- Horseshoe Crab Rescue! 2D - BalanceMay 28, 2024
- Horseshoe Crab Rescue! 2D v1.0.1May 21, 2024
- Horseshoe Crab Rescue! 2D - Postmortem, Part 3May 20, 2024
- Horseshoe Crab Rescue! 2D - Postmortem, Part 2May 16, 2024
- Horseshoe Crab Rescue! 2D v1.0.0May 14, 2024
Leave a comment
Log in with itch.io to leave a comment.