Categories
Eldritch 2

The Style

Making art is one of my least favorite parts of game development. I’m not good at it, it’s slow (maybe because I’m not good at it), and my best efforts still look subpar (definitely because I’m not good at it). If I were a vampire and could devote a decade or two to immersing myself in art the way I’ve immersed myself in programming and design and music, I could probably git gud. But I’ve got a finite number of years on this rock, and games to make in the meantime.

Five years ago, I started learning Substance Designer. I studied other artists’ graphs and picked up a bunch of neat tricks. I made a template to convert Designer’s material properties into the ones my engine expected, and wrote a custom shader to preview materials in Designer the way they’d look in my engine. I liked the non-destructive workflow, and the logical/mathematical basis for texture creation. But I don’t like being tethered to a proprietary tool. My engine and content pipeline are built on free and open source libraries and tools; and while my old Steam copy of Substance Designer 2019 does still work, it felt like a liability to depend on an app that I can’t run without an internet connection. I stopped using Designer and went back to making textures by hand. But a seed was planted.

I’ve got a Trello board for long-term game and tech ideas—the sort of place I’ll write a card like “Vehicles!” and then sit on it for years until I end up making a game that actually wants or needs vehicles. In December 2022, in the middle of working on Li’l Taffer, fed up with making textures, I wrote a card titled “I should make an offline texture generator using a lot of the standard patterns I’ve used for pixeling stuff.” Wild idea, that would probably never happen. Six months later, I actually started building it.

The fundamental click that helped me jump from a vague idea into writing code was realizing that almost all the textures I was making were built in boxes. I didn’t need to make a texture generator that could do lush organic features; I already lived in the realm of bricks and tiles, and most of what I was doing to make those textures could easily be codified. I drew up a list of use cases that I wanted my texture generator to cover, and 99% of them could be broken down into nested boxes with a few common procedural functions.

So, I started coding! I named the project Piet, inspired by Dutch painter Piet Mondrian’s nested rectangular forms, and I got a proof-of-concept done pretty quickly:

I used to be on Twitter.

And then I got busy with Real Life, and I put Piet (and Eldritch 2) aside for a while. In September 2023, I started working on Schloss der Wölfe, which was my first practical use case for Piet. I added a few features, but struggled to make my proof-of-concept tool deliver the materials I wanted. I made a list of things I needed to improve in the future.

This month, with level generation in the rear view mirror, I’ve finally revisited Piet and checked off all those tasks I’d written last year. The schema is now a proper functional language (i.e., with function calls!), reducing a ton of copy-pasted definitions. I’ve added support for water flow materials and decal materials. And I’ve rewritten my material definitions to be primarily height-based, with other properties like normals and smoothness derived from height.

Screenshot from an early version of Eldritch 2. All materials are generated in Piet.

Why do I do this, when there are existing tools that do this sort of thing better? Maybe I’m just stubborn, or maybe I just enjoy this work. I think it’s the latter. I’m a programmer at heart, and making my little tool with my little domain-specific language… it sparks joy. I’m still bad at art, but at least I’m having fun making it.

Categories
Eldritch Eldritch 2 Slayer Shock

Turtles (All the Way Down)

My procedural level generator has come a long way since Eldritch, and outside of the occasional forum post or summary tweet, I haven’t documented it in any meaningful way since 2015. I’ve made a lot of changes for game jams and abandoned projects between 2017 and 2023, and I’ve recently made even more changes as Eldritch 2 takes a clear shape in my mind. I’m sure there will be more changes along the way, but this is the current state of it.

Prologue: Goals

I’ve used versions of this procedural level generation system in two commercial titles (Eldritch and Slayer Shock) and three recent game jams (NEON STRUCT: Desperation Column, Li’l Taffer, and Schloss der Wölfe). These games each have different goals and different reasons to use proceduralism. Eldritch was a roguelike (or roguelite), and it used procedural levels in the conventional roguelike way, generating new dungeons after each player death. Slayer Shock was a longer campaign game where total failure was a rarer occurrence, so it used procedural levels to create the dozens of missions a player might attempt throughout that campaign. I only expect my game jams to be played once, but they used a mix of bespoke and procedural level design to offload some of the burden of making levels under limited development time constraints.

