It's been a while since our last post, but we're working towards some big updates to share this fall. Today, I'll highlight a few key engine improvements that have helped us develop Thumper.
The benefits of a tool that enables non-programmers to hookup effects, animations, sounds, etc. with simple logic are obvious -- every major game engine has something like this. But as a small developer with a custom engine, the cost of building such a complex tool can be hard to justify. You want to make games, not tools, after all. We started this project using a simple property-grid based system and custom object editors, but as the game became more complex, it became unsustainable. Brian was often blocked, waiting for me to create or extend custom objects. So we finally decided to roll our own visual scripting tool. Here's what it looks like.
While implementing, I was concerned with performance and this post on the BitSquid engine's system was a huge help. By focusing on memory layout up front, we have a scripting tool that is cache-coherent and fast. Including the UI, it took about a month to implement. It was worth it.
With this tool, Brian can do much more without my involvement. It's easy for me to create custom script nodes in C -- that's a good way to expose more complicated functionality as needed. I'm tempted to integrate a scripting language (like Lua) as an intermediary between our visual scripts and C, but for Thumper, that's probably not necessary.
I added a "single-step mode" where you can pause the game/editor and advance it by a single frame with a key press. It's simple, but useful. Brian often uses this to closely examine animation and effects. Thumper has a lot of fast animations and quick interactions, so this helps us make sure everything looks and feels completely solid. While in this mode, the main game loop is not run and the time line is not advanced. That's convenient when I'm debugging and trying to reproduce particular conditions or set breakpoints.
Its simple to use, you just press F6 to enter single-step mode. While in the mode, you press F6 again to advance one frame, and Ctrl+F6 to exit the mode. Here are a few frames of an animation, each advanced by a single frame.
Entities and Instancing
Thumper has many different instanced gameplay objects, or entities, including rhythmic cues, decorative meshes, effects, enemies, etc. Initially, we developed entities in an ad hoc way. Brian would create the raw resources and custom code would allocate memory for each instance and stitch everything together. It was OK for prototyping, but things got gnarly fast.
So I finally built a real entity instancing system. Now each entity has its own resource file with a well-defined interface that can be extended with our visual scripting tool. Draw calls for instances are batched. Efficient memory allocation for dynamic entity instances was a challenge, but I found the sections on game object memory management in this game engine architecture book useful.
This screenshot from our test mode shows the obvious benefits of an entity instancing system.
I created some high-level visualizations of what's going on in the game. Including things like audio playback, animations, tasks, memory usage, etc. This is another example of something easy and simple that's indispensable once you have it. Here's a visualization of active animations and tasks. It's hard to overstate how useful it is for debugging.
We've come a long way, but some big tech challenges are ahead. Over the next couple of months, I'll add automatic draw ordering for our renderer, a "shader graph" for creating custom post-processing effects, and a packaging system for our builds, to name a few of the big features. I'll post more about our tech developments as they're implemented.