MVM22 Jam Postmortem


Silent Paradise is a metroidvania I made in a month for the 22th Metroidvania Month game jam. With 8 biomes, 7 movement abilities and more than 20 items to find, it's pretty big even for a month-long jam.

By far the most frequent question I got asked after submitting it to the jam was "how did you make this in a month?". I’m glad you asked because I love talking about this stuff. The short answer is "thanks to good tools and a good process", but I will try to write the long version here.

Prior experiences

First, it should be said it was my first game jam in over a decade. I think the last time I took part in one was around 2010, on the french Games Creators Network forums. Those jams lasted 9 days, which is more comfortable than a weekend, but still pretty fast-paced. I liked the experience, but I always felt that short game jams encouraged small experimental games, which is not necessarily what I'm trying to do with Dreamnoid. Hence why I never joined another jam until now.

What I did join was NaNoWriMo, the well-known "write a novel in a month" challenge that I've completed most years since 2014. NaNoWriMo is very different from your average game jam: it's a marathon. You have to keep the pace, which is recorded on the website as the amount of words written in a day. Over the years, I got really good at it. It really taught me the importance of planning and discipline. In 2021 and 2022 I didn't just reach the official 50 000 words goal but completed 80 000+ drafts to the word 'End', during years where I was really, really busy. And the most surprising part is that at the end of the month I would feel good and refreshed instead of tired and drained. Accomplishing something is one hell of a drug.


For various reasons, I didn't join NaNoWriMo this year. So when I stumbled upon Metroidvania Month Jam and noticed it would fit my schedule, I decided to join. I knew I would treat it with the same dedication as NaNoWriMo but beyond that I didn't know how to organize myself. I always start NaNoWriMo with a complete outline and then it's only a matter of writing at least 1667 words every day... but how would I measure progress with the very much non-linear game dev process?

To add to my doubts, I also did some math before the jam started: what little time I could allocate to it. Just like NaNoWriMo, I would be lucky to have 2 hours a day to work on it, most likely only one. And no weekends. The result of that was closer to a Ludum Dare than I felt comfortable with. So I knew right from the start I would have to be super efficient if I wanted to finish something. As it happened, I managed to find some additional time (including taking a day off from work near the end), but it was still pretty crunchy. Still, the existence of Super Metroidvania Month eased my mind a bit: worst case scenario I could aim for that instead.

Which brings me to an important point about all this, inherited from NaNoWriMo as well: I didn't join to win the jam. Sure it would be nice to get good ratings (turns out Silent Paradise ranked #3), but what I wanted out of this jam was a new completed game. For years I've been working on ways to make gamedev easier for me, with the aim of releasing two games every year, so the fact I managed it for the first time in 2023 is a victory in and of itself.


Initial design

Because the theme isn't a hard constraint in this jam, I was advised to brainstorm a plan before the kick-off. I half-assed the beginning of an idea at the very last moment, and used absolutely none of it because the day the jam started and the theme was announced, I got a flash of inspiration. To my surprise, 'Abyss' won, and my brain went straight to James Cameron's 1989 classic as I wondered "wouldn't that be cool as a Metroidvania?"

The day after the jam started, I was on a train to Paris to visit my siblings. I would be there for the whole extended weekend, Friday to Sunday, with no computer in sight. Not a great start for a jam! So I bought a notebook and a pen and decided to make the most of my trip. Almost the entirety of Silent Paradise was designed during the first evening and those two short train trips.

What I like most in Metroidvanias is the exploration. A sense of place is very important to me and that's what I focused on first (and mainly) for this jam. Once I knew I wanted the adventure to take place in a submarine station, I started to research how they could work. Paradise is pure science-fiction but some underwater labs do exist in real-life and provided me with an initial list of rooms. I then extrapolated by adding the ones related to the fantasy of the underwater city. Believe it or not, I've never played Bioshock, but I knew Rapture was similar to what I was aiming for so I looked up some screenshots that informed the look of the guests' areas.


