Distant Paradise Postmortem
Distant Paradise is a small metroidvania I made for Metroidvania Month Jam #28. This is my third entry for this jam, following Silent Paradise in 2023 and Lost Paradise in 2024 (seeing a pattern yet?). Like every other game of mine, I’m the sole developer.
This document is just me rambling about the development process, thinking about what went right or wrong and hopefully learning some lessons for my next games. Because I do everything myself, it covers everything, so feel free to skip the parts you’re not interested in. Hopefully some of this may spark ideas for your own process.
3, 2, 1, Let’s Jam
Another jam? Why?
Back in Silent Paradise’s postmortem, I said the jam was a nice one-off but I wouldn’t do one again. Then I joined GBA Jam soon after and did another MVM before the year ended. And now, not even six months later, this one. The takeaway? Don’t listen to me, I obviously don’t know what I’m doing.
More seriously, a better takeaway would be: jams are nice, actually. Months-long jams, at least. I consider myself a relatively disciplined person, but man are external deadlines powerful! And while I’m not too active in online communities, it’s nice to talk to others, show your work and look at theirs. It’s less lonely.
So I’m not going to pretend it’s the last jam I will ever join (though it’s probably the last “Paradise” game), but I will still reiterate I have different goals for Dreamnoid. It would be best if I could inject some of that “jam energy” (“jenergy”?) into my regular process.
The goal
Every time I approached this jam with a different side objective. For Silent Paradise, it was about making a full-blown “Dreamnoid game” in just a month. Lost Paradise was about experimenting with sequence breaking and melee combat. This time I wanted to try a new technical approach and make a web build.
Starting with Aereven Lunar Wake, all my released games are made with the same custom engine: Dream, a modular C# engine I’ve built and developed for six years now. I can do a lot of cool things quite quickly with it, as demonstrated by my previous entries in this jam, which were all pretty long and complex games considering they were made in a month each.
But while most people were impressed by the scope of my games, they got way less plays than other entries with a web build. I’ve been told this pattern holds true not only for jams but across the entirety of itch. People just don't want to download random games on the internet! I believe that after all these years I’ve earned players’ trust, but what numbers would I see without this additional friction? Would more people discover my work and give a chance to the other downloadable games? I wanted to try it out at least once and this jam was the perfect opportunity for it.
The need for a new engine
The thing is, getting .NET to run inside a browser is a mess. I have no doubt it will get there one day, but not today. I couldn't just port my engine, I needed different tech this time around. Which was fine, because there was one specific approach I wanted to try anyway.
Last year, I entered the GBA Jam. While I’m pretty sure I could compile Dream ahead of time for all modern consoles, there was just no way that would ever work on a GameBoy Advance. That’s why my entry, Aereven Advance, used its own one-off engine, written in C but featuring an ubiquitous scripting system that powered pretty much the entire gameplay.
While compiling C# to WebAssembly is a mess, compiling C is straightforward. Making a homebrew game taught me how to build a low-level engine from nothing, not even the standard C library, so I had a pretty good idea how I could get it to work. I also had learnings from the GBA Jam that I was dying to put in practice. It was a no-brainer: tech-wise, Distant Paradise would be a successor to Aereven Advance.
Initial design
With that tech roadmap in mind, I now needed a design to go with it.
I already had been toying with a concept for Distant Paradise long before the jam was even announced. I had an idea for a smaller game where you explore a temple built by an ancient civilization, hidden in the jungle of an alien planet. It was originally meant to be an exclusive “bonus game” to accompany the Silent Paradise Collection on Steam, maybe as a way to justify the games costing money while they were free on itch (well, that part of the plan is now ruined).
When the jam started, the theme of “evolution” was chosen and I wondered for a while if I could integrate it with my pre-existing concept for Distant Paradise. It wasn’t as easy as with the first two entries where the theme immediately sparked new ideas, but I kept thinking about it as I got the basic tech up and running, until I found an angle I liked.
The entire “Paradise” series (which was never meant to be one, but here it goes) is meant to have pulp-y sci-fi vibes, often referencing classics like Cameron’s The Abyss or Doyle’s The Lost World (Silent Paradise and Lost Paradise, respectively). For this one, I went with Clark’s Childhood’s End and Boulle’s Planet of the Apes. The initial concept was always about a lost figure (sometimes a young alien, sometimes the astronaut we now have) exploring a temple in the jungle, but the story of an ancient civilization that went post-physical and has now been replaced by sentient apes came from the theme of “evolution”. In a lot of ways, it was the missing piece of the puzzle.
Producing
Pocket Epics
I knew right from the start I would need a smaller scope to account for the new engine I also had to make at the same time. But not too small, as I still wanted it to feel like an adventure, with some exploration.
After Aereven Advance, I came to refer to my action-adventure games as “pocket epics” which perfectly describes this balance I’m trying to achieve. We will see later that this new tech brings constraints, and learning what the right constraints are for me and how to work with them to make those “pocket epics” was part of this experiment.
As it turns out, the final game is pretty much exactly as I envisioned it. Everything I had planned is in the game and nothing more. The final product is exactly what I had on paper.
And yet, it wasn't exactly smooth sailing.
Battle plan
When I first joined this month-long jam, I came up with a very simple schedule.
The first week would be dedicated to designing the game. Pre-production, if you will. Coming Sunday, I should have an exhaustive list of everything needed for the game to work and a solid plan to make it. Then two weeks of actual production, building the world and implementing the gameplay until I have a content-complete alpha version. This version would be rough, probably buggy, but playable from start to finish. Then the last seven days could be dedicated to playtest and polish.
It worked great for Silent Paradise but I was already running a bit late for Lost Paradise. This time around? I reached alpha status mere days before the deadline. For the longest time I was missing important chunks of the experience, entire biomes that were only half-designed while I was busy implementing a title screen or a settings menu (mandatory stuff I already had available in Dream but needed to recreate from scratch here). Not gonna lie, I was a bit scared at times. But thankfully the dominoes finally fell in place and the game coalesced right as the jam ended. The lack of iteration can still be felt in some places, but it is a complete and coherent game. Phew!
Assets
I’m not shy about reusing assets. No matter what some “gamers” may think, I believe it’s good practice. My favorite game ever is Zelda Majora’s Mask, which is pretty telling. Nihon Falcom and From Software are my inspirations, process-wise. Over the years, I’ve been accruing a good collection of sprites, tiles, SFXs, etc. This “snowball effect” is part of my long-term strategy for Dreamnoid and a reason I can make large games by myself with relatively short development times.
But I used very little of that this time. Because you see, I wanted to do things differently…
Pixel art
I’m not a real artist: I already juggle too many roles to really train so I’m just good enough to get by. But pixel art is often more of a puzzle. You have a 16 by 16 grid of colors to represent a monkey: where do you place your pixels? What do you include and what do you leave to the imagination? I love that! This is 100% in line with the “pocket epic” ethos. But even though I’ve improved over the years, I was always missing a part of this puzzle: a constrained color palette.
With the indie boom of the last decade(s), pixel art games are now everywhere and there’s only so many ways you can fill this 16x16 grid. You may think they would start to all look the same by now, right? (Well I’m sure they do for some really untrained eyes.) And yet, some of them look cheap and amateurish while others are super clean and evocative. What’s the difference? And how come some of the most awesome pixel art games have a smaller resolution and color count than many that look worse? I may die on that hill but I believe Zelda Link’s Awakening on the GameBoy looks better than A Link to the Past on the much more powerful SNES. How is it possible?
I believe a well-chosen constrained color palette plays a huge role in that. You can just feel that cohesion. The colors rhyme with each other. There’s a feeling of effortless mastery you don’t get from a less constrained piece.
And for my non-artist lonesome self, the goal has always been “quick to make but looking at least competent, if not good”. That’s one reason I’ve been so reluctant to make a 3D game despite being attracted to the format: it’s very hard to make one that’s simple and yet pleasing to the eye (and not janky).
So this time I grabbed a 32-colors palette and ran with it. Some sprites were adapted from my assets bank and recolored, but the vast majority is brand new and made for this palette.
It’s not a great palette. I don’t think it’s terrible either, but I had a hard time with it. Some colors I used everywhere, others barely if at all. With only 32 of them, you can’t really afford that disparity. Many times I was tempted to cheat and introduce a new color, but I held back.
The result? I believe Distant Paradise looks better than my previous offerings. It feels “rounder”, more cohesive. More confident. So that’s great! I just need a better palette next time.
Character design
I usually make two kinds of protagonists: if the story doesn’t call for a well-defined character I will try to provide a self-insert for the player. Gomez from Lost Paradise was the former and the Auditor from Silent Paradise was the latter. Here I wanted the lone astronaut to be another self-insert. And something I like for self-inserts is making them real blank slates. What’s the gender of the Auditor? What’s their skin color? Their age? No way to know. They wear a suit and a helmet and the SFXs are all bips and boops without a hint of voice to go with it. They could be anyone. They’re meant to be you.
But it’s easier said than done. I’ve been trying this approach ever since The Lightkeeper and even then I had to show some skin tone. You can probably interpret it as a wide range, but sadly not the full spectrum. Silent Paradise uses realistic proportions which gives the Auditor a certain body-type. This time around, I struggled to get a compelling design for the astronaut while making them completely anonymous. I ultimately resolved it by giving them a visored pilot helmet and a scarf, Little Prince-style. The scarf is especially good because it not only hides the lower part of their face, it also provides much-needed secondary movement, a role usually left to hair. Sadly, the illustrated key art looks both white-skinned and masculine, but I consider it just one possible interpretation out of many. The sprite and your imagination are canon!
The sentient monkeys? Yeah I just grabbed a screenshot from the original Planet of the Apes as reference. With so few pixels to work with, there’s not much room to deviate and still evoke your inspiration.
Environments
This brand new tileset was really an exercise in minimalism. I tried to get something versatile with as few tiles as I could get away with. One of my fears was that it would quickly look same-y and players would not differentiate one room from the next. I haven’t received any feedback in that sense though, so maybe it was fine.
One issue I had was differentiating the background with the foreground. At first, I thought I would have enough contrast between both sets of tiles that it would read well, but the limited color palette was an obstacle. I eventually relented and introduced a “fog color” to overlay on top of the background, similarly to what I did in all my other games. It worked, except in the Secret Lab where I couldn’t get a satisfying contrast between the “out of bounds” foreground tiles and the background. It’s not too much of an issue because you always have contrasting edges in between the two, but it’s not ideal. I tried tweaking the fog color but I couldn’t find one that worked to my satisfaction so I dropped the issue.
Music
I’m no artist but I can draw. I’m no musician but I… no actually, I cannot!
Recently, the word “solo dev” has been, like many words, losing its definition. When Geoff Keighley takes the stage to praise a “solo dev and nine of his friends”, you know the word has become a marketing tool. Why is beyond me: there’s nothing glamorous about being alone and overworked.
But I still consider myself a true solo dev. Nobody other than me has ever been working on my games, even when there have been multiple names in their credits. I’ve used public domain music tracks before, but their composers don’t even know I exist! This weirdly parasocial dynamic has been bothering me for a while. Also, I don’t like hearing the soundtrack of my game elsewhere. And I must admit I aspire to have complete control over my work, even if it means doing less things. (And maybe avoiding that stupid “solodev” discourse is part of it too. No need for caveats if your name is truly the only one in the credits.)
So what can I do as a programmer, with no musical talent?
Procedural generation, of course.
(Because everything is terrible these days, I feel the need to clarify that procedural generation has nothing to do with machine learning or generative AI. The word ‘procedural’ means someone bothered to actually create their own procedure and you don't need a planet-burning GPU to throw a dice. Everything I did here you could do with pen and paper.)
A few years back, I absorbed as many YouTube videos about Music Theory as I could stomach and wrote a music generator. I called it Amadeus, because I’m modest like that. The first version offered me a lot of control, from the tempo to the key, the instrumentation and the chord progression, etc. And the result is what you can hear in Hotel Demonica, Silent Paradise, Aereven Sleeping Azure and Lost Paradise. It’s serviceable background music, best left pretty quiet in the mix. But one thing that bothered me was the lackluster melodies. Video game music thrives on melodies. Koji Kondo and Umeatsu Nobuo were able to build masterpieces on a NES! With a box of 8bits sine waves!
So I took one day to experiment with a new branch for Amadeus. This time I focused mainly on generating a good melody. I drew some inspiration from Christer Kaitila and decided that no actually I would not start with the signal but dive right into the noise. I wrote a much simpler algorithm, made every single variable random instead of handcrafted and just generated 100 tracks.
Then I sat down to listen to them all and sort them into “Good” and “Meh” folders. 30% of them ended up in the “Good” folder. Thirty percent! After all the music theory I had codified for the first version, I could not believe that “infinite monkeys on a typewriter” would actually produce better results. Defining a handful of simple patterns and rules to arrange them was enough to create convincing melodies (to my untrained ears, at least. Actual composers are free to roll their eyes at me).
All the tracks you can hear in Distant Paradise, including the combat music, are from this one batch. And remember: it was only one day of experimentation. There’s so much more I could do with this to improve it.
Programming
I’m a programmer, so this section will be the longest and most detailed. Non-programmers are free to call me a nerd and move on.
In the previous episode…
If you want the complete chronological experience, you can read my Aereven Advance postmortem to get the full context. Otherwise, suffice to say this new engine looks somewhat similar to the one that powered my GBA homebrew. Its predecessor’s architecture had been mostly improvised, but this time I had a clearer picture of what I would need and how I wanted to do it.
The Fantasy Console fantasy
Aereven Lunar Wake may have been the first game I shipped with Dream, but it wasn’t the first I was working on. Part of the need for this custom engine was to quickly get new projects and ideas up and running. My discipline wasn’t great back then (I learned a lot from shipping The Lightkeeper and the games that came after) and for every game of mine you see released, plenty are left on the cutting room floor. So it was imperative that I could just reference the engine’s DLLs and get something moving on the screen without further setup.
But that means all my games directly target the same version of those files. The moment I shipped Lunar Wake, I had to be careful about not breaking it when I changed the engine for The Trespasser. And I made many other games afterward! Thankfully, Dream is a modular engine, so I can move code around, promote, demote, deprecate or replace entire modules. But it’s still a bit scary, and gamedev is best when fearless. I want to update my games for decades to come without fear of introducing regressions!
I was also starting to think about preservation. Lunar Wake is already half a decade old! Would it still run in five, ten, twenty years? .NET is open source but would it still be actively maintained if Microsoft lost interest? (Remember XNA?)
I used to make RPG Maker 2000 games in the early aughts, and guess what? Brave souls reverse-engineered the engine to create EasyRPG, an equivalent runtime that allows those games to be preserved and even ported to new platforms. Games from the NES era are still playable today, thanks to emulation. That’s something to aspire to! But how to do that?
I’m fascinated by virtual machines and at the core of all of them is a contract: a list of instructions you can always rely on. Targeting them is a puzzle just like pixel art: how are you going to arrange those few instructions to build new, interesting things? I was also inspired by what I saw on the GBA: the hardware knows what a tilemap or a sprite are. Nowadays those are considered to be parts of our game engines. Does that mean the GBA is half hardware and half game engine? What about PICO-8, which is even higher level and purely virtual? My “pocket epics” share many similarities, could I rationalize them and codify them into a virtual machine spec? Then that would be my reusable game engine, a target that wouldn’t move (until its Super successor came out, of course) and could be emulated in the future.
I called that experiment DreamBox. I built a few versions, but I quickly hit a problem: making a good API is hard.
I mean, where do you draw the line? When designing a regular virtual machine, you may think your goal is clear: you want as few instructions as possible, as generic as possible, with maybe some compromises when performance is an issue. Right? But then, if you read Microsoft’s “The Book of the Runtime” you will learn that many .NET constructs were added to the instruction set not because they were strictly necessary but to force the different CLR languages to share a common base. Clearly, there’s many ways to approach this.
Until now, my own approach had been to stash as many things in the engine itself, with countless classes, parameters and methods, ready to be reused. To the point I often forgot I had a ready-made implementation lying around. The hard work was in the engine itself, to make sure making a game was as easy and as fast as possible. It’s the complete opposite goal from a virtual machine!
On the other hand, this fantasy console required threading the needle. The virtual machine can’t be so high-level and monolithic that you’re drastically restricted in the kind of games you can make, but it can’t be so low-level and generic that every game needs to reimplement everything everytime. Surely there’s a sweet spot between the two?
I couldn’t find it with DreamBox and I didn’t really ask myself this question for Aereven Advance because I was too busy making a game on the freakin’ GBA. As a continuation of my research on this topic, Distant Paradise needed to be a bit more deliberate. For instance, Aereven is a top-down Zelda-like and Distant Paradise is a sidescrolling metroidvania: could a single instruction set cover both genres and their specificities?
In the end, I still needed to make a game under a tight deadline, so Distant Paradise’s engine is not quite this fantasy console I had in mind. But it would be interesting to clean it up after the jam and see how different a game I could make without changing a single line of C code.
The Runtime
Alright, so enough with the set dressing, how does the engine actually work?
As mentioned before, the runtime is written in C. Not C++, just plain C. Why? Isn’t C++ better? While the latter is certainly more powerful, it’s also a real ugly beast. C is far from perfect, but it does have a certain elegance to it. Its concepts are few and simple, not the Jenga tower of rules and gotchas that plague its successor. C is also extremely portable, due to both this simplicity and its history. It’s kind of the lingua franca of the programming world.
In this case, plain C also means “just C and nothing else”. Not even the standard library. If I need a function to compare two strings, I have to write it. Malloc? Nah, we’re not doing that here (everything’s pre-allocated). When I started compiling my code to WebAssembly, I could actually follow the generated instructions (it quickly became too big, but it was nice while it lasted). Math functions can be trickier, so the sin() function is exposed by the host JavaScript and sqrt() calls the built-in intrinsic instruction present in the WASM spec.
As mentioned above, this C code is essentially a specialized virtual machine. It loads and interprets instructions prepared in advance. Those instructions are provided by the “ROM” (read-only memory) file and describe the game, its content and rules, etc. This means the C code is basically a gigantic switch statement inside a loop: you read the opcodes for the next instruction, switch to the case handling it, and then you move on to the next instruction (or jump elsewhere). This virtual machine is stack-based, meaning instructions exchange data by pushing and popping numbers on a stack.
Debug Features
When I first experimented with DreamBox, I thought all debug features would be part of the “devkit” virtual machine implementation. But it meant having a high level understanding of the game’s data, which quickly turned out to be impractical. As an example, Aereven Advance’s runtime knows what an “item” is. The engine has access to a list of item definitions, with very specific fields it can use to display them in the inventory. Distant Paradise does not, which affords it a lot more freedom to handle items the way it wants.
I also knew the “devkit” would never be able to offer on its own all the debug commands I would need. The “ROM” would always have to be able to specify custom debug scripts.
One thing that kinda worked for Aereven Advance was making a debug room filled with all items, enemies, warps toward every destination and special NPCs that triggered debug scripts when talked to. After all, it did not require any specific debug feature to build this. But one drawback was that you couldn't easily return to it, and back to where you were before. I needed a portable debug room.
For Distant Paradise, the ROM provides a single script called when I press a specific button in debug mode (which can never be enabled in the web build, and only if the executable is given a specific argument in the Windows build). This script then reuses the instructions for displaying the inventory menu, except instead of adding actual items to it, it adds debug commands. Each command is associated with its own script to call when I select it in the menu. It worked really well! The only specific debug feature is registering the debug script, and everything else reuses the existing instruction set (in the future, I think I can even simplify it further by adding a “Debug” button to the virtual gamepad). During development I added commands to warp to any save points or other points of interest, commands to get any item in the game, and a last one to heal.
Platform Layer
Dream, my regular engine, is built around the idea of an abstract platform layer. I did the same here. Of course, being written in C, I didn’t have access to fancy interfaces and virtual dispatch, but my needs were simple enough that “extern functions” did the job.
Multiplatform
I originally started with only the web version. I knew I would still need a downloadable desktop build to go with it, but it wasn't my priority. I wanted to make a web game! But after I got my proof of concept up and running, it became apparent that making the game this way would be suboptimal, just as it was when I made my GBA game. One of the highlights of making Silent Paradise was the hot reload. I could very easily change the game, not just the level design but also the code powering the various abilities or enemies, and press F5 to reload the scene and check it out, without having to relaunch the game and move back in position. I also needed debug draws to tweak the hitboxes, etc. The web build allowed for none of that. I needed a desktop version with integrated development features or I wouldn't be able to make the game quickly enough to meet the deadline.
I saw two possibilities for this desktop version. The first one was an idea I had when I was evaluating the fantasy console concept: building a “devkit”. A devkit would be another implementation of the fantasy console specs but in C#, powered by my good ol’ Dream engine and featuring hot reload, dev tools, debug menus, etc. Basically an emulator only I would use while I was developing the game. But this approach scared me. After all, I was already coming hot with the official C runtime, I had no time to maintain another one on the side. The tech wasn't mature enough to afford it. (And even now that the jam is over and I’m trying to integrate Distant Paradise into the anthology bundle, I decided against reimplementing the runtime in C# and instead wrote code to automatically transpile WAT to C#... and the crazy thing is that it works.)
The second option was to reuse the C version, but build it for Windows, with added functionalities. That’s the track I chose. The way WASM works is that all the functions exposed by the JavaScript host are matched with C extern functions, just like a static linker would. So I could implement the same functions in a separate windows.c file and link my game with it to seamlessly replace the JavaScript host.
To get a window and everything I could simply reuse my usual SPF library. But it’s a C++ lib and I didn't want to mess with static linking between the two languages so I simply dropped the DLL in the same folder and called LoadLibrary, exactly as if I was p/invoking it from C# for Dream. It worked great. I had this desktop version up and running in a couple of hours and it instantly became the version I was using to develop and test the game.
Wild West Web
So much that I kinda left the web version on the side, setting myself up for some nasty surprises when I had to get it on par with the desktop build.
One such occurence was the renderer. I originally wrote the web version with the HTML5 Canvas API, as it’s the simplest way to get something on screen. But it’s not good. If you want to change the color of the next rectangle you’re about to draw, you need to format it as a CSS string! A string that you know the browser will have to parse to extract the components you already had to begin with! It is madness, a terribly inefficient API that is symptomatic of the web ecosystem. But still, it worked and apparently fast enough (somehow) so I let it slide.
Until I noticed my sprites were not tinted properly, which was mostly noticeable when drawing colored text (it would stay white). I quickly noticed I had left that as a TODO for later, so alright, I dived into it expecting to spend a couple of minutes on it before moving to the next urgent task. Except… there’s no (sane) way to tint a sprite with Canvas. It’s trivial to specify vertex colors to the GPU when drawing a quad, but it was just not exposed through this API. The solution suggested on StackOverflow and other webdev communities? Create a second offscreen canvas, draw your sprite to it, mess with masks and stuff and blit it back to the main one. Excuse-me?
After going through the five stages of grief I finally reached acceptance and ditched Canvas to write a new batch renderer with WebGL. I did it in a single evening, one hour and change, hated every minute of it and swore to never use Canvas ever again.
I encountered countless surprises near the end of development. CORS issues preventing Firefox access to the local storage because the game was served by itch’s CDN but embedded in the store page? Check. Audio refusing to play on Chrome until users interact with the game? Check. Pressing the usual ESC key to show the map exiting fullscreen mode without a way to prevent it? Check. I get it: if your OS is like your personal garden, the web is the wild west, so you want some limits on what applications can do. But it makes the experience of creating a web game so miserable that I don’t know if I want to persevere in this way even if the numbers turn out good.
The virtual machine
The core of the virtual machine is similar enough to what I described in Aereven Advance, but with some important tweaks.
First, we're not on a GBA anymore so we get access to floating point numbers (wooo!). To accommodate this additional type, the virtual machine works with a ‘Word’ type that is 32bits and can be interpreted in three different ways: signed and unsigned integers and floats. (Booleans are still regular signed integers.)
The second change is that the stack is actually used now. Only the push and jump opcodes have parameters (and only one instead of 3) and everything else is pushed or popped from the stack. It’s less performant, but we're running on hardware that is vastly more powerful than a twenty-year-plus handheld toy. In exchange, we get a lot more flexibility and simplicity.
The data segment (a read-only sequence of words) I introduced late to Aereven Advance is also more prominent. It still stores hitboxes and other bounds, but now all the tilemaps and collisions are also stored in this segment.
Strings are still stored in a separate table, though. They could be stored in the data segment with all the rest, but I opted to keep the table for two reasons: first, it makes it easier to implement the virtual machine in C# for the old .NET Framework (which I still use) where there’s no Span<char> type. Simply read the entire table in memory once and index it, easy, no memory allocation needed to extract the string from a byte array. The second reason is that a brave soul created a patch for the Aereven Advance ROM to translate the game to Spanish, which is so damn cool. I don't think it would have been possible had the strings be scattered randomly into a gigantic data segment. And lastly, it makes it easier to write strings while you’re writing data, which is the same reason why data and bytecode are separate segments to begin with.
Cooperative multitasking
Just like Aereven Advance, the engine is built around a scene containing entities. Both are virtual machines, or processes in the same virtual machine, depending on how you look at it. Scripts running on the scene’s process are modal and pause the gameplay. That’s what happens during the intro, or when you open a menu, or trigger a conversation with an NPC or get an item, etc.
On the other hand, scripts running on entities are executed simultaneously. Well, kinda. Distant Paradise is entirely single-threaded because c’mon (fun fact: I had to fix a bug post-release caused by the game running too fast!). So what happens is that entities are updated sequentially but their scripts can yield, suspending execution until the next frame (or even later). This is an example of cooperative multitasking, just like coroutines.
So the flow is simple: for most entities, a script is run on their process to set them up after spawning. It means setting their sprite, collisions, HP, flags, etc. For static entities, there's nothing else to do and the script finishes execution. For dynamic entities like enemies, it enters an infinite loop that will do stuff like set their velocity and yield for a few seconds, giving them simple movement patterns. I also have specialized yield instructions to wait for specific events, like waiting for the player to enter the entity’s trigger bounds or colliding with a wall.
The player entity is a bit different in that it has a ‘idle’ script called every frame when it’s not already running a script. This update script is in charge of binding the abilities to the buttons, restoring the jumps counter when grounded, animating the character and other game-specific actions. I have the feeling that given some new generic instructions, it could do a lot more than it does, freeing the engine to be more generic and suitable for different kinds of games. As an example, right now it’s the engine itself that handles jumping, but with instructions to handle input buffering and buttons released, it could be moved to the ROM itself. Something I plan to try in the future.
Bootstrapping
The initial idea during the DreamBox experiment was to leave the entire bootstrap sequence to the fantasy console: splash screen, title screen, file loading, settings, etc. That’s how the initial jam version of Distant Paradise worked. But, interestingly, that’s not how Aereven Advance did things.
Instead, the title screen was entirely implemented using the virtual machine and its usual instructions for the simple reason that it was more convenient to do so. I already had an instruction to display a choice popup, why not reuse it?
I ended up doing the same thing for the post-jam version of Distant Paradise. The thing is, you need to be able to customize this bootstrap sequence for every game, even if it’s only the background image you’re using, or the script that displays the credits, or loading extra resources like additional textures or SFXs, etc. The virtual machine and its stack are a great way to do that, no need to reinvent another mechanism. I still have simple high-level instructions like OP_OpenSettings or OP_ChooseSaveFile to do complex things, but otherwise the entire sequence is pretty much entirely configurable.
That being said, the ROM still has a metadata segment (or “headers” or “labels”, I don't really know what to call it) associating a string key to a word. It’s used to store info we may need before we’re in a position to execute any script, like to get a title for the game’s window or even to know where the main script begins in the bytecode segment.
The Tools
Okay, that about covers the runtime part of the tech. But right now we only have a fancy virtual machine and no program for it to execute. How are those programs made?
During my DreamBox experiments, I set up a custom Lisp dialect written in C# to act as the source language compiled to bytecode. I still believe it’s an interesting approach, but my attempts weren't mature enough when the jam started and I knew my attention would already be stretched thin. So I wrote a quick assembler directly in C# and used that, just like I did for Aereven Advance. But one lesson learned from that project was that keeping the list of opcodes and variables in sync between the runtime and the tool was a pain, so I wrote a simple parser for C enum definitions to automatically generate the equivalent C# source file and include it in the build process.
Beside the bytecode compiler, the tool also provides a map editor. It’s just my regular map editor I’ve been using for most of my projects since The Trespasser. (The Dream engine provides a few tools that can be easily integrated and customized for every project.) Likewise with the world and flow editors I already presented when speaking about Silent Paradise. There was no reason for me to not use those tools, even if they were made for a different engine (technically, the tools for Distant Paradise run on the Dream engine, so it’s not like it’s been entirely discontinued).
My usual workflow when making Distant Paradise was to save the level file and press F5 to recompile the ROM. It’s fast enough that I don't need to bother with partial compilation or stuff like that, just regenerate the whole thing everytime. Then I would switch to the runtime window and press F5 there to reload the ROM file and respawn at the same position, pulling data and code from the new version. Changing the code itself, either the prefabs or the player, often required restarting the tool but it wasn't too big a deal and that’s something I know I can fix with the Lisp assembler.
So what does the compiler do, exactly? It has two jobs: writing data and writing code. They're found in two separate segments in the ROM file so I can easily build one while building the other (I often wondered if I needed position independence but decided against it). The unit for code is the function, which is just a sequence of instructions ending with OP_Return.
I first write all the player functions, then the prefabs and items and stuff and then for all the maps I write the tiles and collision data of each room in the data segment and generate a script to run when the map is spawned. This script declares a list of rooms, each with their boundaries and their own spawning script. The nice thing with using the room as the base unit is that I get sparse storage for free at runtime, while still keeping larger chunks for edition. And in those room spawning scripts I generate code to set the current tilemap and spawn all the entities found inside (enemies, gameplay elements, FXs, etc).
For most entities, it’s as simple as telling the runtime to spawn a new entity on a given tile and call the prefab function on its thread. But some prefabs require custom behavior depending on how they’re configured or where they’re placed in the world. Different save points will record different positions in the save files, different items have a different appearance and script when obtained, etc. To do that, the prefab first generates a unique function for each spawner, which is some simple kind of metaprogramming.
Each time a function is generated, I can specify a user-friendly name for it that is used to delimitate functions in an alternative text output for the ROM. This human-readable listing has been an invaluable tool to debug the code generation.
You know, as I iterate on this system, I start to think maybe the virtual machine doesn't need to carry the entire burden of reusability across projects. It would be very easy to move some of those assembler macros in a separate .NET assembly and keep a library of them that can be reused across multiple games targeting the same instruction set. And it would work even better with the Lisp assembler!
In Aereven Advance’s postmortem, I said this data compilation phase was great to run extensive validation and catch as many issues as possible before we’re even in game. I said that next time I would push this concept a lot further. I’m not proud to announce I did none of that this time. No validation. Nada, zero! I guess the game was simple enough that it wasn't required (and I was in too much of a rush).
Game Design
I still have so much to learn about game design, it’s not even funny…
Player Abilities
When it came the time to decide which player abilities would be in the game, I wanted to challenge myself a little. I wanted a different set of abilities than I usually have in my games, but because I was working in such a constrained environment with a lot of work ahead of me and precious little time, I had to be smart about it. Something that Adam Saltsman (maker of Canabalt) said about his PICO-8 games that really resonated with me was how he was trying to do the “easy but not-obvious thing”. That was exactly my goal here: don’t do what I usually do, but still do it with the very simple primitives available to me. Basically: try to find a new arrangement.
The Dash and Double Jump were exempt from that rule. They’re just too simple and enjoyable to exclude. I still tried to keep it interesting by moving the Double Jump toward the later half of the game.
Now, what’s the easiest thing I could build from that? Infinite Jump came naturally: instead of having two jumps you now have 10000+ (not technically infinite, but close enough). Done! And placed as the very last ability it leads to this exhilarating feeling you have when you unlock the Space Jump in Metroid and can fly through the map, essentially performing a victory lap.
Right after that, I thought I could try my hand at a stomping ability, similar to Hollow Knight. Afterall, it’s just a downward dash, right? The only difference would be destroying blocks to reveal new paths. It turned out a bit more complicated as I really wanted to make sure you could chain the Dash and the Stomp, but still, pretty simple.
I couldn’t decide if I wanted the game to be range-based like Silent Paradise or melee-based like Lost Paradise. I’ve done both, multiple times, and I don’t know… I just wanted something different. Early sprites attempts for the astronaut had an arm cannon attached but it never fired anything in game. And after implementing the Dash and the Stomp I thought what if you don’t have a weapon? If the Dash and Stomp could deal damage on their own, maybe the entire combat system could revolve around them and careful positioning. I liked the idea very much! Though we will see later that it caused a few headaches.
Heat Resistance already appeared in Silent Paradise as an optional upgrade, but I thought it would be a very simple thing to implement in this new engine and an interesting passive ability. Something I like about it is that it’s not a hard lock: if you’re fast enough and have collected enough health upgrades, you may reach the exit or the powerup before you die. I only really managed to stage this once, with a health upgrade in the lab, but it inspired me to place the Heat Resistance itself in a hot room and hint to players they’re supposed to race the burning effect to get it. That’s a cool set-piece, right? And it fits the theme of the game of “forced evolution”.
Once I had that, I had to make the other powerups fit similarly. It was easy for the Dash because I already had the idea of using airdrafts as we will discuss later. For the Stomp… well… it breaks blocks, doesn’t it? So we have a fire-themed Heat Resistance, a wind-themed Dash and an earth-themed Stomp. We’re just missing a water-themed ability to conquer the four elements and make thematic sense. And for the longest time, I had no idea what I could do for that last one. I certainly did not want the water-themed ability to be diving underwater, as I did that in all my metroidvanias for the last 6 years and it’s a pain to implement. Waterfall climbing? Done that already… What about walking on water? It’s simply a matter of transforming a killbox into a platform, right? Simple to develop, but a bit boring for the player… But what if you had to keep moving to stay afloat and I shoved obstacles in your path? Now we’re getting somewhere!
But by the time I had this epiphany, the rest of the critical path was planned without this water ability. It was fine by me, I had no problem making it completely optional. And by some half-random chance I managed to keep this powerup accessible right after the Dash, though it requires some serious platforming chops to get it this early. And once you have it, you can get the Heat Resistance from the other side of the room, completely bypassing the Secret Lab area. Love it!
The last planned ability was the Teleport Arrows. That’s not something I’ve encountered much in the games I’ve played, save for teleporting to your chakram in Prince of Persia: The Lost Crown. I thought it would be a nice twist over the formula established in my previous games where the dash itself is what you use to go through grates and vines. It also offered some platforming opportunities, though the initial versions kept your velocity intact after a swap, meaning you would keep your terminal velocity and would instantly fall at max speed after a swap. Resetting the velocity makes sure you have some leeway to react.
The block switching ability wasn’t initially planned. It emerged naturally as I wanted to create a puzzle where you have to hit a single switch multiple times to ascend out of the lab. I could not find a clever way to make it work, so I resorted to giving you access to the switch as an ability. But it had a nice side effect in that you could now traverse the lab very quickly, bypassing all the puzzles you had to solve on your way in.
Combat
I don’t really enjoy making bosses. That’s weird, right? I should be all over the storytelling opportunity they offer.
But bosses are tedious one-off mechanics. They’re used only once in the game despite requiring more work than most enemies. The approach I used in Lost Paradise was to make them all variations of the same enemy, with simple behavior patterns made out of reusable building blocks.
For this game, I decided early I wouldn’t bother. I wondered if it would be an issue, considering that some players complained about Silent Paradise having only two bosses, but so far it wasn’t. Nobody ever mentionned their absence. It’s just not that kind of game, and with them out of the equation, I could get some bandwidth back for the rest of the game.
I did feature a handful of combat rooms (3 to be exact), because it was pretty simple to do with the instruction set I had, but due to the Dash and Stomp being the main offensive abilities, they act more like a platforming puzzle than an actual fight.
Level Design
It’s a small world
I really struggled with the size of the world.
It may be because I was playing Jedi Survivors at the same time I was making this game, but I really wanted players to feel like they were “folding space on itself”. That’s why I didn’t want too many save points or fast travel. I wanted players to be able to quickly go back to previous areas using shortcuts. The planned critical path even required that players backtrack from the right-side of the map back to the beginning on the left-side, so it had to be quick. Also, I did not want players to travel large distances before gaining the Dash ability, which is supposed to be the very first one and your way to actively engage with enemies!
To do that, I chose to have a central hub and spokes that would branch from it before looping back to it. Then I imagined a kind of central “highway” traversing this hub horizontally, connecting the two segments of jungle on each side of the temple. A similar “vertical hightway” would act as a loopback from the ending to the hub once you obtained the ability to fly.
It kinda worked, but not as well as I wanted to. I think there’s only a handful of places where this shortcut strategy really came together. As mentioned above, gaining the block switching ability trivializes the entire Secret Lab, making it way faster to traverse. Gaining the Heat Resistance lets you go back to the central hub through the “horizontal highway” and the fly ability does the same for the “vertical highway”. For the other places, I tried at least to offer players something on their way back. For instance, once you get the Teleport Arrows in the Apes Village, you must simply walk back to the temple, though you now face a room you couldn’t solve before. It also helps that your new ability extends your horizontal reach, so you can traverse more quickly and it also makes you better equipped to deal with the enemies.
The constraints on the size of the world made it difficult to come up with a satisfying layout and a good challenge progression.
Kishoutenketsu
Speaking of a good challenge progression, how would you do it? Scratch that, how are the masters doing it? And by the masters, I mean Nintendo.
In Koichi Hayashida’s words: “First, you have to learn how to use that gameplay mechanic, and then the stage will offer you a slightly more complicated scenario in which you have to use it. And then the next step is something crazy happens that makes you think about it in a way you weren't expecting. And then you get to demonstrate, finally, what sort of mastery you've gained over it.”
For the Winds Chamber, the introduction is the very simple airdraft leading to it. Then I complicate things by adding enemies patrolling on its path, forcing you to move horizontally to avoid them. But both times you’re above safe ground, so there’s not much risk to it. Then things get more complicated as you’re asked to jump from one airdraft to the other, while you’re over deadly spikes. And then the twist: now there are spikes on the ceiling and you need to get out of the airdraft before hitting them! And finally the mastery demonstration where you have spikes both on the floor and ceiling, multiple airdrafts to jump from and to, enemies, etc.
It sounds good on paper! And believe me, it’s not too bad in practice, compared to previous iterations (I had lateral airdrafts at some point but I could never get the physics right). But I can’t say it’s a success either. The size of the game means the four-step journey is condensed into a couple of rooms, with basically one occurrence for each step. That’s not quite enough to really experiment and build mastery. Also, that means no real checkpoint. If you lose all your health, you’re back to the previous save point and have to try again from scratch. It may be fine in a Mario game, but it’s not really the point of a metroidvania. That made this first chamber a huge difficulty spike that was detrimental to the game in its first incarnation.
Also, this philosophy only really worked for the Winds Chamber and maybe for the Secret Lab and its block switches. (The introduction being a simple switch to progress, then complications as you need to toggle them in succession or navigate through a different room to find them. The twist was being trapped by blocks without access to a switch but gaining the ability to control the blocks without it and finally the conclusion is your escape by toggling them in rapid succession as you ascend an elevator shaft.)
The other places and abilities? Not so much. There’s a hot room in the Secret Lab with timed blocks slowing you down on your way to a health pickup. I’m not really sure what my thinking for this room really was beyond “I have no idea what kind of challenge to have here”. I guess the idea was that you already had a room where you can reach the goodie in time if you’re really quick, and this one is a twist in that you need to recognize the rhythm of the blocks and be in sync to reach the end quickly enough…? But I don’t believe it really works, as the path and rhythm are not obvious enough, and you’ve had no training beforehand. (Also, no exit path, so even if you do reach the powerup, you will probably die in this room.) For such a small game it’s way easier to just move on, get the Heat Resistance upgrade and come back at the end if you’re going for 100% completion. This is an example of murky design that could really be focused with this “Kishoutenketsu” structure. But having that at a micro scale while adhering to the metroidvania structure at a macro scale is quite the challenge.
Still, it’s a very neat way of planning this natural progression and escalation. That’s already something I was trying to do with the puzzles in my Zelda-likes and I would like to keep practicing it.
Conclusion
I achieved what I set out to do with this game: a small- scale “microvania” adventure acting as a coda for the Paradise series that feels different enough from my previous games, with a web build on par with the downloadable Windows version.
It didn't do as well in the jam as I would have hoped, though. I blame the early difficulty spike of the Wind Chamber for that, which I fixed as soon as the voting period ended. Surprisingly, the game didn't receive more ratings than my previous downloadable-only entries. And while it’s too early to evaluate the web build, so far it has not been a silver bullet. The combined conversion rate is indeed higher than my other games (> 50%, which is almost double compared to my other metroidvanias) but there were not enough views to be significant. I will continue to monitor those numbers before I draw any conclusion, but considering how much of a pain it has been to get the JavaScript part working correctly, it may remain a one-off occurrence. I still believe getting on Steam is a better plan for increased visibility.
From a tech standpoint, it’s now my second game made with this virtual console approach, and I believe it works. I don't think it will entirely replace my Dream engine, but for lower scale “pocket epics”, it’s definitely viable. I suspect I will have even less free time in 2026, so it may be my ticket to continue making games.
That’s it for me! Another long-ass postmortem, but as it was an experimental project, there was a lot for me to think about. We will see how those learnings will make their way to my next games.
See you next time!
Get Distant Paradise
Distant Paradise
To live is to change: evolve to escape a doomed planet!
Status | Released |
Author | Dreamnoid |
Genre | Action, Platformer |
Tags | 2D, Action-Adventure, Colorful, Exploration, Metroidvania, Pixel Art, Sci-fi, Singleplayer |
Languages | English |
More posts
- Distant Paradise version 1.18 days ago
- New game alert: Distant Paradise is out!23 days ago
Leave a comment
Log in with itch.io to leave a comment.