Course page
Log in

You are watching a free preview. Enroll to open all 24 lessons, track progress and ask the course tutor.

Enroll for $39.99

Section 1: Vectors and directions

Vector operations you use every day

Article 13 min Lesson 2 of 24 Preview

Let us make an enemy move toward the player, and use that to learn the core vector operations.

Subtraction gives the offset

var to_player: Vector2 = player.position - enemy.position

The result points from the enemy to the player. Remember the order: target minus origin. Reversing it gives a vector pointing the wrong way, a very common bug.

Length is distance

var distance: float = to_player.length()

The length uses Pythagoras: the square root of x * x + y * y. When you only need to compare distances, for example "is the player within 200 pixels", compare squared lengths and skip the square root:

if to_player.length_squared() < 200.0 * 200.0:
	start_chasing()

Normalising gives a pure direction

A normalised vector, also called a unit vector, has a length of 1. It keeps the direction but loses the distance:

var direction: Vector2 = to_player.normalized()

Now multiply by a speed to get a velocity:

enemy.velocity = direction * 120.0

Without normalising, the enemy would move faster when the player is far away and slow down as it gets close, because the offset vector is longer at a distance.

Be careful when the vector has zero length, for example when enemy and player overlap. Normalising a zero vector cannot produce a direction. Godot returns a zero vector; other engines may return NaN values that spread through your game. Check the length first when this can happen.

Scaling and adding

Multiplying a vector by a number scales its length. Adding vectors combines movements, such as walking plus a conveyor belt:

var final_velocity := input_velocity + belt_velocity

Diagonal movement

A classic bug: reading input as Vector2(right - left, down - up) gives (1, 1) when two keys are held, which has a length of about 1.41. Players move 41 percent faster diagonally. The fix is to normalise or clamp the input vector:

var input := Vector2(Input.get_axis("left", "right"), Input.get_axis("up", "down"))
input = input.limit_length(1.0)

limit_length keeps analog stick values below 1 intact, which normalising would not.

In Unity

The same operations in C#: target - origin, .magnitude, .sqrMagnitude, .normalized and Vector3.ClampMagnitude. The ideas are identical; only the names change.

← → switch lessons