If you learn one operation in game math, learn the
dot product.
It is a single multiply-and-add that secretly measures the angle between two
directions, and that one number answers a startling range of in-game questions: is the enemy
in front of me? how long is this vector's shadow on that one? how bright is this surface? Its
whole power flows from one identity,
\vec{a} \cdot \vec{b} = \lVert \vec{a}\rVert\,\lVert \vec{b}\rVert\,\cos\theta,
where \theta is the angle between
\vec{a} and \vec{b}
(see dot product and angle).
Because \cos rides along, the dot product is really an
angle-meter in disguise. Here are its three workhorse jobs.
Three jobs, line by line
Step 1 — (i) Facing: the sign tells you in front or behind. The lengths in
\lVert\vec{a}\rVert\lVert\vec{b}\rVert are always positive, so the
sign of \vec{a}\cdot\vec{b} is exactly the sign of
\cos\theta:
\operatorname{sign}(\vec{a}\cdot\vec{b}) = \operatorname{sign}(\cos\theta) = \begin{cases} +\ & \theta < 90^\circ \ \ (\vec{b} \text{ is in front of } \vec{a}) \\ 0\ & \theta = 90^\circ \ \ (\text{exactly beside}) \\ -\ & \theta > 90^\circ \ \ (\vec{b} \text{ is behind } \vec{a}). \end{cases}
Point \vec{a} the way the guard faces and let
\vec{b} be the direction to the player: one dot product, read its
sign, and you know whether the player is ahead, beside, or behind.
Step 2 — (ii) Projection: the shadow length. Take
\vec{b} and a unit direction
\hat{a} (so \lVert\hat{a}\rVert = 1).
Then the identity collapses to the length of \vec{b}'s shadow
cast along \hat{a} — its
projection:
\vec{b}\cdot\hat{a} = \lVert\vec{b}\rVert\cos\theta = (\text{how far } \vec{b} \text{ reaches in the } \hat{a} \text{ direction}).
This is "how much of my speed is forward?", "how far up the ramp did I climb?", "what's my
component toward the goal?" — all one dot with a unit vector.
Step 3 — (iii) Lighting: brightness is a cosine. A matte (diffuse) surface
with unit normal \hat{N} lit from unit direction
\hat{L} is brightest face-on and dims as it tilts away — exactly
\cos\theta. Clamp the back side to dark (no negative light):
\text{brightness} = \max\!\big(0,\; \hat{N}\cdot\hat{L}\big).
This single line — Lambert's cosine law — runs for every lit pixel in the
frame. Face the light: \hat{N}\cdot\hat{L} = 1, full bright. Turn
edge-on: 0. Turn away: clamped to 0,
dark.
For vectors \vec{a}, \vec{b} at angle
\theta,
\vec{a}\cdot\vec{b} = \lVert\vec{a}\rVert\lVert\vec{b}\rVert\cos\theta,
and three jobs follow:
-
Facing — the sign of \vec{a}\cdot\vec{b}
tells you whether \vec{b} is in front of
(>0), behind (<0), or beside
(=0) \vec{a}.
-
Projection — with \hat{a} a unit vector,
\vec{b}\cdot\hat{a} = \lVert\vec{b}\rVert\cos\theta is the
length of \vec{b}'s shadow along
\hat{a}.
-
Diffuse lighting — a surface's brightness is
\max(0,\ \hat{N}\cdot\hat{L}), the (clamped) cosine of the
angle to the light.
"Can the guard see the player?" is a dot product. Let
\hat{f} be the unit direction the guard faces and
\hat{d} the unit direction to the player. The player is inside a
vision cone of total angle \text{fov} exactly when the angle
between them is at most half the cone — i.e. when the cosine is large enough:
\hat{f}\cdot\hat{d} \;>\; \cos\!\Big(\frac{\text{fov}}{2}\Big).
One dot, one precomputed constant \cos(\text{fov}/2), one
comparison — no inverse cosine, no square roots, no branches. That is why stealth games can
afford to ask it for hundreds of enemies every single frame. Narrow the cone and the
threshold rises toward 1; widen it toward
180^\circ and the threshold falls toward
0, "anything not behind me".