Fair warning: This is a very programmer-y post. It’s a post about refactoring, the kind that ends with a triumphant, “And everything worked exactly as it did before!” which is perhaps not the most interesting thing to read.
In You Have to Win the Game and Super Win the Game, world geometry is completely static. Anything that can move or change its state is implemented as a separate entity, including enemies, which mostly bounce between walls, and platforms that may move, crumble when stepped on, or pop into existence when bopped from beneath. Everything else is locked in place and may never change. Some tiles may have different effects, for instance spikes can inflict damage and water can change movement physics, but these tiles can never become anything other than what they are.
This limitation is due in part to how world collision is generated. Each time the player enters a room, a static set of collision surfaces is generated for the entire room, as shown below. In order to improve performance, adjacent surfaces are merged, reducing the number of collision elements over which we must iterate each frame. This process is too expensive to repeat frequently, so we do this once at entry time only.
In Gunmetal Arcadia, I’ve made a change that will facilitate dynamic environments should I want them (e.g., for destructible walls, etc.). I now generate a small set of collision surfaces as they are needed. As the player moves through the world, we search for collision surfaces only within a box containing the entire path of the traversal. Because this is done on a per-frame basis, we are dealing with very small movements, so these search areas tend to be very small as well. In this case, there is no need to merge adjacent surfaces because the set is already so small, as shown in the example below.
(Note that I’m representing this with Super Win assets only because I have not yet begun designing sprites for Gunmetal Arcadia. To be clear, this change does not exist in Super Win; that game is bound by static geometry just as You Have to Win was.)
Because collision is generated on the fly every single frame, I now have the option to change the structure of the world at runtime, and the collision should react accordingly.
I have yet to do a proper performance test of the old and new methods, but by a naive FPS test, there is no difference. What this tells me is that collision is probably not my bottleneck in either case. In the event that generating collision surfaces for multiple moving actors every frame becomes prohibitively expensive, I could optimize the process by caching surfaces and invalidating these caches if the world structure changed.
I should mention too that I haven’t actually tried changing the structure of the world at runtime yet. There will be other problems to solve before this can be a real feature, such as updating the renderable mesh, which has also historically been static except for animated elements. This is one step of several on the road to facilitating destructible or otherwise dynamic environments. But for right now, it’s just in a place where old code was replaced with new code, and everything worked exactly as it did before!