I have made just as many games and game jams that did not use procedural levels at all. I believe procgen is a tool to use when it supports a game’s design and production needs, and its development should be guided by those needs. When I was invited to speak at GDC about the level generation in Eldritch, I called my talk “Procedural Level Design” rather than “Procedural Level Generation” because every decision I made on that algorithm was a level design decision.

Chapter 1: Eldritch

I spoke about Eldritch‘s procedural levels in length at GDC, and you can watch me be uncomfortable at public speaking or you can make us both happier and just read the slides. Also, Eldritch‘s levels were heavily inspired by Spelunky‘s levels (just extruded into 3D), and I highly recommend reading Darius Kazemi’s excellent interactive guide to Spelunky‘s generator. But a quick summary:

Eldritch‘s generation began by generating a maze on a small 4x4x3 grid, where each grid cell represented a 12x12x8m block of space. After that maze was created, it would randomly open some additional paths to create loops in the maze. Then it would turn that into a real space by populating each cell in that grid with a “room”: an authored 12x12x8m chunk of level design, consisting of voxels and entity spawners. Each room was built to fit a certain configuration of maze directions (north, south, east, west, up, and down), and during generation, a room would be selected at random from any of the authored rooms that could fit a maze cell.

In order to minimize the amount of rooms I had to create and to maximize the apparent variation, each room could be used in any of 8 transformations: its default, or rotated by 90, 180, or 270 degrees, and a mirror image version of any of those. These were extremely simple to do because the rooms were made of voxels, so any rotation or mirroring was an axis swap or negation on the voxel grid indexing.

There was one additional step: “feature rooms” were emplaced at random locations as seeds for maze generation, and to ensure that essential features like an entrance and exit—or random features like shops and bank vaults—would appear in the maze exactly as often as the game required. These were guided by rules that would, for example, place the entrance room on the top floor and the exit on the bottom, or make a shop have a random chance of being open, closed, or entirely absent.

Every room filled exactly one cell on the maze grid. For larger features like the ziggurat at the bottom of World 1 or the outdoor snow field at the start of the Mountains of Madness expansion, I had to build big spaces out of smaller chunks, and emplace them as multiple feature rooms with fixed locations and fixed transforms. It was pretty gross.

My main goal in Eldritch was to create interesting gameplay space; it didn’t matter much if rooms fit together neatly. The Lovecraftian theme supported bizarre, unknowable geometry, so if two rooms didn’t quite align spatially or thematically, that was fine. It worked for the game! And it turned out to be an essential part of the randomness of the game, as I would find out on subsequent projects where I added more constraints.

Chapter 2: Vamp

With Slayer Shock, the modern real-world setting and procedural mission objectives demanded some changes:

  • I wanted spatially coherent procedural levels; no more random crashed-together Lovecraftian spaces
  • I wanted levels to be any size, not limited to a small 3D grid
  • I wanted rooms to be placed according to a critical mission path, not expanded randomly in a maze
  • I wanted rooms to be any size, not limited to one cell in the 3D grid
  • I wanted rooms to be composed of static geo meshes and navmesh, not voxels

The sum of these needs represented a huge delta from Eldritch‘s generator, and I rewrote nearly the entire thing. The maze generator went away entirely, replaced by a portal-based algorithm of placing rooms one by one, wherever they could fit to connect to open portals from existing rooms. Rooms now defined their available connections by portal tags—for example, a cave portal could connect to another cave portal, but not to a forest portal; and caves and forests could be joined by special junction rooms with a cave portal on one side and a forest portal on the other.

Feature rooms were no longer seeded randomly within a maze grid, but placed in sequence along a non-branching critical path which was laid out before any subsequent room expansion. This was mainly used to place entrances, exits, and junctions between indoor and outdoor areas like caves and forests.

Every region in Slayer Shock had very different requirements, and my codebase got very messy with special case hacks for each. The introduction of so many content-driven rulesets also meant that the algorithm now had the possibility to fail (which was literally impossible in Eldritch as long as there was a complete set of room configurations). And it did fail often, making level generation much slower as it continually restarted with new random seeds until a successful world was found. Worse than that, it meant that viable levels tended to be very similar. I successfully “fixed” the chaos of Eldritch and ended up with something very predictable and bland.

Chapter 3: Loam and Zeta

In 2019, I began developing a side project fantasy dungeon crawler codenamed “Loam”. That project is currently abandoned, though I’m holding onto it because it feels like it could be my magnum opus. But also it being my potential magnum opus is why it’s currently abandoned, because it was extremely overscoped as a thing to work on in my free time.

