OverviewThis WebsiteHome LabTrading BotEcosimProcedural Generation
Projects

Orbit Camera

A reusable Bevy plugin for orbital camera control. Input handling is fully decoupled from camera logic via a trait, making it straightforward to bind any combination of keyboard, mouse, gamepad, or custom input.


Design

Trait-Based Input

The plugin is generic over an OrbitCameraInput trait. Any type that implements this trait can drive the camera — default mouse controls, a gamepad, keyboard arrows, or a custom input reader that pulls from a network socket. The camera system itself never touches Res<ButtonInput<KeyCode>> directly; it only sees normalised orbit/zoom/roll deltas.

This pattern means the same OrbitCameraPlugin<I> can be dropped into any project and configured for the project's input conventions without forking or patching the plugin.


Mathematics

Quaternion Rotation

All rotations use quaternions exclusively — no Euler angles. This prevents gimbal lock regardless of orbit angle, which matters when the camera is used to orbit a planet where the user can reach any viewing angle including directly over the poles.

Two rotation modes are supported: GlobalY (turntable — yaw around the world up axis, pitch around the camera's local X) and CameraRelative (free-look — both axes relative to the camera). The former is natural for viewing a model; the latter for flying around a scene.


Feel

Speed Falloff

Zoom speed decreases as the camera approaches its minimum radius, providing a natural "resistance" feeling rather than abruptly hitting a hard stop. Orbit speed scales proportionally with the current radius so that a given mouse movement always sweeps the same arc length on the focus sphere — the camera feels equally responsive when zoomed in close or pulled back to a wide view.


API

Usage

Adding the camera to a Bevy app is a few lines:

app.add_plugins(OrbitCameraPlugin::<MouseInput>::default());

commands.spawn((
    Camera3dBundle::default(),
    OrbitCamera {
        focus: Vec3::ZERO,
        radius: 10.0,
        ..default()
    },
));

Swapping to a custom input scheme means changing the type parameter and providing an implementation of OrbitCameraInput — the camera behaviour is unchanged.