2024-11-22
Vectors
Vectors are the most crucial part of nearly any game, they are used for a lot of things. Some examples:
- Location of the player
- Rays (they are basically a position and direction)
- Physics (for example the velocity of a bullet)
To define vectors more thoroughly we first have to define scalars which are just ordinary numbers like 1.0
. These scalars have a magnitude but no direction. In contrast, vectors have both a magnitude and a direction. A vector is nothing more than a set of these scalars, for example (1.0, 2.0)
is a 2D vector with the x
component set to 1.0
and y
set to 2.0
.
Vectors come in many dimensions, in games we commonly use 2D, 3D and 4D variants. We’re going to look at a code example for a 3D vector, as all of the operations we are interested in are defined for the third dimension. We will explain all the vector operations in detail, for now try to follow along with the comments. Pick your favorite programming language below to follow along with the code:
For both programming language we’re using something called operator overloading. This simply means that we’re implementing what should happen when we call an operator for the class. For example in Python we use _add_
to define what happens when we use the +
operator on a vector. In C++ the same thing can be done by defining the operator+
function.
The first couple of operator overloads we wrote are the simple operations:
- Add
- Subtract
- Negate
In the following example we make one vector $a$ and one vector $b$. We can then use these vectors to create a new vector $a+b$. By using $-b$ we can also create $a-b$. In the following example we will draw out all these vectors to form two triangles. Tweak the values of $a$ and $b$ in the editor to get a different result!
Length
The length of a vector can be calculated using Pythagoras theorem. Since all of the components are their own axis, we can always form a right-angled triangle. To calculate the length we need to get all the components squared. In the code example above we’ve used the dot product as a tool for this. As we will see in the next section, the dot product returns the product of the components. So if we use the dot product of a vector with itself, we have the sum of all components squared. Finally taking the square root of the resulting value will leave us with the length.
Normalize
Normalizing is a very common operations on vectors. The idea is that we set the magnitude or length of the vector exactly to one. That will leave us with a vector that only has direction. We call this resulting vector a unit vector. We can easily get this unit vector by dividing every component by the length of the vector.
Dot product
The dot product is defined by adding the products of the individual components of the two vectors as follows:
$$a \cdot b = a_x b_x + a_y b_y + a_z b_z$$The resulting value will be a scalar. The most important property to know about the dot product is that we can use it to determine the angle between two vectors. We can use the following definition use the dot product to get an angle:
$$a \cdot b = |a| |b| cos \theta$$In this second formula we see that the dot product results in the magnitude of both vectors multiplied by the cosine of the angle (in radians). We can make this property really useful to just retrieve the angle if we have two unit vectors. In this case the magnitude of both vectors is equal to $1$ so we’re left with only the cosine of the angle. In the next example we’ll see this relationship illustrated by moving a vector.
Cross product
Unlike the dot product, the cross product returns a vector instead of a scalar. The resulting vector $v$ is perpendicular to both input vectors $a$ and $b$:
$$a \times b = (a_y b_z - a_z b_y\;, a_z b_x - a_x b_z\;, a_x b_y - a_y b_x)$$This property is why the cross product is used most often: to create a perpendicular vector. Similar to the dot product, the magnitude of the resulting vector is scaled according to the length of the input vectors:
$$|a \times b| = |a| |b| sin \theta$$There are a couple of important edge cases to account for: when $a$ and $b$ are completely parallel or inverse, the resulting vector will be (0, 0, 0)
.
That’s most of the practical stuff you should know about vector math that you need regularly in games. Below is an example of the 2D vector library being used in C++ for all the editors above:
1#include <math.h>
2
3struct Float2
4{
5 Float2(float x, float y) : x(x), y(y) {}
6 Float2(float a) : x(a), y(a) {}
7 Float2() : x(0), y(0) {}
8 float x, y;
9};
10
11inline Float2 operator-(const Float2 &a)
12{
13 return Float2(-a.x, -a.y);
14}
15inline Float2 operator-(const Float2 &a, const Float2 &b)
16{
17 return Float2(a.x - b.x, a.y - b.y);
18}
19inline Float2 operator-(const Float2 &a, const float b)
20{
21 return Float2(a.x - b, a.y - b);
22}
23inline Float2 operator-(const float a, const Float2 &b)
24{
25 return Float2(a - b.x, a - b.y);
26}
27inline Float2 operator+(const Float2 &a, const Float2 &b)
28{
29 return Float2(a.x + b.x, a.y + b.y);
30}
31inline Float2 operator+(const Float2 &a, const float b)
32{
33 return Float2(a.x + b, a.y + b);
34}
35inline Float2 operator+(const float a, const Float2 &b)
36{
37 return Float2(a + b.x, a + b.y);
38}
39inline Float2 operator*(const Float2 &a, const Float2 &b)
40{
41 return Float2(a.x * b.x, a.y * b.y);
42}
43inline Float2 operator*(const Float2 &a, const float b)
44{
45 return Float2(a.x * b, a.y * b);
46}
47inline Float2 operator*(const float a, const Float2 &b)
48{
49 return Float2(a * b.x, a * b.y);
50}
51inline Float2 operator/(const Float2 &a, const Float2 &b)
52{
53 return Float2(a.x / b.x, a.y / b.y);
54}
55inline Float2 operator/(const Float2 &a, const float b)
56{
57 return Float2(a.x / b, a.y / b);
58}
59inline Float2 operator/(const float a, const Float2 &b)
60{
61 return Float2(a / b.x, a / b.y);
62}
63inline void operator-=(Float2 &a, Float2 b)
64{
65 a.x -= b.x;
66 a.y -= b.y;
67}
68inline void operator+=(Float2 &a, Float2 b)
69{
70 a.x += b.x;
71 a.y += b.y;
72}
73inline void operator*=(Float2 &a, Float2 b)
74{
75 a.x *= b.x;
76 a.y *= b.y;
77}
78inline void operator/=(Float2 &a, Float2 b)
79{
80 a.x /= b.x;
81 a.y /= b.y;
82}
83inline float Dot(const Float2 &a, const Float2 &b)
84{
85 return a.x * b.x + a.y * b.y;
86}
87inline float Length(const Float2 &a)
88{
89 return sqrtf(Dot(a, a));
90}
91inline Float2 Normalize(const Float2 &a)
92{
93 float len = 1.0f / Length(a);
94 return Float2(a.x * len, a.y * len);
95}