Hello everyone and welcome back to the Blightmare development blog. Today I’m going to begin a series of posts that will describe why we rewrote most of the logic for the game twice and what we learned along the way. I don’t know how many parts this will end up being, but hopefully it’s interesting!
In one of the first posts on this site, I briefly mentioned Object Oriented design and Data Oriented design but never went into why they matter and how that impacts Blightmare. The reason for that is because it’s quite simply a massive topic and I didn’t think it fit in with the early introduction series. But here we are now taking the plunge.
The core mechanics of Blightmare have actually been written 3 times. This refers to the physics, collision response, net behaviour, jumping, bouncing, platforming, and swinging. It probably seems highly inefficient to redo that much work over and over, and that’s correct. Looking back is always easier than looking forward however, and we learned a lot through the journey so it’s not all that easy to determine how much was lost. In this post I’m going to lay out what the original plan was in terms of how mechanics would be built from a programming point of view and how they were intended to be used to construct the game.
Unity provides a fundamental unit of logic called a “Component” which is typically used by extending the provided MonoBehaviour class. This class provides hooks to do setup and teardown work as well as several different parts of each frame. For our purposes the important callbacks are Update and FixedUpdate. Update is called one time for every frame of the game while FixedUpdate is called at an even rate – 20ms by default – which means it could be called 0 or more times for each render frame, depending on what the FPS actually is.
Games with precise timing of mechanics, like Blightmare, benefit significantly from a fixed time step because algorithms can worry less about trying to maintain frame rate independence – going faster or slower depending on the frame time – and many issues related to numerical stability can be essentially ignored. The problem with doing everything in a fixed time step is that when the actual render rate is different, there can be noticeable visual issues such as stuttering or jittery motion. For this reason, visual effects need to be done in Update so they can be synchronized with the render frame. This may sound complicated, but it really boils down to: logic in FixedUpdate and graphics in Update.
This plan seems pretty nice and simple, but like most things, there’s always a catch. In this case, some attributes like the position are shared between logic and animation. It may not be immediately obvious why that’s the case, but if you think about a moving platform then you can see what the problem is. We want the position of the platform to be updated logically in FixedUpdate, but we need it to be interpolated smoothly during Update to make the animation nice. The solution that we use in Blightmare is to slightly delay the visual position of things relative to their logical placement. Essentially, everything that moves during the logic tick keeps a buffer of previous and current positions and also which fixed frame those positions happened to fall on. Then in Update we take the current time and shift it backwards by a single fixed frame to ensure that we’re always behind the latest position and use that time point to interpolate a position from the buffered positions. There are some cases where highly variable frame rates may cause issues with this scheme, but in that case the game is probably not playable anyway so we don’t need to bother trying to deal with it. The other benefit of interpolation versus extrapolation is that we always draw objects in a place that they actually were. Extrapolation always puts objects in places they may be, but at the endpoints of motion – again consider a moving platform that is oscillating back and forth – it will overshoot by mispredicting that the platform will either stop or reverse. This would be a minor effect, but it’s something we can avoid at the cost of a slight visual delay and it’s the choice we have made.
This brings me to the end of part 1. Next week I’ll talk about what components were made and how they fit together, building on the foundational separation of logic and graphics. I hope you will stop by for that!
If you like the content on this blog and want to support the game, please wishlist Blightmare on Steam and give us a follow on twitter. Stay safe and be strong out there. We’re going to get through this together.