My primary goal with proceduralism in Loam was to build levels that looked like D&D maps and played like immersive sim levels. I tore out the critical path stuff from Slayer Shock and focused on how rooms were connected within a smaller space. One of the problems that kept coming up was “the door problem”: when two rooms join, where and how do I spawn a door between them? The door spawner has to belong to one room or the other; how do I ensure that there is a door and that there is only one door (i.e., both rooms aren’t trying to spawn at door at their shared boundary)? I eventually decided to use portal tags to enforce placement of small spacer tiles between rooms, and put the doors in those spacers.

I also wanted to minimize failure cases in the level generator. Most failures were due to unclosed room portals: meaning a room was placed with an open exit, but no rooms could fit beyond that exit. I solved that by allowing the generator to conclude its main expansion phase with some portals left unclosed, and then connect those portals via a series of small connective tissue tiles. These tiles are guided by a graph search between unclosed portals, and guarantee closure and connectivity as long as there is a path. The enforced space between rooms provided by the spacer tiles usually affords such a path; and as an extra step to ensure this worked well, I implemented a limited “rewind” of the generator so that spacer tiles which could not be closed would be removed and replaced with connective tiles.

My first game jam to use this algorithm was NEON STRUCT: Desperation Column, but it used an extremely constrained room set so the connective algorithm never kicked in.

Chapter 4: Fray, Lily, and Wolf

Since Eldritch, my level generator had been fundamentally an indoor generator. I faked outdoor space in Slayer Shock (at the cost of huge performance problems), but it was fundamentally designed to connect small convex rooms to other small convex rooms. I wanted to do something bigger.

In 2022, I was working on a side project codenamed “Fray” which could be summarized as “indie Far Cry Star Wars immersive sim”. Yeah, that one was overscoped too. But one of my goals was Far Cry-style outposts: small indoor complexes within a broader outdoor space, that the player can approach from any direction and scope out before attacking. It would require a radical rethinking of not only my level generator but my sector/portal-based renderer as well.

My solution was to use generators within generators. At the outdoor scale, I would generate very large “rooms” on a coarse grid; and then within those rooms, I could generate indoor sublevels comprised of smaller tiles. The most significant constraint—due to the way sublevel and superlevel portals were connected for the renderer—was that a sublevel had to be contained entirely within a superlevel room’s space; I could not use a small sublevel to connect two superlevels. But that worked fine for my Far Cry-esque goals, and I got it working in about a week.

Shortly after that, I got the idea to use room and sector “colorization” to guide room placement, so that I could build levels with sequenced gating: for example, a player must collect the red key and unlock the red door before they can access the red part of the map. I didn’t have a specific goal in mind for this, it just seemed useful and I realized it wouldn’t be too difficult to implement.

I first shipped this version of my level generator in Li’l Taffer. It had no actual outdoor generation, instead using a fixed outdoor space with a single interior generator; but the colorization feature ended up being used to demarcate the public and private space within the mansion the player is tasked to rob.

The broader feature set finally got a shipping use case in Schloss der Wölfe, which is a largely linear experience but used nested generators and region colorization to make indoor/outdoor portaling work for the renderer, to guide placement of entities, and to provide some small variations on replays.

Chapter 5: Rasa and Tofu

Shortly after I released Schloss der Wölfe, I started another game jam—something akin to The Unfinished Swan or Scanner Sombre where the player starts effectively blind and uses a tool to identify the space around them. I ended up bailing on that to focus on Eldritch 2 instead, but in the short time I was working on it, I added “furnish rooms” to my generator.

Furnish rooms function almost identically to sublevel generators (i.e., the indoor parts of an outdoor room), but they do not create new sectors and portals for the renderer. Instead, they just add all their geo to their containing room. This gave me a way to build most of a room by hand but then let the whitespace be filled in randomly.

