This is the last of three catch-up posts, after the asset system and the platform layer. It’s about the change that the other two were leading up to: games are no longer part of the engine. They are projects that the editor builds, runs, and hot reloads.


The Problem

In the asset system post I described how Arkham ended up compiled directly into the engine after my first attempt at loading it as a module. That kept me productive for a while, but it meant the engine had an Arkham editor mode, Arkham game state, Arkham assets, and a “Play Game” button that only worked for one game. Starting a second game would have meant adding all of that again.

Back in December I also decided that the engine should be more of a library than a general-purpose framework. Every game writes its own systems on top of what the engine provides. For that to work, there has to be a clear line between the two.

So I planned it out properly this time, in five steps: a projects folder, a boundary between the game and the engine, the editor building projects, per-project assets, and finally moving Arkham out.


Projects

A project is a folder: projects/<name>/ with <name>.cpp in it. The title screen lists every project it finds, and New Project writes a small template to start from. The template is a game with an orange square that moves with WASD, which is enough to see that everything is connected.

Every project also has its own data/ folder and its own editor_metadata.json. When I open the Assets Mode, it opens the assets of the project selected on the title screen, and a running game loads its own KEA. The engine’s own data/ is only for the editor. Moving Arkham’s data out of the engine’s folder was a single step, but it moved a little over four hundred files and about a gigabyte of card images.


The Boundary

A game includes exactly one header, engine_api.h, and exports one function, GameUpdateAndRender.

Most of what is in that header is compiled into the game itself: types, math, memory arenas, strings, random numbers, input, and Nuklear. None of that needs to go through the engine. The rest is the engine_api table, a struct of function pointers that the engine fills in and passes to the game every frame. Rendering, asset lookups, fonts, and anything else that needs the engine’s state goes through it. The table is rebuilt every frame on purpose, because when the engine itself is hot reloaded, all of its functions move.

The game library doesn’t link against the engine at all. When I built the template by hand to check, the only thing it imported was KERNEL32.

The engine also owns the game’s memory. A game_memory struct holds a permanent arena and a transient arena, and the game keeps its state in them. Because the memory belongs to the engine, it survives when the game’s code is reloaded. This is the same trick Handmade Hero uses for the platform and the game, applied one level up.

It has one consequence that games need to respect: no function pointers in game state. After a reload, the old code is gone, and a pointer into it points at nothing. In Arkham, card effects are looked up by card code with a switch instead of being stored as callbacks on the cards.


Build, Run, Reload

Pressing Run builds the selected project with the same compiler and flags as the engine, in the background, and loads it when the build is done. From then on the editor checks the project’s source files every half a second. When something changes, it rebuilds. A good build loads the new library, swaps it in, and unloads the old one, while the game’s memory stays where it is. A failed build shows the compiler output, and the last good code keeps running.

The library is always loaded from a copy, so the original can be overwritten by the next build while the game is running. The platform keeps a list of every library it loaded and unloads what’s left when the editor quits, so the temporary copies don’t pile up.

For a small project the whole cycle takes about a second. I save the file, glance at the screen, and the change is there. This is the part that made all the work worth it.

The controls for the running game live in the debug window from the previous post: Stop, Restart, the build status, how many builds and reloads happened, the build log, and which asset file the game loaded. Stop turned out to be trickier than expected. Asset loads that were still in flight could finish after the game’s memory had been freed and write their textures into it. Stop now takes two frames: the first lets every pending load finish, and the second frees the game’s textures and closes its asset file.


Tools for Games

Games get the same tools the engine uses. The profiler macros work in game code, and the game’s blocks show up in the profiler nested under GameUpdateAndRender. The LOG macros work too, and their lines go to the same log panel.

The newest addition is text. GetFont, PushText and GetTextDim load a .ttf file from the project’s data/ttfs folder with stb_truetype and draw text with it. Each whole-pixel size is baked into its own atlas texture the first time it’s drawn, from 128 by 128 up to 2048 by 2048, whatever fits. Because glyphs land on whole pixels, text stays crisp at any zoom level.

Adding text also uncovered an old bug. The first test crashed on a simple color assignment. The vector type v4 is an SSE __m128, which the compiler expects on a 16 byte boundary, but render entries were placed wherever the buffer happened to be. The other render functions never got the aligned store, so the bug just sat there. Every render entry now starts on a 16 byte address.


Arkham and Spellweaver

Arkham is now a project like any other, and the engine doesn’t know it exists. Its progress deserves its own post, but in short: the card database for the Core Set is in place, and the basic round structure, skill tests with the chaos bag, enemies, and the encounter deck all work. Player cards are next.

It also has company. Spellweaver is back after a long break, the same game that the SSWM format was originally made for. It’s now a project in the engine, a real-time top-down 2D RPG built around combining elements into spells. It was the first one to use the new text drawing, and it got the Q key as a cast button, which is the kind of small engine change a game should be able to ask for. It will get its own posts once there is more to show.


What’s Next

There is still a lot missing. Games only run inside the editor, and there is no standalone export yet. Everything in this post was built and tested on Windows, and the Linux side hasn’t caught up with the projects work. Some debug features, like watching values in the debug window, are not available to games yet.

But the structure is finally the one I wanted. The engine is a library with an editor, and games are separate projects built on top of it. Starting a new game is a button press, and changing it is a file save.