Math in Video Games

pixel art notebook
Game Development 101: (incomplete)
a. Basics of Game Development
b. Designing your Video Game
c. Math in Video Games
d. AI Decision-making & Pathfinding
e. Asset Creation & Game Art
f. Physics & Collision Detection
g. Shaders & VFX in Games
h. Multiplayer & Networking in Games

Reading

The purpose of this text is to familiarize you about the math in games; you should not stress on this too much, as you will eventually grasp the concepts as you progress into making stuff. Most of the things discussed here are wholly or partially well-implemented in game engines such as Unity, Godot or Unreal.

Game Math

Math lies in the depths of all computations. Video games are no exception. In games, the most common is the vector mathematics; we treat all objects as vectors in space & by manipulating vectors allows us to compute anything that can be represented as a vector.

In previous articles, you have noticed that when we moved an object, we just added a value to the position vector of the object & the object had moved to the new coordinate values.

2D & 3D Space

Position of any object on a 2D plain can be represented by how much it is away from origin in both x & y axes. In 3D, we need another axis to represent an object’s position. It is the z-axis. Thus a coordinate system established by x, y, z axes make the space; in which we can put our game objects. Everything that occurs in this space must be a vector.

What are Vectors?

Vectors are used to represent quantities that have some direction in space & some magnitude in that direction. Just like a scalar number is used to represent a quantity in 1D space, a vector is used to represent a quantity in 2D or 3D space.

A vector can be represented as:

vector A = direction A * magnitude A

If we simply need direction of a vector, we can divide the vector by its own magnitude. The resulting vector will have magnitude of 1. This is called a normalized vector. It is important in games since we often need the direction.

Vectors can be Added or Subtracted

Two vectors can be added. If we have two scalar values 5 & 10; and we wish to add them, we will get 15. Similarly if we have two vectors vec2(2, 3) and vec2(6, 5) and we wish to add them, we add the x & y components of these vectors; thus we will get vec2(2+6, 3+5) or vec2(8, 8). Vector subtraction is similar.

Vectors can be Multiplied

Vector multiplication with scalar is simple: it just scales the magnitude by some amount. However, between two vectors, multiplication can be any of the two things: the dot product or the cross product.

  1. Dot Product: Dot product between two vectors A & B returns the projection of A on B (a scalar value). Suppose A & B are perpendicular; their dot product will be 0 since A is not at all projecting on B. But if both vectors are parallel, their dot product will be maximum as if both vectors are multiplied like the standard scalar multiplication.
  2. Cross Product: Cross product is defined for 3D space. Cross product between two vectors A & B returns the area between the two vectors. The direction of the area is perpendicular to both the given vectors A & B. Thus if A(1, 0, 0) is cross multiplied with B(0, 0, 1); the result will be vec3(0, 1, 0). Cross product returns the vector unlike dot product which returns the scalar.

Interesting Uses in Games

Make the Enemy Chase the Player: Player’s position is represented by A & enemy’s own position is represented by B. If we subtract B from A, we will get a vector pointing from B to A. Now we will normalize this vector and add it to the enemy’s position to make it move towards the direction of this vector every frame.

void chase_player(player_position, enemy_position){
    vector_to_player = normalize(player_position - enemy_position)
    enemy_position = enemy_position + vector_to_player * speed
}

Checking if Player is in Enemy’s View: In many games, we often need to check if enemy is looking at player (thus enemy can shoot if player is in view). A simple math-based approach is to use dot product between normalized enemy’s local direction vector & normalized enemy_to_player vector.

bool is_looking_at(player_position) {
    vector_to_player = normalize(player_position - enemy_position);
    return dot(vector_to_player, -local_z_vector) > cos(field_of_view / 2.0);
}

The local_z_vector is the vector used to represent an object’s front direction (i.e. where the object is looking at or pointing to). In game engines such as Godot, it is already provided. But if you working from scratch, you’ll need some calculations to calculate it based on rotation of your object.

Making an Airplane Physics: For simple arcade-like airplane physics, we can calculate thrust, drag, lift & weight vectors & apply them to the center of gravity of our airplane in a game. The resulting simulation can be acceptable for simple airplane games.

Matrices & Vector Transformations