Then I grouped the rooms together in biomes. I had 5 main ones (Surface, Habitat, Life Support, Research and the Depths) that I then subdivided further. I started to link the rooms together depending on what made sense. The kitchen must be close to the cafeteria, the hotel rooms must connect to waste disposal and wouldn't it be cool to have the public aquarium and the private specimen tanks be connected? This gave me a graph, and for the first week all work was done exclusively on that graph.

Eventually, I came back home from my weekend in Paris and gained access to more than a notebook. One of the reasons I was interested in MVM22 is because I was thinking about making a new Metroidvania as a sequel to my previous games, so I was already doing some engine and tools work that could be put to use. One such tool is the Flow Editor, a graph where I can place rooms and connect them together. I recreated my notebook graph in this tool and implemented a new feature to specify the items needed to traverse a link. Then I could toggle items on and off and see which parts of the map were available at each point of the game. This tool was invaluable to help me figure out the map and the progression.


It was only natural that the progression would lead you from the surface to the depths, so that's how I arranged the rooms and placed the upgrades. It was a bit of a puzzle to disentangle the graph into a coherent 2D space that respected all constraints, but after a while, I had a solid plan to build upon.

Dream.Vania

Let me talk about my engine a bit. People seem surprised that in addition to making a big(-ish) game for a jam, I did it with my own custom engine. But that's just it! I could do it because I have my own engine tailored to my needs and strengths. I doubt I would have succeeded with a general purpose engine like Unity or Godot.

I talked about Dream in detail in my postmortem for Lunar Wake. But for this jam, I listed the engine as "Dream.Vania" instead of just "Dream". This is because Dream is an ECS-based modular engine and Vania is a (big) module built from parts and scraps from my previous game, The Trespasser. And now Silent Paradise has added a lot to it that will carry on to new projects, even in different genres. That was the promise behind this architecture: games building upon each-other, like a rising tide carrying all boats. I will never compete with Unity, Unreal and Godot in terms of features, but it's okay because I'm not trying to make any possible game, only my own. My engine is opinionated: it makes what I want to make quick and easy to do and ignores the rest.

Still, it's not without issues. I mentioned in The Trespasser's postmortem that I kind of lost sight of this philosophy for a while. I tried to make a purely data-driven engine, which makes no sense for a solo programmer. I built features upon features that are already covered by the programming language I'm using. I knew I had to do things differently for this jam. I removed all scripting languages integration from the Vania module in favor of loading a dynamic C# assembly. This way I had access to the full capabilities of the engine without having to write any repetitive binding, I had the full tooling of C# in Visual Studio which meant a very expressive language, extensive validation and IntelliSense. And because it was a dynamic DLL I could write changes, recompile and reload the assembly on the fly, without the need to restart the game. Reader, let me tell you: working like this was soooo nice.


(As it turns out, it's not all rainbows and unicorns. While this approach is great for development, it makes deployment a lot harder, especially on different platforms. Silent Paradise is a .NET Framework game and porting it to .NET 8 with trimming or AOT is hampered by this dynamic assembly. I'm not sure yet how I should solve this issue for future projects.)

