Physics
For every action…
For every action…
Much of gameplay derives from how agents in the game world (players, enemies, puzzle pieces, interactive items, etc) interact with each other. This is the domain of physics, the rules of how physical (or game) objects interact. In a sense the game physics define how the game’s simulation will unfold.
While game physics often correlate to the physics of the world we inhabit they don’t have to! In fact, most game physics approaches at least simplify real-world physics models to allow for real-time processing, and some abandon real-world physics altogether.
When games do try to implement realistic physics, they typically start with rigid body dynamics, a simplification of real-world physics where objects are represented as a rigid body and a point mass. In games, the rigid body is essentially the collision shape we covered in chapter 4, and a point mass represents the object’s position as a vector, and the mass as a float.
In this chapter we’ll examine how to implement rigid body physics in games.
At some point in your K-12 education, you probably encountered the equations of linear motion, which describe motion in terms of time, i.e.:
$$ v = at + v_0 \tag{1} $$ $$ p = p_0 + v_ot + \frac{1}{2}at^2 \tag{2} $$ $$ p = p_0 + \frac{1}{2}(v+v_0)t \tag{3} $$ $$ v^2 = v_0^2 2a(r - r_0) \tag{4} $$ $$ p = p_0 + vt - \frac{1}{2}at^2 \tag{5} $$
These equations can be used to calculate motion in a video game setting as well, i.e. to calculate an updated position vector2 position
given velocity vector2 velocity
and acceleration vector2 acceleration
, we can take equation (5):
position += velocity * gameTime.ElapsedGameTime.TotalSeconds + 1/2 * acceleration * Math.Pow(gameTime.ElapsedGameTime.TotalSeconds);
This seems like a lot of calculations, and it is. If you’ve also taken calculus, you probably encountered the relationship between position, velocity, and acceleration.
position is where the object is located in the world velocity is the rate of change of the position acceleration is the rate of change of the velocity
If we represent the position as a function (6), then velocity is the derivative of that function (7), and the acceleration is the second derivative (8):
$$ s(t) \tag{6} $$ $$ v(t) = s'(t) \tag{7} $$ $$ a(t) = v'(t) \tag{8} $$
So, do we need to do calculus to perform game physics? Well, yes and no.
Calculus is based around the idea of looking at small sections of a function (it literally comes from the latin term for “small stone”). Differential calculus cuts a function curve into small pieces to see how it changes, and Integral calculus joins small pieces to see how much there is.
Now consider our timestep - 1/30th or 1/60th of a second. That’s already a pretty small piece. So if we want to know the current velocity, given our acceleration, we could just look at that small piece, i.e.:
velocity += acceleration * gameTime.elapsedGameTime.TotalSeconds;
Similarly, our position is:
position += velocity * gameTime.elapsedGameTime.TotalSeconds
That’s it. We can consider our acceleration as an instantaneous change (i.e. we apply a force). Remeber the definition of force?
$$ \overline{F} = m\overline{a} \tag{9} $$So to find our acceleration, we rearrange equation 9 to read:
$$ \overline{a} = \overline{F}/m $$Thus, our acceleration would be the force acting on the body, divided by the mass of the body. We could calculate this from real values, or try different numbers until we found ones that “felt right”.
If we have multiple forces acting on an object, we simply sum the individual accelerations to find the net acceleration at that instant, which is then applied to the velocity.
Games are a “soft” simulation, in that we are emulating, but often not duplicating behavior of objects the real world. This also brings into play hyperreality, a philosophical concept that deals with modern humans’ inability to distinguish perceptions of reality and simulated realities.
One great example is LucasArt’s Digital Molecular Matter developed for the Force Unleashed series. Early tests with the physics engine duplicated the breaking of objects based on the actual molecular physics involved. Playtesters responded that it “felt fake” as wood did not explode into splinters and glass did not shatter into clouds of thousands of pieces when broken… so the developers made the effects more unrealistically intense to satisfy player expectations.
Let’s look at a simple example, a lunar-lander style game. We have a lunar lander that has a downward-facing thruster that the player can fire with the spacebar, causing an acceleration of 10 pixels/second2. The lander is also moving laterally at a velocity of 100 pixels/second, and gravity is pulling downward at 5 pixels/second2. We could write an update function like:
// Initial velocity
Vector2 velocity = new Vector2(10, 0);
public void Update(GameTime gameTime)
{
float t = (float)gameTime.ElapsedGameTime.TotalSeconds;
if(keyboardState.IsKeyDown(Keys.Space))
{
// apply thruster acceleration upward
velocity += new Vector2(0, -40) * t;
}
// apply gravity downward
velocity += new Vector2(0,30) * t
// update position
position += velocity * t;
}
Note that we can avoid all those complicated calculations, because we are only looking at a small slice of time. If we wanted to look at a bigger span of time, i.e. where a bomb might fall, we have to take more into account.
Also, notice that for this game, we ignore any friction effects (after all, we’re in the vacuum of space). Game programmers often take advantage of any simplifications to these calculations we can.
There is a second set of equations that govern angular motion (rotation) you may have encountered, where $ \omega $ is angular velocity, $ \alpha $ the angular acceleration, and $ \theta $ the rotation of the body:
$$ \omega = \omega_0 + \alpha t \tag{1} $$ $$ \theta = \theta_0 + \omega_0 t + \frac{1}{2}\alpha t^2 \tag{2} $$ $$ \theta = \theta_0 + \frac{1}{2}(\omega_0 + \omega)t \tag{3} $$ $$ \omega^2 = \omega_0^2 + 2\alpha(\theta-\theta_0) \tag{4} $$ $$ \theta = \theta_0 + \omega t - \frac{1}{2}\alpha t^2 \tag{5} $$
These equations parallel those we saw for linear dynamics, and in fact, have the same derivative relationships between them:
$$ \theta(t) \tag{6} $$ $$ \omega(t) = \theta'(t) \tag{7} $$ $$ \alpha(t) = \omega'(t) \tag{8} $$
And, just like linear dynamics, we can utilize this relationship and our small timestep to sidestep complex calculations in our code. Thus, we can calculate the rotational change from the angular velocity (assuming float rotation
, angularVelocity
, and angularAcceleration
values expressed in radians):
rotation += angularVelocity * gameTime.elapsedGameTime.TotalSeconds;
And the change in angular velocity can be calculated from the angular acceleration:
angularAcceleration = angularVelocity * gameTime.elapsedGameTime.TotalSeconds;
Finally, angular acceleration can be imposed with an instantaneous force. However, this is slightly more complex than we saw with linear dynamics, as this force needs to be applied somewhere other than the center of mass. Doing so applies both rotational and linear motion to an object. This rotational aspect is referred to as torque, and is calculated by taking the cross product of the force’s point of application relative to the center of mass and the force vector. Thus:
$$ \tau = \overline{r} x \overline{F} $$Where $ tau $ is our torque, $ \overline{r} $ is the vector from the center of mass to where the force is applied, and $ \overline{F} $ is the force vector.
torque = force.X * r.Y - force.Y * r.X;
Torque is the force imposing angular acceleration, and can be applied directly to the velocity:
angularAcceleration += torque * gameTime.ElapsedGameTime.TotalSeconds;
And much like force and linear acceleration, multiple torques applied to an object are summed.
XNA does not define the cross product function in its Vector2
library, so we computed it manually above. If you would like to add it as a method in your projects, we can do so using an extension method, i.e. define in one of your game’s namespaces:
public static class Vector2Extensions {
/// <summary>
/// Computes the cross product of two Vector2 structs
/// </summary>
/// <param ref="a">The first vector</param>
/// <param ref="b">The second vector</param>
public static float CrossProduct(Vector2 a, Vector2 b)
{
return a.X * b.Y - a.Y * b.X;
}
/// <summary>
/// Computes the cross product of this vector with another
/// </summary>
/// <param ref="other">the other vector</param>
public static float Cross(this Vector2 a, Vector2 other)
{
return CrossProduct(a, other);
}
}
As long as this class is in scope (i.e. you have an appropriate using
statement), you should be able to invoke Cross()
on any Vector2
. So the code listing:
torque = force.X * r.Y - force.Y * r.X;
Could be rewritten as:
torque = force.Cross(r);
Let’s work through an example. Consider this diagram:
The red arrow is the force vector applied from the left rocket. The green arrow is the vector from the position the force is applied to the center of mass of the ship. Firing just this one rocket will impose a rotation on the ship in the direction of the white arrow. Firing both rockets will impose an opposite torque, cancelling both.
Let’s write the control code for this spaceship. We’ll define class fields for both linear and angular velocity and acceleration, as well as position, direction, and angle. We’ll also supply a constant for the amount of linear acceleration applied by our spaceship:
// Constants
float LINEAR_ACCELERATION = 10;
// Linear movement fields
Vector2 position;
Vector2 direction;
Vector2 velocity;
// Angular movement fields
float angle;
float angularVelocity;
Then we’ll use these in our update method:
public void Update(GameTime gameTime)
{
KeyBoardState keyboardState = Keyboard.GetState();
float t = (float)gameTime.ElapsedGameTime.TotalSeconds;
Vector2 acceleration = Vector2.Zero; // linear acceleration
float angularAcceleration = 0; // angular acceleration
// Determine if the left rocket is firing
if(keyboardState.IsKeyPressed(Keys.A))
{
// LABEL A: Apply linear force in the direction we face from left rocket
acceleration += direction * LINEAR_ACCELERATION * t;
// TODO 1: Calculate and apply torque from left rocket
}
// Determine if the right rocket is firing
if(keyboardState.IsKeyPressed(Keys.D))
{
// LABEL B: Apply linear force in the direction we face from right rocket
acceleration += direction * LINEAR_ACCELERATION * t;
// TODO 2: Calculate and apply torque from right rocket
}
// Update linear velocity and position
velocity += acceleration * t;
position += velocity * t;
// update angular velocity and rotation
angularVelocity += angularAcceleration * t;
angle += angularVelocity * t;
// LABEL C: Apply rotation to our direction vector
direction = Vector2.Transform(Vector2.UnitY, new Matrix.CreateRotationZ(theta));
}
There’s a lot going on in this method, so let’s break it down in pieces, starting with the code following the LABEL
comments:
When we apply force against a body not toward its center, part of that force becomes torque, but the rest of it is linear acceleration. Technically, we can calculate exactly what this is with trigonometry. But in this case, we don’t care - we just want the ship to accelerate in the direction it is facing. So we multiply the direction
vector by a constant representing the force divided by the mass of our spaceship (acceleration), and the number of seconds that force was applied.
Note that we consider our spaceship mass to be constant here. If we were really trying to be 100% accurate, the mass would change over time as we burn fuel. But that’s extra calculations, so unless you are going for realism, it’s simpler to provide a constant, a lá LINEAR_ACCELERATION
. Yet another example of simplifying physics for games.
We need to know the direction the ship is facing to apply our linear acceleration. Thus, we need to convert our angle angle
into a Vector2
. We can do this with trigonometry:
direction.X = (float)Math.Cos(theta);
direction.Y = (float)Math.Sin(theta);
Or we can utilize matrix operations:
direction = Vector2.Transform(Vector2.UnitY, new Matrix.CreateRotationZ(theta));
We’ll discuss the math behind this second method soon. Either method will work.
Here we need to insert code for calculating the torque. First, we need to know the distance from the rocket engine to the center of mass of the ship. Let’s assume our ship sprite is
$ 152x115 $, and our center of mass is at
$ <76,50> $. Thus, our r
vector would be:
Vector2 r = new Vector2(76,50);
The second value we need is our force. We could factor in our ship rotation at this point, but it is easier if we instead rotate our coordinate system and place its origin at the center of mass for the ship, i.e.:
Then our force vector is simply a vector in the upward direction, whose magnitude is the amount of the force tangential to the r
vector. For simplicity, we’ll use a literal constant instead of calculating this:
Vector2 force = new Vector2(0, FORCE_MAGNITUDE);
Then we can calculate the torque with the cross product of these two vectors:
float torque = force.X * r.Y - force.Y * r.X;
And then calculate the rotational acceleration:
float angularAcceleration += torque / ROTATIONAL_INERTIA;
The ROTATIONAL_INERTIA
represents the resistance of the body to rotation (basically, it plays the same role as mass in linear dynamics). For simplicity, we could treat it as
$ 1 $ (no real effect - after all, we’re in a vacuum), which allows us to refactor as:
float angularAcceleration += torque;
Now that we’ve seen the longhand way of doing things “properly”, let’s apply some game-programming savvy shortcuts to our right-engine calculations.
Note that for this example, the vector r
does not change - the engine should always be the same distance from the center of mass, and the force vector of the engine will always be in the same direction. relative to r
. So the cross product of the two will always be the same. So we could pre-calculate this value, and apply the proper moment of inertia, leaving us with a single constant representing the angular acceleration from the engines which we can represent as a constant, ANGULAR_ACCELERATION
. Since this is reversed for the right engine, our simplified acceleration calculation would be:
angularAcceleration -= ANGULAR_ACCELERATION * t;
(and the right engine would have been angularAcceleration += ANGULAR_ACCELERATION * t;
)
Thus, with careful thought we can simplify six addition operations, four multiplications, one subtraction, one multiplication, and two struct allocation operations to just two multiplications and two additions. This kind of simplification and optimization is common in game programming. And, in fact, after calculating the ANGULAR_ACCELERATION
value we would probably tweak it until the movement felt natural and fun (or just guess at the value to begin with)!
Of course, if we were doing scientific simulation, we would instead have to carry out all of the calculations, couldn’t use fudge factors, and would have additional considerations. For example, if the fuel tanks are not at the center of mass, then every time we fire our rockets the center of mass will shift! That is why scientific simulations are considered hard simulations. Not so much because they are hard to write - but because accuracy is so important.
Now that we’ve looked at movement derived from both linear and angular dynamics, let’s revisit them from the perspective of collisions. If we have two rigid bodies that collide, what should be the outcome? Consider an elastic collision (one in which the two objects “bounce off” one another). From Newtonian mechanics we know that:
Thus, if we consider our two objects in isolation (as a system of two), the total system must have the same energy and momentum after the collision that it had before (Note we are talking about perfectly elastic collisions here - in the real world some energy would be converted to heat and sound).
Momentum is the product of mass and velocity of an object:
$$ \rho = mv\tag{0} $$Since we have two objects, the total momentum in the system is the sum of those:
$$ \rho = m_1v_1 + m_2v_2\tag{1} $$And, due to the law of conservation of momentum,
$$ \rho_{before} = \rho_{after} \tag{2} $$So, substituting equation 1 before and after the collision we find:
$$ m_1u_1 + m_2u_2 = m_1v_1 + m_2v_2\tag{3} $$Where $ u $ is the velocity before a collision, and $ v $ is the velocity after (note that the mass of each object does not change).
And since our objects are both moving, they also have kinetic energy:
$$ E_k = \frac{1}{2}mv^2\tag{4} $$As energy is conserved, the energy before the collision and after must likewise be conserved:
$$ E_{before} = E_{after} \tag{5} $$Substituting equation 4 into 5 yields:
$$ \frac{1}{2}m_0u_0^2 + \frac{1}{2}m_1u_1^2 = \frac{1}{2}m_0v_0^2 + \frac{1}{2}m_1v_1^2 \tag{6} $$Assuming we enter the collision knowing the values of $ u_0, u_1, m_0, and m_1 $, we have two unknowns $ v_0 $ and $ v_1 $ and two equations containing them (equations 3 and 6). Thus, we can solve for $ v_0 $ and $ v_1 $:
$$ v_0 = \frac{m_0 - m_1}{m_0 + m_1}u_0 + \frac{2m_1}{m_0+m_1}u_1 \tag{7} $$ $$ v_1 = \frac{2m_0}{m_0+m_1}u_0 + \frac{m_1 - m_0}{m_0 + m_1}u_1 \tag{8} $$
These two equations can give us the new velocities in a single dimension. But we’re primarily interested in two dimensions, and our velocities are expressed as Vector2
objects. However, there is a simple solution; use a coordinate system that aligns with the axis of collision, i.e. for two masses colliding, A and B:
Note how the X-axis in the diagram is aligned with the line between the centers of mass A and B. We can accomplish this in code by calculating the vector between the center of the two bodies, and determining the angle between that vector and the x-Axis:
Remember that the angle between two vectors is related to the dot product:
$$ cos\theta = \frac{ a \cdotp b}{||a||*||b||} \tag {9} $$If both vectors $ a $ and $ b $ are of unit length (normalized), then this simplifies to:
$$ cos\theta = a \cdotp b \tag {10} $$And $ \theta $ can be solved for by:
$$ \theta = cos^{-1}(a \cdotp b) \tag{11} $$Given two rigid bodies A
and B
, we could then calculate this angle using their centers:
// Determine the line between centers of colliding objects A and B
Vector2 collisionLine = A.Center - B.Center;
// Normalize that line
collisionLine.Normalize();
// Determine the angle between the collision line and the x-axis
float angle = Math.Acos(Vector2.Dot(collisionLine, Vector2.UnitX));
Once we have this angle, we can rotate our velocity vectors using it, so that the coordinate system now aligns with that axis:
Vector2 u0 = Vector2.Transform(Matrix.CreateRotationZ(A.Velocity, angle));
Vector2 u1 = Vector2.Transform(Matrix.CreateRotationZ(B.Velocity, angle));
We can use these values, along with the masses of the two objects, to calculate the changes in the X-component of the velocity using equations (7) and (8):
float m0 = A.Mass;
float m1 = B.Mass;
Vector2 v0;
v0.X = ((m0 - m1) / (m0 + m1)) * u0.X + ((2 * m1) / (m0 + m1)) * u1.X;
Vector2 v1;
v1.X = ((2 * m0) / (m0 + m1)) * u0.X + ((m1 - m0) / (m0 + m1)) * u1.X;
And, because the collision axis and the x-axis are the same, this transfer of velocity only occurs along the x-axis. The y components stay the same!
v0.Y = u0.Y;
v1.Y = u0.Y;
Then, we can rotate the velocities back to the original coordinate system and assign the transformed velocities to our bodies:
A.Velocity = Vector2.Transform(v0, Matrix.CreateRotationZ(-angle));
B.Velocity = Vector2.Transform(v1, Matrix.CreateRotationZ(-angle));
Some notes on this process:
at this point we are not accounting for when the collision has gone some way into the bodies; it is possible that after velocity is transferred one will be “stuck” inside the other. A common approach to avoid this is to move the two apart based on how much overlap there is. A more accurate and costly approach is to calculate the time from the moment of impact until end of frame, move the bodies with the inverse of their original velocities multiplied by this delta t, and then apply the new velocities for the same delta t.
this approach only works on two bodies at a time. If you have three or more colliding, the common approach is to solve for each pair in the collision multiple times until they are not colliding. This is not accurate, but gives a reasonable approximation in many cases.
If we want to incorporate more robust and realistic physics than that we have just explored, we would be well-served to look at physics engines designed for use in games. These are simulations built along the same lines as what we’ve discussed - essentially they represent the objects in the game world as rigid bodies, and provide the means for simulating them somewhat realistically.
One of the best-known of the physics engines developed for XNA was the Farseer Physics Engine, which was renamed to Velcro Physics when it was moved from CodePlex to GitHub.