Matrix is a set of vectors. Its important property is; when multiplied by a vector, it can transform it to a new vector. A very simple example is the stretching of image by “stretching” its coordinates by multiplying it with a matrix.

Matrices can be seen as functions that transform vectors: f(V) = M x V

Where V is input vector, and f(V) is the output vector; M is the matrix.

Why We Use Matrix?

We typically use matrix transformations to switch between different spaces. For example, our screen space has origin at top-left of screen. Any vector in screen space is defined in terms of its displacement from screen’s origin. Suppose we hit a bullet at a particular point on the screen & we want to propagate the bullet in the game world; we would have to multiply the screen space vector to some matrix that would transform the screen space position to the world space position (thus, bullet’s position would then be defined in terms of world origin rather than screen origin).

The transformation matrix that converts coordinates from screen space to world space is commonly known as the “inverse projection matrix” or “view projection matrix.” This matrix is often used in computer graphics to transform 2D screen coordinates (in normalized device coordinates, NDC) back to 3D world coordinates.

There are many other such matrices, which are used to convert from model space to world space, world space to model space, and so on. But you will discover them later eventually.

Recall (obtaining local-z-vector; the front-pointing vector): Remember that we talked about local z vector above; to obtain the local z-axis vector in a game, you need to multiply the world-space z-axis vector by the inverse of the rotation matrix that represents the orientation of the object or camera. This operation transforms the global z-axis into the local coordinate system of the object.

Some Matrix Operations & Transformations

THIS SECTION IS UNFINISHED

Math Behind Basic Collision Detection

The basic approach is to check for intersections between two rectangles in case of 2D and between bounding boxes for 3D. However, it is an inferior approach. Most physics libraries (such as Bullet3D) do much more than that. However, I will not discuss details of collision detection here; but an example of 2D rectangle-based collision is sufficient for a beginner’s understanding.

// Define a struct for a Rectangle
struct Rect {
    float x;      // X-coordinate of the top-left corner
    float y;      // Y-coordinate of the top-left corner
    float width;  // Width of the rectangle
    float height; // Height of the rectangle
};

bool is_intersecting(rect1, rect2){
    // Check if rect1 is to the left of rect2
    if (rect1.x + rect1.width < rect2.x or rect2.x + rect2.width < rect1.x)
        return false;

    // Check if rect1 is above rect2
    if (rect1.y + rect1.height < rect2.y or rect2.y + rect2.height < rect1.y)
        return false;

    // If the above conditions are not met, the rectangles are intersecting
    return true;
}

Math Behind Shading & Lighting

3D game objects must interact with light. There are many shading models for this, but here I’ll discuss simple Lambert reflection model & Blinn-Phong model for specular highlights.

Lambertian Reflection: The Lambertian reflection model is a simple model for simulating diffuse reflection of light from a surface. It assumes that light is reflected equally in all directions, regardless of the viewer’s perspective. The Lambertian model doesn’t account for specular highlights.

vec3 lambertianReflection(vec3 lightDirection, vec3 surfaceNormal, vec3 lightColor, vec3 surfaceColor) {
    // Calculate the cosine of the angle between the light direction and surface normal
    float cosTheta = max(0.0, dot(lightDirection, surfaceNormal));

    // Ensure cosTheta is non-negative
    cosTheta = max(0.0, cosTheta);

    // Calculate Lambertian reflectance
    vec3 lambertianReflection = (lightColor / pi) * surfaceColor * cosTheta;

    return lambertianReflection;
}

Blinn-Phong Specular: Blinn-Phong model can be used for specular highlights, to add shininess.

vec3 blinnPhongSpecularReflection(vec3 lightDirection, vec3 surfaceNormal, vec3 viewDirection, vec3 lightColor, vec3 surfaceColor, float shininess) {
    // Calculate the halfway vector between view direction and light direction
    vec3 halfwayDir = normalize(viewDirection + lightDirection);

    // Calculate the cosine of the angle between the surface normal and the halfway vector
    float cosAlpha = max(0.0, dot(surfaceNormal, halfwayDir));

    // Calculate the specular reflectance using the Blinn-Phong model
    vec3 specularReflection = (lightColor / pi) * surfaceColor * pow(cosAlpha, shininess);

    return specularReflection;
}

Limits & Derivatives

Limits

