System Fault Devlog 5

Been a while since I did one of these. I said that I wouldn’t unless I had something worth reporting, which (I think) I finally do.

Cutting straight to it, the great physics refactor is finally done over a month after I began. All the obvious systems and some non-obvious ones now use the physics engine for their computations. The original Rampage had some pretty awful code in bunches of places, most of which is now cleaner and easier to reason about. Best of all, most of this work is portable, meaning any future games I develop should be able to use it with minimal fuss.

Last week’s focus was performance, which presented several challenges:

  • Bevy outputs performance data in a form readable via chrome://tracing. Either I don’t know how to use that interface, or it is inaccessible.
  • Even if I could figure out how to read the profiling JSON, Bevy 0.5 has an additional bug wherein the actual data I’d need isn’t even in the trace. That’s been fixed, but it isn’t in a release yet, and I can’t use the main branch.
  • Programming is one set of rules. Physics is another. When the two combine, the best debugging tool is often the physics engine’s debug renderer, which I can’t use because I can’t see the shapes corresponding to physics bodies. Sometimes performance issues are actually physics behaving exactly as it should, and the solution is tweaking the model itself rather than optimizing non-physics performance. But without the debug renderer, knowing when to use either approach is tricky.

With no other choice, I did something I normally don’t like and made some shotgun optimizations, tweaking bunches of code based on intuition. The biggest change switched map modelling from a separate collider per blocked tile, to single colliders for horizontal walls, with some tracing optimization to detect where square and rectangular expansion was possible. The result was a nearly 3 order of magnitude reduction in the number of map colliders, reducing them from roughly 19000 to about 50. Given that everything from pathfinding to visibility now relies on collision detection, slashing that space so dramatically improved things across the board. I don’t have hard numbers because of the lack of usable profiling data, but the game feels far less janky and performs reliably.

Another win was in compilation times. The non-performant code I initially wrote needed higher optimization levels to be playable, resulting in a minute or more of delay for each iteration. After the performance tweaks, I found that I could return to the initial non-optimized builds with the original performance characteristics. This restores compilation times of around 15 seconds, meaning I can iterate much more quickly.

There are still minor issues. Bullets seem to lose all velocity when colliding with something, even if the solver is told to filter out that particular collision. I suspect a physics engine bug based on some conversations I’ve seen, but I can hopefully work around this one. But otherwise this work is done, and I can return to game development with a cleaner and more stable foundation. Next week I’ll be fleshing out powerups and robot AI, driven largely by what the physics engine enables.

One other factor delayed this work. Burnout. I don’t want to get too far into things, but accessible game development is hard and lonely work. When that work is less accessible or particularly challenging, continuing onward is tough. Fortunately, I have the space to take breaks from time to time, and I took last week to focus on myself. The burnout isn’t gone, but it’s been beaten back as well as I’m able to right now. If you’re feeling similarly, and have the freedom to take time off for self-care, do it. You’ve only got one life. Don’t wreck it for a project, audience, or anything else that has challenged you more than you can currently manage.