Rotating in 2-D
Every spinning thing in a game — a turret tracking the player, a coin twirling on a table, a
twin-stick ship facing its aim — is, at heart, the same little piece of trigonometry. Before
we touch three dimensions, quaternions, or any of the exotic machinery, we build the one
matrix that turns the plane about the origin by an angle \theta.
Everything else in this chapter grows from it.
We already met this matrix in pure form on
rotation matrices;
here we re-derive it from scratch, with a game designer leaning over our shoulder.
Where do the basis vectors land?
A linear map is completely pinned down by what it does to the two basis vectors
\hat{\imath} = (1,0) and \hat{\jmath} = (0,1):
once you know their images, every other vector is just a combination of them. So rotating the
plane reduces to one question — where do these two arrows go?
Step 1 — rotate \hat{\imath} = (1,0). It starts on
the positive x-axis with length 1.
Swing it anticlockwise by \theta and its tip rides the unit circle
to the point at angle \theta:
\hat{\imath} \;\longmapsto\; (\cos\theta,\; \sin\theta).
Step 2 — rotate \hat{\jmath} = (0,1). It starts
pointing straight up, at angle 90^\circ. Adding
\theta puts it at angle 90^\circ + \theta,
and \cos(90^\circ + \theta) = -\sin\theta,
\sin(90^\circ + \theta) = \cos\theta:
\hat{\jmath} \;\longmapsto\; (-\sin\theta,\; \cos\theta).
Step 3 — stack the images as columns. The columns of a transformation matrix
are the images of the basis vectors. Drop them in, in order:
R(\theta) = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix}.
That is the whole 2-D rotation matrix, conjured by asking two arrows where they were going.
Apply it to a point
Step 4 — multiply R(\theta) by a point
(x, y). A matrix times a vector is just a weighted sum of
the columns:
R(\theta)\begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix}\begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} x\cos\theta - y\sin\theta \\ x\sin\theta + y\cos\theta \end{bmatrix}.
Step 5 — sanity-check with a quarter turn. Set
\theta = 90^\circ, so \cos\theta = 0 and
\sin\theta = 1. The point (x, y) maps to
(-y, x) — the player at (3, 0) swings up
to (0, 3), exactly the anticlockwise quarter-turn you'd expect.
Two rotations add their angles
Rotate by \beta, then by \alpha. The
combined motion ought to be a single rotation by \alpha + \beta —
and the matrices say so. Multiply R(\alpha)R(\beta) out.
Step 6 — multiply the two matrices.
R(\alpha)R(\beta) = \begin{bmatrix} \cos\alpha & -\sin\alpha \\ \sin\alpha & \cos\alpha \end{bmatrix}\begin{bmatrix} \cos\beta & -\sin\beta \\ \sin\beta & \cos\beta \end{bmatrix}.
Step 7 — expand each entry (row times column):
= \begin{bmatrix} \cos\alpha\cos\beta - \sin\alpha\sin\beta & -(\cos\alpha\sin\beta + \sin\alpha\cos\beta) \\ \sin\alpha\cos\beta + \cos\alpha\sin\beta & \cos\alpha\cos\beta - \sin\alpha\sin\beta \end{bmatrix}.
Step 8 — recognise the angle-sum identities. Each entry is exactly a
\cos or \sin of
\alpha + \beta:
R(\alpha)R(\beta) = \begin{bmatrix} \cos(\alpha+\beta) & -\sin(\alpha+\beta) \\ \sin(\alpha+\beta) & \cos(\alpha+\beta) \end{bmatrix} = R(\alpha + \beta).
Composing rotations adds their angles — and since addition commutes, in the plane the order
never matters (R(\alpha)R(\beta) = R(\beta)R(\alpha)). Enjoy that
while it lasts; in three dimensions it is the first thing to break.
The anticlockwise rotation of the plane about the origin by an angle
\theta is the linear map with matrix
-
R(\theta) = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix};
-
its columns are the images of the basis vectors:
\hat{\imath} \mapsto (\cos\theta, \sin\theta) and
\hat{\jmath} \mapsto (-\sin\theta, \cos\theta);
-
rotations add:
R(\alpha)R(\beta) = R(\alpha + \beta), so the inverse is
R(\theta)^{-1} = R(-\theta);
-
it is orthogonal with
\det R(\theta) = \cos^2\theta + \sin^2\theta = 1 — lengths,
angles and areas are all preserved (a rigid spin, no warping).
R(\theta) spins about the origin, but a game rarely wants that —
a door swings about its hinge, a planet orbits its sun, a sprite spins about its own centre
\mathbf{p}, not about (0,0). The fix is
the classic translate, rotate, translate back sandwich. To rotate a point
\mathbf{v} about the pivot \mathbf{p}:
\mathbf{v}' = R(\theta)\,(\mathbf{v} - \mathbf{p}) + \mathbf{p}.
Subtracting \mathbf{p} slides the pivot to the origin, where
R(\theta) knows how to work; adding it back afterwards puts the
world where it belongs. This bracketing trick — move the problem to where the maths is easy,
solve it, move it back — returns again and again, and it is exactly how
composing transformations
chains pivots, scales and spins together.
Turn the dial
The blue arrow is a fixed point P = (x, y). The orange arrow is its
image R(\theta)\,P. Drag the angle slider: the point rides a circle
of fixed radius about the origin, its length never changing, while the live matrix and
rotated coordinates update beneath it. At \theta = 90^\circ watch
(x, y) jump to (-y, x).