Vectors

In game development, many physical and graphical objects require us to manipulate objects which have either a position or a direction in the game world. These objects are often best described by using a mathematical object called a vector.

A vector is a mathematical (or geometric) object that has a magnitude and direction. There are many ways to represent a vector, but the most common one is the Cartesian vector. In 2D, this vector has two components, xx and y y, and these components represent how far along is a vector displaced in the xx and y y directions. If you recall in the previous chapters, we used this system to place the rectangle in the scene but also to move it around.

A representation of three vectors and their components. The first number in each vector is the xx coordinate, the second is the yy coordinate. Note that in computer graphics the yy coordinate points downward.

Vectors have many useful properties that make them essential in game programming. Vectors can be added, and this is done by adding the respective components of each vector. For example, the sum of the vectors (2, 3) ( 2 , 3 ) and (2, -1) ( 2 , -1 ) is (2 + 2, 3 + (-1)) ( 2 + 2 , 3 + ( -1 ) ) or (4, 2) ( 4 , 2 ) . Graphically, this is represented by the second vector pointing away from the tip of the first vector.

The sum of two vectors. The resulting vector's xx component is the sum of the xx components of the initial vectors, similarly the yy component is the sum of the yy components of the initial vectors.

Similarly, vectors can be subtracted. When subtracting two vectors, the result is equal to the first vector's components minus the second vector's components. For example, when we subtract the vector (1, 3) ( 1 , 3 ) from the vector (4, 5) ( 4 , 5 ) , the result is (4 - 1, 5 - 3) ( 4 - 1 , 5 - 3 ) or (3, 2) ( 3 , 2 ) .

Vectors can also be scaled, which means we can multiply a vector with a number. When scaling a vector, both the xx and yy components are multiplied by the number, for example the vector (2, 1) ( 2 , 1 ) scaled with the value 3 3 will result in the vector (3 * 2, 3 * 1) ( 3 × 2 , 3 × 1 ) or (6, 3) ( 6 , 3 ) . Vectors can be scaled with both positive and negative numbers, scaling a vector with a positive number does not change it's direction, while scaling a vector with a negative number will make it point in the opposite direction of the original vector. For example, scaling the vector (2, 1) ( 2 , 1 ) with the value -1 -1 will result in the vector (-2, -1) ( -2 , -1 ) .

When a vector is scaled with a positive number it will still point in the same direction as the original vector. When a vector is scaled with a negative number, it will point in the opposite direction of the original vector.

The magnitude of a vector represents its length. This can be calculated by forming a triangle from the coordinates of the vector and using the Pythagorean theorem. Given the vector (x, y) ( x , y ) , the magnitude is equal to sqrt(x*x + y*y) ( x 2 + y 2 ) . For example, the magnitude of the vector (3, 4) ( 3 , 4 ) is equal to sqrt(3*3 + 4*4) ( 3 2 + 4 2 ) or sqrt(9 + 16) 9 + 16 or sqrt(25)25 which equals 55.

The mangitude of a vector can be calculated using the Pythagorean theorem.

Vectors which have a length of 11 are called unit vectors, and turning an ordinary vector into a unit vector is called normalization. To normalize a vector, we divide the vector by its own magnitude. For example, the vector (4, 3) (4,3) has a magnitude of 55, and after normalization this vector has the value (4/5, 3/5) ( 45,35 ) or (0.8, 0.6) (0.8,0.6) .

Finally, one of the most important operations we can do with vectors is called the dot product. The dot product is used in many cases, such as when projecting one vector on another, calculating the angle between two vectors, or in general when comparing how "similar" two vectors are. The dot product of two vectors is calculated by multiplying their respective components and summing the products. For example, the dot product of the vectors v1 = (x1, y1) v 1 = ( x 1 , y 1 ) and v2 = (x2, y2) v 2 = ( x 2 , y 2 ) is x1 * x2 + y1 * y2 x 1 x 2 + y 1 y 2 . If we divide this value by the magnitudes of both vectors, we will get the cosine of the angle between the vectors.

cos(theta) = (v1 dot v2)/(mag(v1) * mag(v2)) cos θ = v 1 v 2 | v 1 | | v 2 |

Now that we've shown what vectors can do, let's write a class called Vec2 which will allow us to create and manipulate vectors. Create a file called vec2.js and place it on the src directory, then add the following code to it:

export default class Vec2 {
    constructor(x, y) {
        this.x = x;
        this.y = y;
        Object.freeze(this);
    }

    add(otherVec) {
        return new Vec2(this.x + otherVec.x, this.y + otherVec.y);
    }

    subtract(otherVec) {
        return new Vec2(this.x - otherVec.x, this.y - otherVec.y);
    }

    scale(scalar) {
        return new Vec2(scalar * this.x, scalar * this.y);
    }

    magnitude() {
        return Math.sqrt((this.x * this.x) + (this.y * this.y));
        // return Math.sqrt(this.dot(this));    // equivalent to the line above
    }

    normalize() {
        let magnitude = this.magnitude();
        return new Vec2(this.x / magnitude, this.y / magnitude);
    }

    dot(otherVector) {
        return this.x * otherVector.x + this.y * otherVector.y;
    }

}

Note the Object.freeze(this) statement in line 5. By calling that statement we make our Vec2 class immutable, making it impossible to change the values of an instance after its been instantiated. Immutability is highly desirable since we can guarantee that no module of our game will accidentaly overwrite a vector used by another module. If we need to operate on our Vec2 instance, we'll have to create a new copy.

In the following chapters we'll make extensive use of this class when describing the position and motion of all game objects.

Recommended reading: Physics and motion

Next: Linear interpolation