Skip to main content

Vectors

Excalibur provides three vector types to handle spatial math:

  • Vector: 2D vectors for positioning, velocities, and physics on a plane.
  • Vector3: 3D vectors commonly used for spatial transforms, lighting, and shader uniforms.
  • Vector4: 4D vectors primary used for homogeneous coordinates, WebGL shaders, and matrix transformations.

Excalibur uses the Vector structure to represent points. The Vector class has many different static methods available for doing vector math as well as instance methods to combine vectors together in different ways.

Creating Vectors

To quickly create vectors, use the global shorthand functions vec, vec3, or vec4:

ts
import { vec, vec3, vec4 } from 'excalibur'
 
const point2D = vec(0, 10)
const point3D = vec3(0, 10, 5)
const point4D = vec4(0, 10, 5, 1)
ts
import { vec, vec3, vec4 } from 'excalibur'
 
const point2D = vec(0, 10)
const point3D = vec3(0, 10, 5)
const point4D = vec4(0, 10, 5, 1)

Alternatively, you may see examples of using the more verbose new Vector(x, y) format:

ts
import { Vector, Vector3, Vector4 } from 'excalibur'
 
const point2D = new Vector(0, 10)
const point3D = new Vector3(0, 10, 5)
const point4D = new Vector4(0, 10, 5, 1)
ts
import { Vector, Vector3, Vector4 } from 'excalibur'
 
const point2D = new Vector(0, 10)
const point3D = new Vector3(0, 10, 5)
const point4D = new Vector4(0, 10, 5, 1)

To set the value of an existing vector, use Vector.setTo:

ts
import { vec, vec3, vec4 } from 'excalibur'
 
const point2D = vec(0, 10).setTo(10, 10)
const point3D = vec3(0, 10, 0).setTo(10, 10, 10)
const point4D = vec4(0, 0, 0, 0).setTo(10, 10, 10, 1)
ts
import { vec, vec3, vec4 } from 'excalibur'
 
const point2D = vec(0, 10).setTo(10, 10)
const point3D = vec3(0, 10, 0).setTo(10, 10, 10)
const point4D = vec4(0, 0, 0, 0).setTo(10, 10, 10, 1)

Each vector class provides built-in constants for common directional and utility vectors:

FeatureConstants Available
VectorZero, One, Half, Left, Right, Up, Down
Vector3Zero, One, Left, Right, Up, Down, Forward, Back
Vector4Zero, One

Cloning vectors

Vectors are objects, so mutating them will change the state for all references. Use the Vector.clone method to clone a vector to mutate it:

ts
import { vec } from 'excalibur';
 
const point = vec(0, 10);
point.setTo(8, 8);
(method) Vector.setTo(x: number, y: number): void
const samePoint = point;
const anotherPoint = point.clone();
anotherPoint.setTo(50, 50);
 
console.log(point.toString()); // "(8, 8)"
console.log(samePoint.toString()); // "(8, 8)"
console.log(anotherPoint.toString()); // "(50, 50)"
ts
import { vec } from 'excalibur';
 
const point = vec(0, 10);
point.setTo(8, 8);
(method) Vector.setTo(x: number, y: number): void
const samePoint = point;
const anotherPoint = point.clone();
anotherPoint.setTo(50, 50);
 
console.log(point.toString()); // "(8, 8)"
console.log(samePoint.toString()); // "(8, 8)"
console.log(anotherPoint.toString()); // "(50, 50)"
tip

Notice how both point and samePoint share the same vector reference, so using setTo mutates the vector. Use clone to ensure you are not changing vectors unexpectedly!

setTo returns void so that it "feels awkward" to chain it. This is intentional to avoid confusion about whether you are mutating the vector or creating a new one.

Linear interpolation (lerp) between vectors

All vector classes support lerp, which returns a new vector representing a point along the path between two vectors. Given a target vector and an interpolation factor t between 0 and 1, it produces the intermediate vector.

The standard formula for linear interpolation is:

lerp(a, b, t) = a + (b - a) * t
lerp(a, b, t) = a + (b - a) * t
  • a = starting vector
  • b = target vector
  • t = interpolation factor (0 ≤ t ≤ 1)

When t = 0, the result is a.

When t = 1, the result is b.

When t = 0.5, the result is exactly halfway between a and b.

ts
import { vec, vec3 } from 'excalibur'
 
const start2D = vec(0, 0)
const end2D = vec(100, 200)
const mid2D = start2D.lerp(end2D, 0.5) // vec(50, 100)
 
const start3D = vec3(0, 0, 0)
const end3D = vec3(10, 20, 30)
const mid3D = start3D.lerp(end3D, 0.5) // vec3(5, 10, 15)
ts
import { vec, vec3 } from 'excalibur'
 
const start2D = vec(0, 0)
const end2D = vec(100, 200)
const mid2D = start2D.lerp(end2D, 0.5) // vec(50, 100)
 
const start3D = vec3(0, 0, 0)
const end3D = vec3(10, 20, 30)
const mid3D = start3D.lerp(end3D, 0.5) // vec3(5, 10, 15)