Your calculus voodoo majik is very strong 8D <3
lost contact devlog 2
The following content is a transcript of the video.
lost contact devlog 2
Do you remember the prototype I showed off last week? Well, I deleted it. The purpose of a prototype, in my opinion, is to experiment and get a better understanding of what I’m trying to build.
You might have experienced this yourself, if you like making things or even if you just like playing games. The first time you do something, it’s not so great because you don’t have any idea of what’s going on. The second time you do it you have a better idea of what to anticipate and you can do a better job. Making a prototype gives me an opportunity to get a better idea of what kinds of solutions may or may not work well, but it only works if I commit to throwing it away so I can have a second go at it.
So it’s deleted. Unless you’re a skull patron on Discord, because you funded this foolishness. You see that soft lock under “Known Issues”? I’m not fixing it.
Anyway, one of the things that really bothered me in my prototype was the camera controller code. Creating a new API to do all of this interpolation nonsense for me seemed nice, because I would be doing interpolation in many other places in the game. And, at first, it seemed like it would be easy. I mean, on Monday, at the end of my stream I was saying:
And uh, I will see you all tomorrow! And hopefully we’ll implement another system that won’t take nearly as long.
Foreshadowing is a literary devi-
interpolation is easy, right? right???
If you’re not familiar with how games work, games, like animations, make things look like they’re moving on a screen by showing you a series of images really quickly. Lets say, for example, that we want to render 60 images every second, also known as 60 frames per second.
Every time we want to render a new frame, we also want to update the position of everything in the game as if 1/60th of a second, or 0.016 seconds, has passed. This creates the illusion of motion.
So, if we use the code I showed earlier to interpolate between 0 and 100, the value will look like this. After 1 second, the value for a game running at 60 fps is 98.407. For a 30 fps game, it will be, uh… 98.634. Wait, these aren’t the same numbers!
Sure, they’re close, but this is only after one second. Imagine what would happen if I let the player move the camera around, constantly changing the value we’re going to, and we decided to play for longer than a second.
You can reliably side-step this issue by ignoring the frame rate and sticking to some fixed rate instead and just doing that as many times as needed until you get to the current time. But I don’t really like that approach because it’s really just an approximation of what the real answer is supposed to be and it makes it harder to implement the interpolation API I want to use.
So, how do we do this without approximating?
oh no, calculus
Wait wait wait, don’t go! I’m not going to get into the details of how calculus works and how to use it; this video would take too long to make otherwise – and this is a devlog, not a math video. I already spent hours stumbling over that while streaming, and I’m just going to play that in the background while I talk about the design of the interpolation API I wanted and why that resulted in calculus.
First of all, I’m not trying to say that approximating is bad. However, I didn’t feel like approximating aligned with how I want my tools to work. I want to use tools that, among other things, uses mutable state sparingly and prioritizes exact, correct answers over fast, inaccurate ones. I wanted an interpolation API that could, given some initial state, calculate the new position and velocity at any arbitrary point in the future.
And, this isn’t even the first time I’ve tried making this kind of API. A long time ago I worked on an interpolation library that worked exactly the way I wanted it to in Unity. I used it in quite a few projects, so I want to create something for Godot that I am happy to bring into other games.
I remember the library being really nice to use, but while porting it to Godot I found out it was wrong. In fact, it was just the usual approximation with extra steps. Alas, it’s been so long since I’ve written the original library that my needs and desires have changed. I want it to be exact!
Unfortunately, or maybe fortunately depending on whether or not you like math, calculus is what I need to use in order to get the answers I’m looking for. This is because I have things that continuously change like smooth interpolations that can speed up and slow down and calculus is about things that continuously change.
Thankfully, I found a few resources to be very helpful for figuring all of this out:
- First, there’s a blog post titled Critically Damped Spring Smoothing from Math Proofs written in 2013 by Phil Nowell. Many game development articles I found about how to implement critically damped springs are more than happy to sort of gloss over the mathematical formula for implementing one and launch straight into iterative approximations. This, unlike those resources, went through each mathematical step for finding the formula to a critically damped spring in the first place.
- There’s also this random git repository called DRAKE from RobotLocomotion that just happened to have an implementation of a critically damped spring in it, without any iterative approximations. I found this on accident so I don’t really know this repository is or what it’s for, but it was really useful for checking whether or not my solution was correct or not.
Anyway, now we can create new camera controls with code that looks like this. And, I just want to say thank you to all of my patrons for their support, it really means a lot. Don’t forget to join the Discord server and- What do you mean it looks the same? I promise it’s very different, look, it’s simple. It’s just a little bit of calculus voodoo magic and-