In mathematics, a limit is a fundamental concept that describes the behavior of a function as its input approaches a certain value.

Derivatives

The derivative of a function represents the rate at which the function is changing at any given point. It measures the slope of the tangent line to the graph of the function at a specific point. In other words, it provides information about how the function behaves locally.

Mathematically, it is: d(x) = (f(x + h) – f(x)) / h

where x is the function input, h is a very small value whose value approaches 0. This makes f(x + h) slightly different from f(x), thus by calculating the difference between two values of a function per their difference h, we can find the slope of the function at that point (or the degree of how much this function is changing at that point).

Derivatives Uses in Games

This property is useful for calculating the slope of the terrain surface; which can later be used for reflecting light properly in correct direction & for simulating surface erosion by guiding the rain drops to the correct direction.

Some More Math

Representing a Straight Line

Equation of straight line: y = mx + b

where y is the y-axis value, x is x-axis value, m is the slope (or call it y/x ratio) and b is the offset or displacement from the origin.

We can draw straight lines by making a function that prints different colors for all pixels (x, y), for which the equation is true.

Polygons & Geometry

Geometry is represented in the form of polygons. Polygons are shapes composed of vertices/points in space and edges connecting those points.

Calculations for simple polygons (convex shape) are often simple; but for those that have cavities (concave shape) are difficult.

Math Functions Cheat Sheet

IDFormulaDescription
1abs(x)Absolute value of x
2sign(x)Sign of x (-1, 0, 1)
3floor(x)Largest integer not greater than x
4ceil(x)Smallest integer not less than x
5round(x)Nearest integer to x
6trunc(x)Integer part of x
7fract(x)Fractional part of x
8mod(x, y)Remainder of x/y
9min(x, y)Minimum of x and y
10max(x, y)Maximum of x and y
11clamp(x, minVal, maxVal)Clamps x to the range [minVal, maxVal]
12mix(x, y, a)Linear interpolation between x and y with factor a
13step(edge, x)0.0 if x < edge, 1.0 otherwise
14smoothstep(edge0, edge1, x)Hermite interpolation between 0 and 1 based on x
15length(vec)Length of the vector
16distance(vec1, vec2)Distance between two points
17normalize(vec)Normalize the vector
18dot(vec1, vec2)Dot product of two vectors
19cross(vec1, vec2)Cross product of two vectors
20reflect(I, N)Reflection of incident vector I across normal N
21refract(I, N, eta)Refraction of incident vector I across normal N with eta
22saturate(x)Clamps x to the range [0, 1]
23sin(x)Sine of x
24cos(x)Cosine of x
25tan(x)Tangent of x
26asin(x)Arcsine of x
27acos(x)Arccosine of x
28atan(x)Arctangent of x
29atan(y, x)Arctangent of y/x
30exp(x)Exponential function
31log(x)Natural logarithm of x
32exp2(x)2^x
33log2(x)Base-2 logarithm of x
34pow(x, y)x raised to the power y
35sqrt(x)Square root of x
36inversesqrt(x)Inverse square root of x
37abs(vec)Component-wise absolute value of a vector
38sign(vec)Component-wise sign of a vector
39floor(vec)Component-wise floor of a vector
40ceil(vec)Component-wise ceil of a vector
41round(vec)Component-wise round of a vector
42trunc(vec)Component-wise truncation of a vector
43fract(vec)Component-wise fractional part of a vector
44min(vec1, vec2)Component-wise minimum of two vectors
45max(vec1, vec2)Component-wise maximum of two vectors
46clamp(vec, minVec, maxVec)Component-wise clamp of a vector to a range
47mix(vec1, vec2, a)Component-wise linear interpolation between two vectors
48step(edge, vec)Component-wise step function
49smoothstep(edge0, edge1, vec)Component-wise smoothstep function
50length(mat)Frobenius norm of a matrix
51transpose(mat)Transpose of a matrix
52inverse(mat)Inverse of a matrix

More Reading

A very good book on game math: https://gamemath.com/book/intro.html

Game Development 101: (incomplete)
a. Basics of Game Development
b. Designing your Video Game
c. Math in Video Games
d. AI Decision-making & Pathfinding
e. Asset Creation & Game Art
f. Physics & Collision Detection
g. Shaders & VFX in Games
h. Multiplayer & Networking in Games