I bailed on that game jam and returned to Eldritch 2. The last big thing I needed to address to make all of these years of changes work for this game was a problem that had been lingering since Slayer Shock. Ever since I moved from voxels to mesh-based rooms, I’d needed to make sure that navmesh edges fit together precisely so I could stitch them up after rooms were placed. This prohibited me from doing the sort of chaotic crashed-together spaces of Eldritch, because every edge of every room needed to conform to a consistent navmesh signature. I waffled on this for a while, but ultimately decided that the accidental chaos of Eldritch‘s worlds was something I needed to now intentionally repeat. I rewrote some big chunks of my navmesh stitching and pathfinding code, and now my generator will stitch together any overlaps in coincident navmesh edges, to ensure that AIs can navigate between rooms as long as there is any actual space to do so.

I also finally reintroduced two Eldritch-era parts of the generator: mirrored rooms (a whole separate blog post in and of itself because of all the new requirements of mirroring meshes and navmesh, through multiple stacked transforms) and feature rooms, which are now emplaced at two stages: first as seed rooms before the map is “colorized” for gating; and then in a second pass, for feature rooms which may depend on that region colorization.

All of this leads to the current state of Eldritch 2‘s level generation. Levels are formed by a series of nested generators (outdoor overworlds with indoor dungeons, both with rooms optionally populated by random furnish rooms); with optional sequence gating, either within the overworld or within any dungeon; and each nested level seeded parametrically to guide placement of special features like shops and bank vaults. Room placement can still fail at any step depending on the authored room content; but as long as I populate all the common cases and keep the constraints reasonable, it rarely does. And the generator produces interesting levels in under 200ms, keeping load times to a minimum.

Categories
Eldritch 2

What Eldritch 2 Will Not Be

Since I’ve mentioned multiplayer, I feel some disclaimers might be in order:

Eldritch 2 will not be a live service game. The networked elements will be run on Steam and funded by the 30% revenue share that Valve takes anyway. It will always be fully playable offline.

Eldritch 2 will not have microtransactions or DLC. It will be a one-time premium purchase, and any subsequent additions will be gratis.

Eldritch 2 will not be a worse experience if you choose to play offline. The networked elements are additive, but it is and will always be fundamentally a single-player game.

Categories
Eldritch 2

Dancing Without a Partner

I teased Eldritch 2 on social media last year, but I haven’t properly announced it. And this still isn’t a proper announcement. That will come sometime in the future, with screenshots and a trailer and a PR agency to do the words good. Please do not write that I have announced Eldritch 2. But I haven’t been shy about the fact that I’m working on it, and that I want to develop it in the open as much as possible.

I buy myself a new Moleskine notebook for every project. Most of them end up unused. Not this one.

So, the three big questions: Why Eldritch 2? Who’s making it? When’s it coming out?

Why Eldritch 2?

Since Eldritch‘s release, many players have asked for multiplayer, and my answer has remained: I originally wanted it to be a multiplayer game, but it would have blown up the game’s very tight (8-month) schedule; subsequent tech and design decisions I made along the way locked it into a single-player structure; and let’s save it for the sequel. Those last words have haunted me for a decade.

The obvious option was to follow Spelunky‘s lead and make a co-op action roguelike. Eldritch was already indebted to Spelunky in so many ways, and the model works. It was always unrealistic to convert Eldritch into a multiplayer game for tech reasons, but if I were starting a new project from scratch, I could conceivably do that.

But then I worked on two multiplayer games (The Blackout Club and South Park: Snow Day!) in my day job at Question. Both of those were developed in UE4, which has very stable netcode (which my own Rosa engine does not have); and still I was reminded how much more complicated everything is when code has to resolve four disparate game states in realtime. Multiplayer makes it harder to make games. Or to put that another way, making a multiplayer game for the same budget necessarily sacrifices a lot of features that are important to me.

So I had kinda sorta promised that Eldritch 2 would be a co-op multiplayer game, but also I didn’t want to make a co-op multiplayer game. And the bigger question in my mind was: why make Eldritch 2 at all? Was I just wanting to relive a past moment of glory? Hoping that the modest success of the first game could penetrate the crowded indie scene? What could I do with a Lovecraftian roguelike immersive sim that I couldn’t just do by adding some free expansions to the original game?

It took a long time and a few false starts, but I feel that I’ve found a way to answer those questions and weave multiplayer into it. I don’t want to give too much away, because it is part of a central mystery of the game, but I’m using the procedural nature of roguelikes in a way that I don’t think I’ve seen done before; and which has the convenient side effect of synchronizing the pseudorandom levels that each player sees. The best analogy I can offer is: imagine the daily challenge mode in Spelunky, where every player sees the same levels on the same day—what if you could also see all the other players running those levels at the same time? And what if there were secrets only accessible on certain days? And what if you could rewind to previous daily challenges, and find those secrets again?

