Game Math
What, you didn’t think games did math?

What, you didn’t think games did math?
Math plays a very large role in computer games, as math is used both in the process of simulating game worlds (i.e. to calculate game physics) and in the rendering of the same (i.e. to represent and rasterize 3D objects). Algebra and trigonometry play a vital role in nearly every game’s logic. However, vectors, matricies, and quaternions play an especially important role in hardware-accelerated 3D rendering.
Computer games almost universally take place in a simulated 2D or 3D world. We use coordinate systems to represent the position of various objects within these worlds. Perhaps the simplest coordinate system is one you encountered in elementary school - the number line:
The number line represents a 1-dimensional coordinate system - its coordinates are a single value that range from $ -\infty $ to $ \infty $. We normally express this as a single number. Adding a second number line running perpendicular to the first gives us a 2-dimensional coordinate system. The coordinate system you are probably most familiar with is the planar Cartesian Coordinate System:
While 2D games sometimes use this coordinate system, most instead adopt the screen coordinate system. It labels its axes X and Y as does the Cartesian Coordinate System, but the Y-axis increases in the downwards direction. This arrangement derives from the analog video signals sent to Cathode Ray Tube computer monitors (which were adapted from the earlier television technology), and this legacy has lived on in modern computer monitors.
Points in both coordinate systems are represented by two values, an x-coordinate (distance along the x-axis), and a y-coordinate (distance along the y-axis). These are usually expressed as a tuple:
$ (x, y) $. MonoGame provides the Point
struct to represent this, however we more commonly use a Vector2
as it can embody the same information but has additional, desirable mathematical properties.
For three dimensions, we add a third axis, the z-axis, perpendicular to both the x-axis and y-axis. In Cartesian coordinates, the z-axis is typically drawn as being vertical. However, the two most common 3D rendering hardware libraries (DirectX and OpenGL), have the x-axis horizontal, the y-axis vertical, and the z-axis coming out of or into the screen. These two approaches are often referred to as left-hand or right-hand coordinate systems, as when you curl the fingers of your right hand from the x-axis to the y-axis, your thumb will point in the direction of the z-axis:
DirectX adopted the left-hand coordinate system, while OpenGL adopted the right-hand system. There are some implications for this choice involved in matrix math, which we will discuss later, and for importing 3D models. A 3D model drawn for a left-hand system will have its z-coordinates reflected when drawn in a right-hand system. This can be reversed by scaling the model by $ -1 $ in the z-axis. When importing models through the content pipeline, this transformation can be specified so that the imported model is already reversed.
Coordinates in a 3D system can be represented as a tuple of three coordinates along each of the axes:
$ (x, y, z) $. However, video games universally represent coordinates in 3d space with vectors; MonoGame provides the Vector3
for this purpose.
Trigonometry - the math that deals with the side lengths and angles of triangles, plays an important role in many games. The trigonometric functions Sine, Cosine, and Tangent relate to the ratios of sides in a right triangle:
$$ \sin{A} = \frac{opposite}{hypotenuse} = \frac{a}{c} \tag{0} $$ $$ \cos{A} = \frac{adjacent}{hypotenuse} = \frac{b}{c} \tag{1} $$ $$ \tan{A} = \frac{opposite}{adjacent} = \frac{a}{b} \tag{2} $$You can use the System.MathF
library to compute
$ \sin $,
$ \cos $ using float
values:
MathF.Sin(float radians)
computes the
$ \sin $ of the supplied angleMathF.Cos(float radians)
computes the
$ \cos $ of the supplied angleMathF.Tan(float radians)
computes the
$ \tan $ of the supplied angleYou can inverse these operations (compute the angle whose $ \sin $, $ \cos $, or $ \tan $ matches what you supply) with:
MathF.Asin(float s)
computes the angle which produces the supplied
$ \sin $ valueMathF.Acos(float c)
computes the angle which produces the supplied
$ \cos $ valueMathF.Atan(float t)
computes the angle which produces the supplied
$ \tan $ valueMathF.Atan2(float x, float y)
computes the angle with produces the supplied x/y ratio. This form can be helpful to avoid a division by 0 error if y
is 0.These angles are measured in radians - fractions of $ \pi $. Positive angles rotate counter-clockwise and negative ones clockwise. It can be helpful to consider radians in relation to the unit circle - a circle with radius 1 centered on the origin:
The angle of
$ 0 $ radians falls along the x-axis. MonoGame provides some helpful float
constants for common measurements in radians:
MathHelper.TwoPi
represents
$ 2\pi $, a full rotation around the unit circle (
$ 360^{\circ} $).MathHelper.Pi
represents
$ \pi $, a half-rotation around the unit circle (
$ 180^{\circ} $).MathHelper.PiOver2
represents
$ \frac{\pi}{2} $, a quarter rotation around the unit circle (
$ 90^{\circ} $).MathHelper.PiOver4
represents
$ \frac{\pi}{4} $, an eighth rotation around the unit circle (
$ 45^{\circ} $).Inside the unit circle you can inscribe a right triangle with angle at the origin of $ \theta $. This triangle has a hypotenuse with length 1, so $ \sin{\theta} $ is the length of the opposite leg of the triangle, and $ \cos{\theta} $ is the length of the adjacent leg of the triangle. Of course $ \tan{\theta} $ will always equal $ 1 $.
Games almost universally use vectors to represent coordinates rather than points, as these have additional mathematical properties which can be very helpful. In mathematical notation, vectors are expressed similar to points, but use angle brackets, i.e.:
$
MonoGame provides structs to represent 2-, 3-, and 4- element vectors:
Vector2
- two-element vectors, often used to represent coordinates in 2d spaceVector3
- three-element vectors, often used to represent coordinates in 3d spaceVector4
- four-element vectors, used for affine transformations (more on this soon)The magnitude is the length of the vector. In XNA it can be computed using Vector2.Length()
or Vector3.Length()
, i.e.:
Vector2 a = new Vector2(10, 10);
float length = a.Length();
This is calculated using the distance formula:
$$ |\overline{v}| = \squareroot{(x_0 - x_1)^2 + (y_0 - y1)^2} \tag{0} $$ $$ |\overline{v}| = \squareroot{(x_0 - x_1)^2 + (y_0 - y1)^2 + (z_0 - z_1)^2} \tag{1} $$
In some instances, we may be able to compare the square of the distance, and avoid the computation of a square root. For these cases, the vector classes also define a LengthSquared()
method:
Vector2 a = new Vector2(10, 10);
float lengthSquared = a.LengthSquared();
In some cases, it can be useful to normalize (convert into a unit vector, i.e. one of length 1, preserving the side ratios) a vector. This can be done with Vector2.Normalize()
or Vector3.Normalize()
. When invoked on a vector object, it turns the current vector into its normalized form:
Vector2 a = new Vector2(10, 10);
a.Normalize();
// Now a is a normal vector <0.5, 0.5>
Alternatively, you can use the static version to return a new vector computed from the supplied one:
Vector2 a = new Vector2(10, 10);
b = Vector2.Normalize();
// Vector a is still <10, 10>
// Vector b is a normal vector <0.5, 0.5>
Mathematically, normalization is accomplished by