Renderer · class
ThirdPersonCamera
Generic camera mechanism. The caller supplies its already-resolved target height and desired FOV, keeping crouch rules and speed effects in the game.
Explained in Hello world.
class ThirdPersonCameraimport { ThirdPersonCamera } from '@driftengine/core';Constructor
new
constructor(colliders: ColliderSet, options: Readonly<ThirdPersonCameraOptions>, surface?: GroundSurface | null, /** * A world query for the arm, instead of the collider set. * * Fourth and defaulted, so no existing call site changes — the same shape `surface` was added * in. When it is given, `colliders` is not consulted at all: a caller with one has the whole * world behind it and a set of boxes beside it would be a second, partial answer to the same * question. See `BoomObstruction` for why one consumer could not supply the set. */ obstruction?: BoomObstruction | null)| Parameter | Type | Description |
|---|---|---|
colliders | ColliderSet | |
options | Readonly<ThirdPersonCameraOptions> | |
surface? | GroundSurface | null | The walkable ground, where the caller has one. Not optional in
practice, and the reason is worth stating: colliders is not the whole world.
A consumer whose drivable surface is a banked ribbon cannot put it in the collider
set — a world-axis box around a banked cross-section has a flat lid at its highest
corner, which is floor where nothing is drawn and a wall where the next slab's lid
stands above the character's feet. So that surface is invisible to segmentHit, and a
boom tested only against boxes passes straight through the deck.
Which is exactly what it did. On a descent, pitching the view down put the eye
under the track, with the deck between the camera and the character: the rig sees the
underside of the surface instead of the character, because dynamic surfaces are not
part of what it tests against. Worse in replays, where the camera moves far more than
a player ever moves it. |
/** * A world query for the arm | | |
instead of the collider set. * * Fourth and defaulted | | |
so no existing call site changes — the same shape `surface` was added * in. When it is given | | |
`colliders` is not consulted at all: a caller with one has the whole * world behind it and a set of boxes beside it would be a second | | |
partial answer to the same * question. See `BoomObstruction` for why one consumer could not supply the set. */ obstruction?: BoomObstruction | null | |
Properties
| Name | Type | Description |
|---|---|---|
camerareadonly | Camera | |
boomUpX | number | The frame the arm is built in. World axes unless a caller says otherwise.MoreThe arm used to be a spherical boom about world Y — The rig already anticipated this case and stopped one step short of it: |
boomUpY | number | |
boomUpZ | number | |
boomForwardX | number | The frame's forward, readable because a caller that sets one needs to know what it became.MoreBoth |
boomForwardY | number | |
boomForwardZ | number |
Methods
setBoomUp
setBoomUp(x: number, y: number, z: number): voidPoint the boom's up at a direction; the rest of the frame follows it.
| Parameter | Type | Description |
|---|---|---|
x | number | |
y | number | |
z | number |
More
Forward is carried from the frame it had rather than derived from a world axis, which is
what makes a body walking from a floor onto a wall and onto a ceiling continuous. Deriving it
from a fixed reference would be one line shorter and would put a pole wherever up lined up
with that reference: the arm would swing through a half turn as a subject crossed it, on a rig
whose whole job is that the view does not snap.
The fallbacks are only reached when the carried forward has become parallel to the new up, which a continuous rotation cannot do in one step and a teleport can. World Z first and world X behind it, so there is always an answer and it is never a normalised zero.
A zero-length vector keeps the frame it had, for the reason setUp on the character
controller gives: a NaN basis is a camera that is nowhere, and every number downstream of it
reads as a rig that has failed rather than as an argument that was refused.
setBoomForward
setBoomForward(x: number, y: number, z: number): voidPoint the boom's forward at a direction; the up is kept and the arm is rebuilt around it.
| Parameter | Type | Description |
|---|---|---|
x | number | |
y | number | |
z | number |
More
For a subject that has a heading of its own, which the carried forward cannot express.
setBoomUp keeps whatever forward the frame had, re-projected — the right default, because it
is what makes a body walking from a floor onto a wall continuous, and because most subjects
have no heading to speak of. What it cannot say is "behind this": a yaw of π puts the eye
opposite a forward the caller never chose and, until this existed, could not read either. A
subject spawning face-on to a wall got a shot framed at random, and a caller trying to work out
how far to roll so that the support surface sits at the bottom of the picture had to know where
the arm was — an azimuth measured from an axis nothing reported.
This is not a reversal of the carried forward and does not change it for anyone. A caller
that never calls this carries its forward exactly as before, and one that does still has it
re-orthogonalised by every setBoomUp — the heading is a direction in the world, and the frame
it lands in is the surface's.
A zero-length vector, or one parallel to the current up, keeps the frame it had. Deliberately
not the fallback chain setBoomUp uses, and the asymmetry is the point: that method must
produce some forward because the up is what changed and a frame needs one, so an arbitrary
axis is better than none. Here the forward is the argument, and answering a direction the
caller did not ask for is precisely the failure this exists to fix.
snap
snap(): voidupdate
update(frameDt: number, yaw: number, pitch: number, targetX: number, targetY: number, targetZ: number, targetFovYDeg: number, /** * Roll, radians. The caller's policy: this rig knows nothing about banked * surfaces, only that a camera can be tilted. Damped here rather than by * the caller so a subject snapping between surfaces cannot snap the view. */ targetRoll?: number): void| Parameter | Type | Description |
|---|---|---|
frameDt | number | |
yaw | number | |
pitch | number | |
targetX | number | |
targetY | number | |
targetZ | number | |
targetFovYDeg | number | |
/** * Roll | | |
radians. The caller's policy: this rig knows nothing about banked * surfaces | | |
only that a camera can be tilted. Damped here rather than by * the caller so a subject snapping between surfaces cannot snap the view. */ targetRoll?: number | |