Who’s Making It?

It’s me. It’s just me.

Since 2017, I’ve worked full time with Question, and I hope to stay with Question for years to come. They’ve allowed me to continue my personal work at Minor Key Games (even if it has been mostly on the backburner); and in the last year, I’ve switched to working 4/10 weeks (four ten-hour days), opening up my Fridays for personal use. That has given me a lot more time to work on side projects like Eldritch 2.

I developed the first game (and large parts of its engine) in about 8 months, solo, save for an assist from my old Guildhall colleague Kale Menges on the key art. I like the creative independence of working alone; not because I’m allergic to outside ideas, but because I believe the strongest ideas come from a creator with a holistic view of all the parts working together. I can’t imagine trying to pitch the networked elements of Eldritch 2 to a team that was developing the roguelike elements separately; they all need to function with each other.

I formed Minor Key Games with my brother in mid-2013, and we have operated the company as a collective; a shared identity with shared values, but making separate games. This has led to some confusion, with each of us sometimes being credited for games the other made. To be clear, I’m making Eldritch 2 alone. He’s making Project Verse and I’m excited about that.

When’s It Coming Out?

Man, I dunno. I work fast and I’ve got an engine that was literally made to make Eldritch, but this is a part-time project for me, and the scope of Eldritch 2 is substantially bigger than the first game in a few ways. If this were a full-time project, I’d estimate it could be done in 12-18 months. Given my limited time to work on it, it will take, uh, longer than that.

I don’t have a real schedule yet. I don’t know when I’ll have a proof of concept build or a first playable milestone. I don’t know when I will have enough real art to put together a Steam store page and properly announce the game. I don’t know what my marketing strategy will be. I don’t know if I’ll do any kind of public beta or early access. I don’t know when it will be done.

What I do know is that it is rewarding to share things as I work on them, and that early feedback will help shape it into a better game. And this is the most fun I’ve had MAEKing GAEMs in years, so I’m going to keep chasing that.

Categories
Eldritch Eldritch 2 NEON STRUCT Slayer Shock

Rosa

I’ve been developing games professionally for 17 years, and I’ve been developing my personal engine Rosa for about as long. This isn’t going to be a post about whether or not you should make your own engine. I generally wouldn’t advise it, but you should make your own decision. I made my decision a long time ago. I love making games, but I love programming too; and engine development provides some of the most satisfying programming challenges. This is a brief history of Rosa and my current ideas for its future.

The origins of Rosa came from my student work at SMU Guildhall (2005-2007). As part of the curriculum, every programming student had to build their own 3D game engine, with OpenGL and Direct3D rendering, audio, input, collision and physics, etc. The more game-specific parts of an engine were left to each individual to focus on, and I chose to pursue AI with studies on behavior systems, awareness models, and pathfinding. This was my first time writing a 3D engine from scratch, and I got it done but it was messy.

When I graduated in 2007 and became a full-time professional game developer, I also began building a new personal engine, taking the good parts of what I had learned and cleaning up the messier, poorly-architected bits. Incidentally, I jettisoned OpenGL at this point, believing Direct3D to be the way (and Windows to be the only OS). I did this work mostly for fun, but I also hoped to eventually make a game of my own. Between 2007 and 2012, I built a lot of engine parts without ever getting close to actually shipping a game.

Brass City Couriers (unfinished, circa 2009-2012).

I leaned on Blender heavily at this time, even using it as my level editor because that seemed easier than building a level editor. The lighting model was baked and static, encoded into level geo as vertex colors and applied to dynamic meshes through a coarse volumetric directional ambience grid. Game objects were inheritance-based. Development was slow.

Codename “Agency” (extremely unfinished, circa 2012).

In the summer of 2011, I was frustrated with the inheritance model of game objects and decided to experiment with this fancy new “Entity Component System” concept I’d been hearing about. I was also eager to explore procedural generation, for reasons I don’t quite recall but probably had something to do with Minecraft. I built a simple textmode graphics framework and began building an infinite procedurally-generated world populated with composition-based objects. It was fast, it was fun, and in retrospect, it may have been the most important step I could have taken in my career.

Second Order (unfinished, circa 2012).

