orbit-camera
A third-person orbit/follow camera for action-adventure games, extracted from a shared pattern across several games (magic-journey, bad-fates, oneira-woods, cyborg-evolution, ruffy-wonder, open-world-minigolf).
The camera orbits a focus point that smoothly follows a target. Yaw and pitch are set directly; the orbit distance scales with pitch and can be zoomed. Focus and distance interpolate frame-rate independently. An optional clipping pass pulls the camera in when geometry blocks the view.
Design
- No matrix dependency.
view()returns eye, focus, up and projection parameters. Build the view-projection matrix with whatever the renderer uses. - Backend-agnostic clipping.
cliptakes anyClipimplementor. Enable thecollide-meshfeature for a ready-made impl oncollide_mesh::CollisionWorld. - Fully configurable. Every constant (pitch range, distances, smoothing,
field of view, clip margins) lives in
CameraConfig.Defaultmatches a Zelda-style feel.
Built on ga3::Vector<f32>.
Example
use Vector;
use OrbitCamera;
let mut camera = new;
// per frame:
camera.rotate;
camera.zoom;
camera.follow;
camera.clip; // requires the `collide-mesh` feature, or impl Clip yourself
let view = camera.view;
let view_projection = perspective
* look_at;
// move the player relative to the camera:
let basis = camera.basis;
let movement = basis.forward * input.y + basis.right * input.x;
Features
collide-mesh— implementClipforcollide_mesh::CollisionWorld.
AI-coding friendliness
- Pure-function camera state:
view()/basis()/eye()have no side effects. - No implicit matrix convention baked in — the consumer owns the projection.
- Clipping is decoupled behind the one-method
Cliptrait, so the camera has no hard dependency on any collision backend.