While we my not often think about it, movement of objects is one of the key aspects of most video games. In any game that has any sort of characters, enemies or items interacting in a fictional – or real – world, these agents and objects have to move in order for anything to happen.
In most cases, we want these movements to appear natural, or at least believable and consistent. This allows the player to better put themselves in the feet of the protagonist, or avatar, as they can control their movements in an intuitive way – not having to think about each key press individually, but merely willing to move in a certain direction and letting muscle memory take over from there.
One of the most obvious ways to calculate movement in a game or simulation is to emulate real world physics in some or all aspects.
The closer the in-game physics are to what we expect from real life, the more intuitive and believable our game will be – at least in theory.
Let us then take a look at real world kinetics – the study of motion and its causes – and see if and how we can easily implement the same inside a game.
To limit our scope for this post, we will only look at Newtonian physics (and skip Einstein’s relativity), and even further, we will only consider the movement of single objects, and not talk much about the interaction between them. Lastly, we are also going to skip on rotation for now. All of these are topics for a different post.
Everything discussed here applies equally well to two or three dimensional space.
Newtons laws of motion
Since we are looking at Newtonian physics only, let us start out with Newton’s laws of motion and work out way to an implementation from there.
- First law: When viewed in an inertial reference frame, an object either remains at rest or continues to move at a constant velocity, unless acted upon by a force.
- Second law: The vector sum of the forces F on an object is equal to the mass m of that object multiplied by the acceleration vector a of the object: F = ma.
- Third law: When one body exerts a force on a second body, the second body simultaneously exerts a force equal in magnitude and opposite in direction on the first body.
Since we are not dealing with interactions between bodies, we can skip the third law for this post.
To make sure we understand them, the other two can be restated – albeit slightly less accurately – as follows.
- First law: If no force acts on an object, it will continue moving with the same constant velocity it is moving in at the moment – if it is not moving, it will continue to not move.
- Second law: If an object is accelerating with acceleration a and has mass m, the total force acting on it is F = m * a.
Note that in the second law, both F and a are vectors. That means that both force and acceleration are directed – and since m has a single dimension, they have same direction.
Further, it follows that if a total force F acts on an object of mass m, that object will necessarily accelerate with acceleration a = F / m.
Positions, velocities and accelerations
The above is all well and good, but it does not give us an actual description of how an object will move exactly when we accelerate it.
To understand that, we have to look at the definitions of velocities and accelerations.
The former is the rate of change in location/position, while the latter is the rate of change in velocity.
Mathematically, the velocity is the derivative of the position over time, while the acceleration is the derivative of the velocity over time.
v = dp / dt a = dv / dt
In other words, if an object is moving with 5m/s, after one second, it will have moved five meters from where it was before. Similarly, if an object is accelerated with 5m/s2 its velocity will change by 5m/s each second.
Of course, if an object is accelerating, its velocity does not just tick up once a second. Instead it increases continuously. That means that at any point in time, an accelerated object’s velocity is changing. This makes it much harder to calculate the objects position over time, compared to one that moved at a constant velocity.
Newton solved that problem by inventing Calculus.
With Calculus we can take the integral over our changing velocity to determine the resulting change in location over a given time period exactly. If our acceleration is changing too, we can integrate it to calculate our velocity, and then integrate again for the position.
Unfortunately, calculating real integrals in games is in most cases infeasible. The problem is that in a real game with interaction between objects, accelerations and velocities change very rapidly and radically, resulting in functions that are almost impossible to integrate efficiently – or at all.
Therefore, we have to approximate. Intuitively we can think of the integral of a function as the area below it. To approximate this area it is enough to take a large number of samples of the function, sum them up, and divide them by the number of samples.
In other words, we divide the area under the function into a number of intervals on the x axis (or in our case the time axis) and draw a rectangle from the baseline to a point of the function in that interval.
By summing up the area of those rectangles, we get an approximation of the area under the function.
The key aspect that allows us to use this method to calculate our integrals is that as long as our function is mostly continuous (for the precise condition, see: Riemann Integral Integrability) the Riemann Sum will converge on the real integral, as we take more and more samples.
This can clearly be seen in the following animation: By taking ever smaller intervals under the function, we cover the area more and more accurately.
Applying approximate integration to our physics
Going back to our topic of game physics, we can apply this way of approximating an integral as follows.
Let us say we need to know the position of an object at time t=1s. However, we only know where it was at t=0s.
Let us say we also have an idea of how the object was accelerated during that entire time.
We can now apply a Riemann Sum to approximate the real integral of the acceleration function to calculate the velocity function. Given that, we can repeat the same process to calculate the object’s position function, and from it we can read the object’s position at any time, including the time we are interested in.
For the sake of argument, let us use a Riemann Sum with 10 intervals. That means that each of our intervals will be 0.1 seconds in length.
To calculate the velocity of the object at any point we simply have to sum the acceleration values every 0.1 seconds starting at t=0 until the time we are interested in, and multiply the result by 0.1s to account for the width of our approximating rectangles under the curve.
v(t) = 0.1s * ∑(acceleration samples before t in steps of 0.1s)
Note that by multiplying with 0.1 seconds we also gain the correct unit: m/s2 * s = m/s. The same applies to the formula below
And similarly, we can do the same calculations to determine our position:
p(t) = 0.1s * ∑(velocity samples before t in steps of 0.1s)
Note how for each passing 0.1 seconds, we add one more sample to our sum. However, the rest of the formula does not change. that means, we can easily refactor this calculation to be recursive, instead of iterative, which will come in handy in just a moment. We end up with the following relation:
sum = previous sum + next sample v(t) = v(t - 0.1s) + a(t) * 0.1s p(t) = p(t - 0.1s) + v(t) * 0.1s
As you can see, these formulas only require two sets of data: the acceleration at the current interval, and the velocity and position of the previous interval. From these three values we can calculate the new position and velocity.
Let us abstract a bit further. Instead of thinking of samples, let us simply thing of these formulas as general relationships between the three physical quantities in question. Further, let us remove the fixed time step we chose and replace it by a generic delta time
v(t) = v(t - dt) + a(t) * dt p(t) = p(t - dt) + v(t) * dt
Remember, that as we have seen above, this relationship is of course only an approximation. The smaller our values for
dt, the better this approximation will be.
Implementing basic game physics
Let us again remind ourselves why we are manipulating these equations in the first place: We want to simulate physics in a game or simulation.
The way we represent the state of our game world is by having some sort of collection of all our objects in the world, each storing its current state.
The game world is updated a number of times per second (commonly 60 times or more to correspond to the refresh rate of monitors). With each update, we loop over all our objects, and update them each by changing their positions, and other properties according to our game logic.
If we wanted to have 100% accurate physics, we would have to calculate the integrals of acceleration and velocity for the time since the last update, and determine our new position and velocity.
Instead, we can use our approximation.
Let us say our object stores its own position and velocity. We can then use the formula we just derived to update both as follows:
dt = time since last update current velocity = last velocity + current acceleration * dt current position = last position + current velocity * dt
It is as easy as that!
Note how we replaced the entire integral by a single step through our equations relating the three physical quantities. While this is just an approximation, since we are dealing with for example 60 updates a second, it is as good an approximation as taking a Riemann Sum with 60 rectangles under the curve to determine the position after one second.
In most cases, this is more than accurate enough.
If it turns out that our game needs a better approximation however, we can simply update it using the same code but with smaller time steps.
An added bonus with this method is that even when the game runs at inconsistent frame rates, the accuracy of our approximation might suffer slightly, but objects will still move the same speed, no matter how often they are updated.
In this post we worked our way from Newtons law’s of motion to a simple few lines of code that can be used to simulate the movement of objects in a game, as they respond to being accelerated by various forces we might expose them to.
We have seen how working from principles and definitions can help us ensure that our resulting solution is indeed correct, or in this case a correct approximation.
Part of that is the note on checking the units of our values. By ensuring all our units work out correctly, we can eliminate many errors that might occur otherwise and may be hard to track down.
Of course, there is a lot more that can be said on the topic of game physics. We barely scratched the surface here today.
Feel free to leave a comment below if you have any questions, or if there are other topics you would like me to write about.
Enjoy the pixels!