In early 2013, I left the relative security of my Big Commercial Games job and went into business for myself. (A few months later, my brother did the same and we formed Minor Key Games.) I had a very limited budget and needed to ship a game in around 8 months. The obvious choice might have been to lean on all the tech I had built over the prior 6 years, but instead, I ripped it all up and streamlined everything. I used the simplest possible art and rendering: all albedo, no normal maps, no specular lighting. I rewrote my collision code for AABBs exclusively, because it’s fast and simple and I was using voxels so everything was axis-aligned anyway. And I incorporated some ECS concepts to make game object composition and reusability a breeze.

Eldritch (2013), Minor Key Games.

My engine didn’t have a name at this time, but I’ll call this Rosa v1. Eldritch was a modest success and I followed it with my Thief-meets-Deus Ex cyberpunk stealth love letter NEON STRUCT. I didn’t make many engine changes for Neon; I improved my tools and optimized the voxel lighting grid to support turning lights on and off at runtime, but it is fundamentally still Rosa v1: all albedo, with voxel worlds.

NEON STRUCT (2015), Minor Key Games.

By the time I finished Neon, I was growing tired of Minecraft comparisons. I liked the ease of working with voxel worlds, but I couldn’t escape the shadow of the king of voxels. I was also starting to feel constrained by how much I had cut my engine to the bone; in particular, I was constantly working around the lack of dynamic lights and eager to rebuild my rendering skills. I undertook a substantial overhaul of my engine during development of the next game, removing voxels in favor of mesh-based geo and replacing the simple forward renderer with a deferred renderer using a principled physically-based lighting model.

Slayer Shock (2016), Minor Key Games.

My engine still didn’t have a name, but I’ll call this Rosa v2. Slayer Shock missed the mark, but that’s a story for another time. I ran out of money around the end of 2016, got a job, and largely put my engine and my games aside for a while. From 2017 to 2020, I dabbled in a couple of projects but ultimately got nowhere. But while copying code back and forth between a couple of projects and renaming classes to match the project, I realized that it would be easier if I had a unifying name for game-level classes in my codebase. So instead of renaming top-level classes like EldritchFramework to NeonFramework to VampFramework, all my games could use one common codename. I picked “Rosa” because it was like tattooing “MOM” on my arm, and that became the de facto name of my engine.

In 2020, stuck inside while a pandemic raged on, I started doing game jams to scratch the indie dev itch. I finished four game jams that year, and have done at least one every year since. These game jams culminated in what I consider a trilogy of minimalist first-person games: NEON STRUCT: Desperation Column, Li’l Taffer, and Schloss der Wölfe. Each of these improved upon my engine’s pipeline and workflow, largely by leaning on procedural generation. I developed tools to quickly generate procedural meshes and materials for world geo, and I reinvented my procedural level generation algorithm to create indoor spaces nested inside larger outdoor spaces. These three games also share a common visual style which I’ve been calling my signature “toy aesthetic”.

Schloss der Wölfe (2023).

I consider this current generation of the engine to be Rosa v3, and it is the engine I am now using to develop Eldritch 2. It combines the fast iteration and relatively simple art pipeline of Rosa v1 with the physically-based dynamic lighting and materials of Rosa v2. It’s a joy to work in, and work on. My current work is revising my procedural level generation (again) to bridge the gap between the more spatially plausible worlds of my recent game jams and the intentionally bizarro crashed-together spaces of Eldritch.

Looking to the future, there’s at least one unavoidable tech task that I need to deal with. Rosa is still using Direct3D 9 and OpenGL 2, absolutely ancient graphics APIs that have increasingly fragile vendor support. I don’t feel the need to upgrade for any particular modern features—I’m actually rather happy working with 20-year old tech—but OpenGL is no longer supported on Mac and getting a modern Windows PC set up to build a Direct3D 9 app requires jumping through a lot of hoops that they would clearly developers not do anymore. I’ll have to port to Vulkan eventually, and probably Metal if I ever want to target Mac again.

That’s a lot of words, and I’ve barely scratched the surface of my engine. I’ve got some tech that has been largely stable since as far back as Eldritch: AI behavior trees, event + reaction gameplay scripting, collision detection and sweeps/traces. I’ve got a custom cook process that is fast as heck. I’ve shipped 13 games or game jams on this engine, and it has grown a lot along the way. And I’m excited to keep using it!