In that same spirit I reversed the workflow between the game and editor. Previously, all my games worked a bit like Unity or GameMaker (or rather RPG Maker as it's what gave me my start more than two decades ago): you work in the editor and press a button to launch the game and test your changes. I switched things around for this game. Instead, you play the game and press a button to start the editor and change what's around you, and when you're done you press F5 to reload the scene with your changes. I plan to double down on that way of working in the future so that pressing a key like F2 opens the editor right on the current map. Fast iteration really is key.


Still, The Trespasser's approach had one nice thing going for it: because all data was edited in the editor instead of in script or code, I could visualize my changes instantly. But the fast reload feature I mentioned before was enough in most cases. Only maps and particle emitters remain data-driven and in the editor, for obvious reasons. I built a Zoo Editor where I can spawn any prefab and work with the reload, but I didn't use it much for Silent Paradise. It was just as easy and more interesting to stay in the game and teleport to a place where my prefab was instantiated and then reload as I worked on it. And I could still build dedicated spaces in the debug map if needed.


Beyond that, most of the tools I’ve built during the jam are visualization tools. Most of them are found in the World Editor and render bitmaps for different kinds of maps, to display different kinds of information. But we will talk about that in more detail later.

The last tool I want to mention is the 3D character creator and renderer I use for some sprites, but I already talked about it in The Trespasser’s postmortem.

Building Paradise

If the first week was dedicated to the macro structure, from the concept to the flow of upgrades, the second (and a bit of the third) went to level design. 

Like I mentioned in The Trespasser’s postmortem, I build my worlds out of multiple smaller maps, assembled together in the World Editor. For Silent Paradise, I started creating a different map for every biome I had listed in my notebook.

My editor has a “stamp” feature that allows me to quickly paint basic greyblock rooms. So looking at the rooms listed in the flow graph and how I placed them, I started laying them out in the biome maps. I didn’t stress the details yet, as all I really wanted for now was to get a feel for the entire space and how the different biomes connected. Of course it was only a first draft and I had to relocate a lot of stuff as I started mapping for real, but most of it worked as-is thanks to the work done at the graph-level.


I did this for the first few biomes, those on the upper half of the central elevator. Then I took a break to decide what each biome would look like. If you played the game, you may have noticed that most biomes use similar tiles, just with different colors. So I worked on those first. Then I had to draw a few unique tiles to differentiate them and “sell the fantasy” of each location. I tried to reuse as many as I could while still maintaining the unique identity of each place.

Once I was confident this approach would work for the upper half, I resumed work on the lower half. It was long and often tedious, but I powered through, hoping to get a “first playable” version as soon as possible.

Another way to motivate me was to render a full-map bitmap of the entire world. That’s something I had done for The Trespasser’s “marketing” (= a tweet) and I really enjoyed having a look at the entire world at once.


Enemies

I had designed an initial palette of enemies during my train trip. I implemented them quickly and started to place them in the Guests Housing biome, to get a feel for how they behaved in relation to the space.


Most enemies are a single 16x16 sprite (even the “crab” mirrors its single frame to raise one leg after the other). The two exceptions are the bigger Hivemind boss and the “Sentinel Drone”, which uses the same 3D model and animations as the player. I only had to model a handful of new parts for the character creator that powers them both.


One thing I imagined when I was designing the game on paper was to have all drones controlled by the central Hivemind AI boss. Wouldn’t it be cool if, once defeated, all the drones stopped working? It was simple enough to implement, as my prefabs are code functions that can execute any logic I want. I can simply check the flag raised once the boss is defeated and spawn the “powered down” version of the enemy instead. But then what would the player fight as they backtracked?

It’s kind of a spoiler, but one of the twists of the game is that the Paradise submarine station discovered aliens at the bottom of the ocean. (Sorry I also spoiled you James Cameron’s The Abyss.) My idea was that the aliens would only reveal themselves once you defeated the Hivemind (they were in hiding because the AI was killing them), so switching the drones for them was the logical thing to do.

But I didn’t want to go back to every room and place a different set of enemies. I also can’t place two spawners on the same tile in my editor, which would have been an awkward constraint.

To solve that, I made the aliens so that they would match a drone. For instance, the flying aliens spouting fireballs match the Fly Drones that randomly move around in the air, while the crawling aliens match the Crab Drones that patrol on the ground. I then implemented dynamic spawners that would spawn either the live drone or its alien equivalent. It worked and served as an easy way to give a mid-game upgrade to all enemies. But for players to understand the twist, I had to also spawn powered-down versions of the drones. My only complaint is that, because the dead drone and the alien start on the same spot, it betrays the underlying system a bit.


One of the issues I had with The Lightkeeper is that I designed a handful of enemies that ended up appearing very rarely (one of them was only used once!). It was wasteful. So for my subsequent games, I implemented a tool that would count how many times each prefab was used and printed a .txt report. I wanted to go further for this project, so I quickly implemented a mini-map view that displayed the enemy spawners. This way I could see how enemies were dispatched, not just count them. (Of course I still had a validator to display usages as a table.)

Enemies themselves are built out of reusable bricks. Of course the components are the building blocks of my ECS engine, but the behavior patterns are also reusable snippets. The alien enemies introduce no new actions, instead mix-and-matching those made for the drones. Because those blocks are also used for the player, the Alien Rusher was able to reuse the player’s dash as-is. Even the bosses are built out of existing actions. That’s an approach I really like and intend to explore further in the future.


Alpha and beta

A week and a half remaining! The goal was to reach beta by the end of the third week so I could dedicate the last one to playtesting and polish.

During the level design process, I would often map small alcoves and other places where I could hide items later on. It’s all about finding opportunities and going with the flow. Improvising.

But then I needed to keep track of those “empty chests”, so to speak. Yet again, I added this feature to my World Editor, so it would locate those spawners and display the item assigned to each. If there’s no item, it shows an error.

A lot of the work needed to reach that feature-complete beta was staring at the minimap and deciding what optional items should go where.


Of course… I had more spots in the world than items to dispatch…

This predicament forced me to come up with new power ups.

On the plus side are the three Fuel Canister upgrades. Making the dash consume the entire stamina bar balanced it compared to The Trepasser, and the extra bars gained with the canisters feel meaningful and rewarding.

On the other hand, the Whisky Flasks felt useless to me. I always err on the easy side for my games (as I believe hard games need impeccable design), but having three full heals was too much. You couldn’t die once you got those flasks. I did like them for their world-building merits though, so my post-jam solution was to drastically reduce the health drops you get from small props or enemies. This make the healing flask a bigger deal.

Missiles… are okay. It’s just too bad I didn’t have any place where you need them to progress. The only breakable rocks are right at the end and intended for the morphball. Breakable walls and props is something I added in the subsequent versions, even hiding a few sequence breaks behind them.


This approach forced me to come up with extra content, which contributed to the “bigger than a jam entry” feeling. It all needed balancing afterward, but on its own it’s not without merits. And for someone like me who loves exploration, finding a new kind of item is already a reward in and of itself.

When I was designing the game on paper, I noted a few powerups that I wanted to implement for the first time in my games. The environment hazards like the “Intense Heat” rooms are one, so are the Grappling Hook and the Sliding move. (Waterfall climbing was an old idea I had prototyped for a previous game, though it worked a bit differently in Silent Paradise.) It was a bit of a gamble, because right until the end of the jam, the grapple was super janky. That’s why it’s never placed on the critical path, and I didn’t know if I would have to cut it altogether at the last moment (and despair at how I would fill those empty spots).

Writing

Not much to say here. Most of the story was decided during the initial brainstorm. I placed the codex entries in the world before writing them. It wasn’t random, I already had an inkling of what they could talk about depending on where they were. I then listed them all in order in a Google Doc and decided what each would say before fleshing them out. I did all this while I was away from my computer, mostly during commutes and lunch breaks. Then, back home, I implemented them in the game and started the terrible process of editing them out until the words fit the small textbox.


I did the same for item descriptions, but chose a different approach from my previous games that employed an omniscient point of view (like Dark Souls). For this one I wanted the item descriptions to reflect what the outside world knew of the station, and leave the juicy reveals to the newly introduced codex.

Music

I’ve been making games in one capacity or another for more than two decades, to the point I can do a lot on my own. But music always escaped me.

For my previous games, I relied on royalty-free music. There’s a lot of incredible artists out there making their work available for free, and I’ve always found nice tracks to fit my games. But then you play someone else’s game and hear the same music and… the illusion breaks.

That, and the desire to make the entire game on my own, pushed me to look for solutions.

Last year, I dedicated a lot of my winter break to a deep dive into music theory. I just binged articles and videos, trying to make sense of scales, keys, chords, modes, harmonics, you name it. Then I implemented those lessons in code to build a music generator.


I first tested this approach to generate short loops for my JRPG Hotel Demonica. It worked okay. They weren’t the beautiful and memorable tracks you could find in a Final Fantasy, but they were harmless and effectively set the mood. I still used a few royalty-free tracks to supplement them, though.

Silent Paradise’s “soundtrack” (if you can call it that) was built entirely this way. Even the boss fight music, certainly the least convincing track of the lot. But as far as I know, nobody felt the need to outright mute the game, so it was good enough for the jam.

I still have a lot to improve in this area, but I believe it’s the right approach for me and I want to push it further.

Playtest and polish

My plan was to dedicate the last week to playtesting the game and polish it, but the beta version was running late. A couple of days before the deadline, two coworkers volunteered to test the game, but I messed up and gave them a defective build (because of an inadequately tested last minute change, proof that QA remains an unsolved issue in my process). That delayed their feedback to the point I only got them Friday afternoon, mere hours before the submission deadline.

I scrambled to fix as many issues as I could, but there was one change I did not dare make so close to the deadline: tweaking the jump. Both playtesters told me the jump was too abrupt. I agreed but feared that changing the impulse or gravity numbers would mess with some platforming later in the game, and at that point I didn’t really have time for an entire full playthrough. Turns out, most (if not all) of the kind people who rated my game for the jam had the same complaint. It was issue Number One with my entry and I knew about it, but did not dare change it. That’s certainly a process failure!


What would a solution look like? A bot playing through the entire game is way too complex for me. And I don’t have the metadata required to validate the metrics directly in the editor. How could I have known if reducing gravity would have made a jump impossible somewhere in the game?

In this case, the solution was to change the way jumping and falling was implemented in my engine. Previously, I specified two numbers: general gravity acceleration and the player’s impulse when jumping. But those numbers are completely arbitrary and mean nothing to me. I’m interested in how high I can go, and how long until I reach the apex of the jump. The individual forces are just a means to an end.

So I looked up the formulas used to derive those numbers and reversed the equations. Now, my input as a game designer is “the player can jump 3 tiles and a half max” and “it takes 0.3 seconds to reach that height” and the system decides which forces are needed. This way I know for sure that all platforms placed 3 tiles above can be reached, and I can change the time to apex independently without fear of messing it up.

I need to generalize this approach in my engine and also invest in extensive metrics validation. To truly take player’s feedback into account you have to be able to make a lot of changes quickly, which takes either courage or confidence. I would rather have the later than the former.

Post-Jam

Three versions later, I think I've addressed most of the feedback I got from both the playtests and the jam itself. So once that was done, my effort was focused more on "spicing it up". Hidden shortcuts and secret rooms and items, additional dialogs and fleshed out puzzles, this kind of thing. As with NaNoWriMo, the end of the jam only gives you a first draft, a foundation upon which to build.


Still, there's only so much I can do with Silent Paradise. The short time-frame required a smaller scope that in turn informed the world and story. I toyed with the idea of adding a new boss to the game but can't find room for it both in the actual space and in the narrative. This is often the signal that I need to move on to a new game. Of course, I will keep updating Silent Paradise if new feedback or ideas arrive, but I feel like this "quick jam thing" can now stand next to my other games.

Conclusion

Making Silent Paradise was a joy. It was a mad rush, sure, but it didn’t feel stressful. Quite the contrary: just like with NaNoWriMo, I felt energized at the end of the month, instead of depleted.

Now, I’m not about to enter the Metroidvania Month Jam every 3 months. My goal for Dreamnoid is still to make high quality games on my terms. This jam was a fun exercise to round-up the year and put in practice what I’ve learned from making my previous games, but after that I’m back to my regular schedule.

Still, I’ve learnt a lot, improved my tech and got a good game out of it. That's my definition of success.

Get Silent Paradise

Leave a comment

Log in with itch.io to leave a comment.