Postmortem, part2: The Tools and Pipeline


Legends of Aereven: Lunar Wake is a 2D Zelda-like I made in 2020. As I continue my gamedev journey with other projects, I wanted to take the time to look back on it. I'm planning a series of posts acting as a postmortem for the project.

This is the second part, dedicated to the tools and pipeline I used to create the content for the game. You can find Part 1 about the Dream Engine here.

The Approach

I’m the lone developer on my games, and I’m a programmer. It means I’m not afraid of writing code to get the results I want. Thus, as discussed in the previous part, my current approach is not fully data-driven.

But it still is to a large extent, because some things are a pain to express in code. Sure, you can write the stats of your RPG characters directly in a C# file, even derive them using very advanced formulas (aka ‘code’), but when you want to just visualize them neatly in a row, it’s difficult. You need tools. If you’re going to build them, you may as well do everything in them.

Most of my games have a ‘Database’. Not the SQL kind, but something similar to what RPG Maker 2000 had, at the very beginning of my  gamedev journey. If it works, it works!


The database is a big JSON file with definitions for almost every element found in the game. Items. Maps. Warp points. Characters. Spells. Etc.

Of course, I don’t edit it manually. Instead, I’ve built an editor, in C#, using Dream and leveraging the IMGUI system in Dream.Editor. This editor has three responsibilities:

  • Editing the database (with either generic or custom UI, depending on the use case)
  • Editing the tile maps
  • Launching scripts and process, like the Validator

Sharing data

The editor is a separate executable from the game. A different project in the same solution. I didn’t want to include the editor inside the game (even as a separate mode) because I wanted to be able to recompile the game without closing the editor. If they used the same .exe, that wouldn’t be possible.

But I still needed a way for the two executables to share data. The proper .NET solution would have been to create a third assembly for the shared code. It seemed a bit overkill, so instead I relied on a nice Visual Studio feature to reference the same source file in both projects. All data definition went to a Shared.cs file used in both compilations. So the only time I needed to close the editor was when I updated that file (but then that usually implied that the editor would change to reflect that) or Dream itself.

Turns out, I change Dream quite often, actually. So I often had to close the editor anyway. For new projects, I’ve decided to bite the bullet and just have the editor in the same executable, only accessible through an “editor” command line argument. You can try it with Tower of Ordal: start the executable with the “editor” flag and happy modding!


Map Editor

For years (scratch that, for more than a decade now) I’ve been using a little WinForm+XNA tool named Blacksmith for all my projects. Reckless Squad used it. The original Tower of Ordal used it. The Lightkeeper used it. It’s old, but it works. (On my PC, anyway. The XNA Runtime dependency becomes more cumbersome with every new version of Windows…). Best return on investment ever.


I didn’t use it for Lunar Wake.

First, Blacksmith has some problems. Number one: it doesn’t have an Undo command. But more importantly, it’s game agnostic. Very generic. Which, in many ways, is a strength! I wouldn’t have used it for so long and for so many projects if it wasn’t. Still, I want to be able to customize the editor for each project. I want to be able to preset the layers, tile size, tileset, grid measurements, etc. I want to be able to place prefabs directly indexed from the game code. And some projects could require custom features, etc.

For years, I meant to develop a “Blacksmith 2.0” in WPF, with plugins support. The idea never took off, because it was the wrong approach.

The right one was Dream.Editor.Blacksmith.

This namespace in the Dream.Editor module is a toolkit to quickly build map editors, using the other IMGUI controls. And almost everything can be composed or extended in different ways.


For Lunar Wake, I still use the BSM file format from Blacksmith. It means I can still open and edit my maps with the good ol’ Blacksmith editor. It was my safety net while working on this new map editor. Nowadays it is robust enough that I can go with specific file formats, but still… code rot is a thing, and using a well-supported file format saved a few of my old projects.

But for Lunar Wake, I still needed to store more data than BSM allowed. So I used the “Name” field on my maps to store arbitrary JSON (that would include the actual map name!). There, done, problem solved.

What I did not want was to choose the size of the map before laying the first tile. Because at that point, I have no idea how big it’s going to be. Infinite maps is a thing, Tiled does it, but it’s a pain to implement and that wasn’t really necessary. I went for something easier: maps are created small (the size of a single screen) but can be easily extended by another screen in any direction, or cropped. It’s a bit like sculpting: sometimes you add clay, sometimes you remove some.

Something I added that I never had in the original Blacksmith is Undo/Redo. I handled it in a very brute-force way, by making a full snapshot of the tilemap everytime a draw operation is completed (= when the mouse button is released) and storing it in a circular buffer. Not memory efficient by any mean, but it worked, and it was super helpful. Misclicking with the paint bucket used to be apocalyptic, but not anymore.

Prefabs preview

Prefabs being described in code does not preclude them from being listed in the editor. The Lunar Wake editor will read the game assembly and list all prefabs (identified by an attribute) to present them in a swatch.

It will also instantiate one of each and look for a Sprite component to display some kind of ‘preview’. It was a handy feature, but it introduced a few headaches, because spawning outside of the game, with no active scene or player, would not work for every prefab. I had to handle those situations, check if I was in the game or the editor, and change the prefab accordingly. Not great.


For my current projects, prefabs are a bit more data-driven. The behavior is usually written as code, but the appearance of each entity is configured in the editor (because a visual editor is best for visual stuff, who knew?), so it can easily be used by the map editor to preview the prefab.

The Validator (not starring Arnold Schwarzenegger)

The other feature that I knew I wanted from the start was the Validator.

It’s a pretty simple script that will browse all the data in the game (both in the database or in the map files) and check that everything is coherent. While doing ‘level design’ (scare quotes added to not insult my level designer colleagues) I would often place a chest without deciding on a specific reward. The validator would then print me a list of every empty chest in the world. It would check that entities were correctly configured, that IDs actually referenced things that existed, that no data was missing, etc.


The Validator quickly evolved into a general-purpose reporting tool. Beside errors, it would report stats on the world, like how many times each enemy was used (when making The Lightkeeper, I created an enemy that ended up being used only once, which was quite a waste), how many piece of hearts were to be found in the world (and making sure it was a multiple of 4), and a guess about how much money the player could reasonably collect in a playthrough, which helped me balance the shop prices.


The Validator would run on the press of a button in the editor, and output a simple text file presented as a list.

It was simply invaluable. So much that it acted like a secondary To-Do list in addition to my Trello board.

Conclusion

As a professional Tools Programmer, this is a very important topic for me, even for my hobby projects. A lot of what I exposed here has been refined with The Trespasser and will continue to change in the future.

Still, that approach for Lunar Wake was a great starting point. The Ancient Forge extension was produced quite quickly and painlessly thanks to those tools.

Next time, we will talk about game design! Stay tuned ;)

Get Legends of Aereven: Lunar Wake

Leave a comment

Log in with itch.io to leave a comment.