walk me through calculating movement vector (when pressing forward/backward/left/right) while taking into account camera rotation (y coordinate Euler). Explain the trigonometry part from the very basics

Basics of 2‑D Trigonometry

Concept Meaning
Angle (θ) Measured from the positive X‑axis, counter‑clockwise.
Cosine cos θ = adjacent side / hypotenuse → gives the X component of a unit vector pointing at angle θ.
Sine sin θ = opposite side / hypotenuse → gives the Y (or Z in 3‑D) component of that unit vector.
Unit vector A vector of length 1. For angle θ it is (cos θ, sin θ).

In a typical first‑person controller the camera rotates around the Y‑axis (yaw).
That yaw angle, call it yaw, tells us which direction “forward” points on the horizontal plane.


Converting Camera Yaw to a Forward Vector

forward = ( cos(yaw), 0, sin(yaw) )
right   = ( -sin(yaw), 0, cos(yaw) )
  • forward points where the camera looks.
  • right is 90° clockwise from forward (cross product of up (0,1,0) and forward).

Both vectors are already normalized (length 1) because cos² + sin² = 1.


Combining Input Axes

Assume you receive two input values each frame:

Input Range Meaning
moveZ -1 … 1 Forward (+1) / Backward (‑1)
moveX -1 … 1 Right (+1) / Left (‑1)

The raw movement direction (still in world space) is:

[
\mathbf{d} = moveZ \cdot \text{forward} + moveX \cdot \text{right}
]

Expanding with the formulas above:

[
\begin{aligned}
d_x &= moveZ \cdot \cos(yaw) \;+\; moveX \cdot (-\sin(yaw)) \d_y &= 0 \d_z &= moveZ \cdot \sin(yaw) \;+\; moveX \cdot \cos(yaw)
\end{aligned}
]


Normalising the Result (Optional)

If both axes are pressed (moveZ = moveX = 1), the vector length becomes √2, giving a faster diagonal speed.
To keep a constant speed, normalise when the length exceeds 1:

1
2
3
4
length = sqrt(d_x**2 + d_z**2)
if length > 1:
    d_x /= length
    d_z /= length

Putting It All Together (Pseudo‑code)

# yaw is in radians; convert from degrees if needed
forward = (cos(yaw), 0, sin(yaw))
right   = (-sin(yaw), 0, cos(yaw))

# input axes
moveZ = get_axis("Vertical")   # forward/backward
moveX = get_axis("Horizontal") # right/left

# combine
dx = moveZ * forward[0] + moveX * right[0]
dz = moveZ * forward[2] + moveX * right[2]

# optional normalisation
len_sq = dx*dx + dz*dz
if len_sq > 1.0:
    inv_len = 1.0 / sqrt(len_sq)
    dx *= inv_len
    dz *= inv_len

# final movement vector (world space)
movement = (dx, 0, dz) * speed * deltaTime

The same math works in any engine (Unity, Unreal, Godot, etc.)—just ensure the yaw angle matches the engine’s coordinate system (right‑handed vs left‑handed).

Edit

Pub: 21 Jan 2026 06:32 UTC

Views: 1