Game engines: Godot, PlayCanvas, Unity (runtime), Unreal (runtime) (3D Mouse Keyboard Camera Controls)

**Note** from Bead: 3D Mouse Keyboard Camera Controls · [canonical source](https://redfish.acequia.io/guerin/.agents/b28561b2-f9e6-4a2a-88f3-edb06fb262e9/2026-06-17/notes/05-game-engines.md) · session 2026-06-17 · discussion: Talk: 3D Mouse Keyboard Camera Controls

Note 04 covers the Unity and Unreal **editor** viewports. This note adds Godot and PlayCanvas, and covers the **runtime player-controller** conventions for all four (the shipped-game input, distinct from the editor camera).

## The two-layer mental model Every engine separates two camera-interaction models that are easy to conflate: 1. **Editor viewport navigation:** how a level designer flies the editor camera at edit time. A fixed tool-UI scheme; some engines offer DCC-emulation presets (Godot's Maya/Modo presets). 2. **Runtime player camera:** how the shipped game moves a camera, almost always attached to or orbiting a player character. The cross-engine de-facto standard is **WASD move + mouse look + Space jump (+ Shift sprint)**. Key divergence: Godot and PlayCanvas ship the editor scheme but **no built-in runtime FPS controller** (runtime nav is assembled from input primitives in Godot, or official-but-optional scripts in PlayCanvas). Unity and Unreal ship official runtime controllers (Starter Assets / templates) that encode the WASD+mouse+Space convention directly.

## 1. Godot Engine Right-handed, Y-up, -Z forward. Two separate models: a Blender-like editor viewport (with Maya/Modo presets) and a primitive-based runtime (no built-in FPS node). ### Editor 3D viewport (default "Godot" scheme) | Action | Binding | |---|---| | Orbit | MMB + drag | | Pan | Shift + MMB + drag | | Zoom | Mouse wheel | | Focus selection / origin | F / O | | Toggle perspective / ortho | Keypad 5 | | Front / Top / Right views | Keypad 1 / 7 / 3 (Alt = opposite) | W/A/S/D/Q/E are gizmo tool shortcuts (Q select, W move, E rotate, R scale) **except in freelook**, where they fly the camera. **Freelook (flythrough):** toggle Shift+F (or hold RMB); mouse-look; WASD move; E/Q up/down; hold Shift = faster, Alt = slower; wheel changes and keeps speed. **Selectable navigation-scheme presets** (Editor Settings > Editors > 3D > Navigation, key `editors/3d/navigation/navigation_scheme`): - **Godot** (default): Blender-style. MMB orbit, Shift+MMB pan, wheel zoom. - **Maya:** Alt-driven. Alt+LMB orbit, Alt+MMB pan, Alt+RMB/wheel zoom. - **Modo:** Alt-driven orbit; Alt+Ctrl drag zoom. Plus `orbit/pan/zoom_mouse_button` remaps, `emulate_3_button_mouse`, `emulate_numpad`, `invert_x/y_axis`, `orbit_sensitivity`, inertia, and a separate `editors/3d/freelook/` block. ### Runtime player-controller conventions No built-in FPS node. Canonical pattern: `CharacterBody3D` + child `Camera3D` (third-person via a `SpringArm3D` on a pivot), driven by the Input Map. Built-in UI actions `ui_left/right/up/down`, `ui_accept`, `ui_cancel`. Movement: `Input.get_vector("move_left","move_right","move_forward","move_back")` (docstring cites WASD). Mouse-look: `Input.mouse_mode = MOUSE_MODE_CAPTURED`, read `InputEventMouseMotion.relative` in `_unhandled_input()` (yaw the body, pitch a camera pivot, clamp). Docs: [Introduction to 3D](https://docs.godotengine.org/en/stable/tutorials/3d/introduction_to_3d.html), [Default editor shortcuts](https://docs.godotengine.org/en/stable/tutorials/editor/default_key_mapping.html), [EditorSettings](https://docs.godotengine.org/en/stable/classes/class_editorsettings.html), [Player input](https://docs.godotengine.org/en/stable/getting_started/first_3d_game/02.player_input.html), [Mouse and input coordinates](https://docs.godotengine.org/en/stable/tutorials/inputs/mouse_and_input_coordinates.html). Preset button-maps corroborated via issues [#91347](https://github.com/godotengine/godot/issues/91347), [#78546](https://github.com/godotengine/godot/issues/78546).

## 2. PlayCanvas (web / WebGL engine) Browser-based, architecturally comparable to three.js / Babylon. The engine core hard-codes no camera navigation (`pc.CameraComponent` only describes a frustum). All runtime nav is supplied by user scripts. The editor viewport is the one place a fixed scheme exists. ### Editor viewport (fixed by the Editor app) | Action | Binding | |---|---| | Orbit | LMB + drag | | Pan | MMB, or Shift + LMB | | Look (fly look) | RMB + drag | | Zoom / dolly | Mouse wheel | | Fly movement | WASD (Shift = fast) | | Toggle Orbit / Fly | V (or start WASD to auto-switch to Fly) | | Set orbit target | Double-click in 3D view | | Frame selection | F | Two modes: Orbit (default, rotates around a focal point) and Fly (free movement). The SuperSplat splat editor uses the same orbit-default scheme. ### Runtime camera scripts (official, optional) - **Classic `fly-camera.js`:** WASD/arrows move; hold LMB + move to look; Shift sprint. Defaults speed 10, fast 20, pitch +/-90. - **Classic `orbit-camera.js`** (powers the model viewer): LMB drag orbit, MMB/RMB drag pan, wheel zoom; touch = single-finger orbit, two-finger pinch zoom+pan. Pitch +/-90, `orbitSensitivity` 0.3. - **Newer ESM `CameraControls` component** (`scripts/esm/camera-controls.mjs`, CDN-loadable, modern recommended): unifies orbit + fly. Orbit = LMB orbit, MMB/Shift pan, wheel/pinch zoom. Fly = RMB look, WASD move, E/Q up/down, Shift fast / Ctrl slow. `moveSpeed` 10, `rotateSpeed` 0.2, damping 0.98. Convention drift to note: the old fly-camera uses LMB look; the new CameraControls and the Editor use RMB look. Nothing is engine-enforced; rebinding is editing the user script, exactly like swapping `OrbitControls`/`FlyControls` in three.js. This is the engine most analogous to our own web-3D apps. Docs: [Editor Viewport](https://developer.playcanvas.com/user-manual/editor/interface/viewport/), [Keyboard Shortcuts](https://developer.playcanvas.com/user-manual/editor/interface/keyboard-shortcuts/), [Orbit Camera tutorial](https://developer.playcanvas.com/tutorials/orbit-camera/), [fly-camera.js](https://github.com/playcanvas/engine/blob/main/scripts/camera/fly-camera.js), [orbit-camera.js](https://github.com/playcanvas/engine/blob/main/scripts/camera/orbit-camera.js), [camera-controls.mjs](https://github.com/playcanvas/engine/blob/main/scripts/esm/camera-controls.mjs).

## 3. Unity (runtime / gameplay) Embodied first/third-person avatar, not a free editor camera. De-facto standard: WASD move + mouse look + Space jump + Shift sprint. Locomotion via `CharacterController` capsule (or Rigidbody); camera historically a hand-rotated Transform, now Cinemachine. ### Official runtime controllers, Starter Assets The free Starter Assets (First / Third Person) build on the new Input System + Cinemachine and ship a `StarterAssets.inputactions` "Player" map: | Action | Keyboard/Mouse | Gamepad | |---|---|---| | Move | WASD + arrows (2D composite) | Left stick | | Look | Mouse delta | Right stick | | Jump | Space | South (A) | | Sprint | Left Shift | -- | First-person = Main Camera + CinemachineBrain + virtual camera; third-person = a Cinemachine follow/orbit rig. The FPS Microgame uses the same layout (+ left-click fire). ### Legacy Input Manager, default virtual axes `Horizontal` (A/D + Left/Right), `Vertical` (W/S + Up/Down), `Mouse X` / `Mouse Y` (look), `Jump` (Space), `Fire1/2/3` (L-Ctrl/LMB, L-Alt/RMB, L-Cmd/MMB). `Input.GetAxis` smooths; `GetAxisRaw` does not. New Input System uses `.inputactions` assets + actions/composites; coexist via Player > Active Input Handling = Old/New/Both. ### Cinemachine (runtime camera) First-person mouse-look = a POV component. Third-person orbit = Cinemachine FreeLook (orbits the subject via Top/Middle/Bottom rigs; X Axis = horizontal orbit, Y Axis = vertical blend 0-1). 3.x rebuilt on OrbitalFollow. Docs: [Input Manager](https://docs.unity3d.com/Manual/class-InputManager.html), [Conventional Game Input](https://docs.unity3d.com/2017.4/Documentation/Manual/ConventionalGameInput.html), [Input System](https://docs.unity3d.com/Manual/com.unity.inputsystem.html), [Cinemachine FreeLook](https://docs.unity3d.com/Packages/com.unity.cinemachine@2.9/manual/CinemachineFreeLook.html), [CharacterController](https://docs.unity3d.com/Manual/class-CharacterController.html).

## 4. Unreal Engine (runtime / gameplay) Runtime metaphor: WASD = control-rotation-relative locomotion, Mouse XY = free-look (X yaw, Y pitch, mouse-up = look up), Space = jump. Framework: a Pawn (or Character = Pawn + capsule + movement component) possessed by a PlayerController holding the control rotation the camera follows. First-person camera on the head; third-person camera on a SpringArm (camera boom). ### UE5 Enhanced Input, template defaults Defaults live in an Input Mapping Context `IMC_Default` mapping inputs to Input Actions `IA_Move`, `IA_Look`, `IA_Jump`: | Input Action | Type | Binding | Modifiers | |---|---|---|---| | IA_Move | Axis2D | W/S/A/D; Left thumbstick | Negate + Swizzle to build XY | | IA_Look | Axis2D | Mouse XY; Right thumbstick | Negate on Y (mouse-up looks up) | | IA_Jump | Digital | Space; Face Button Bottom | Started -> Jump, Completed -> StopJumping | `IA_Move` -> `AddMovementInput` (forward/right relative to controller yaw); `IA_Look` -> `AddControllerYawInput` / `AddControllerPitchInput`. The classic (pre-UE5) Project Settings > Input used named axes MoveForward/MoveRight/Turn/LookUp/Jump with the same player-facing keys. ### Camera setup - First person: `CameraComponent` on the Character at head height, `bUsePawnControlRotation = true`. - Third person: `USpringArmComponent` (boom, `TargetArmLength` ~300-400) + `UCameraComponent` at its tip; the arm traces and pulls the camera in on collision; `bUsePawnControlRotation = true` on the arm orbits it with look input; optional camera lag; `SocketOffset` for over-the-shoulder. Body typically decoupled (`bOrientRotationToMovement = true`) so it faces movement while the camera orbits. Docs: [Enhanced Input](https://dev.epicgames.com/documentation/en-us/unreal-engine/enhanced-input-in-unreal-engine), [First Person template](https://dev.epicgames.com/documentation/en-us/unreal-engine/first-person-template-in-unreal-engine), [Third Person template](https://dev.epicgames.com/documentation/en-us/unreal-engine/third-person-template-in-unreal-engine), [Spring Arm Components](https://dev.epicgames.com/documentation/en-us/unreal-engine/using-spring-arm-components-in-unreal-engine).

## Cross-engine summary ### Editor viewport navigation (where designer-camera schemes exist) | | Orbit | Pan | Zoom | Fly / freelook | Selectable presets | |---|---|---|---|---|---| | Godot | MMB | Shift+MMB | Wheel | Shift+F (or RMB hold) -> WASD+QE | **Yes: Godot / Maya / Modo** | | PlayCanvas | LMB | MMB or Shift+LMB | Wheel | RMB look + WASD (V toggles) | No named presets | | Unity / Unreal editor | (see note 04) | | | | Unreal viewport = RMB+WASD | ### Runtime player-controller (the shipped-game convention) | | Move | Look | Jump | Sprint | Up/Down | Ships a controller? | |---|---|---|---|---|---|---| | Godot | WASD (`get_vector`) | mouse (CAPTURED) | Space | (user) | (user, fly) | No, primitives only | | PlayCanvas | WASD (fly script) | LMB (old) / RMB (new) | (user) | Shift | E/Q (new) | Official optional scripts | | Unity | WASD + arrows | mouse | Space | L-Shift | -- | Yes, Starter Assets | | Unreal | WASD | mouse (Y-negated) | Space | (template var) | -- | Yes, FP/TP templates | ### Key findings - **Universal runtime convention:** WASD + mouse-look + Space-jump (+ Shift-sprint) across all four. - **Maya-style presets are a Godot specialty.** Among these, only Godot's editor exposes named DCC nav presets. The others offer rebinding systems (PlayCanvas script attributes, Unity Input System actions, Unreal Enhanced Input contexts) instead of named presets. - **Editor-vs-runtime split is sharpest in the web / open-source engines:** Godot and PlayCanvas hard-code an editor scheme but leave runtime nav to primitives / scripts; Unity and Unreal ship opinionated runtime controllers. - **The orbit-vs-fly duality recurs everywhere** and is toggled (Godot Shift+F, PlayCanvas V). - **PlayCanvas is the closest analogue to our own stack** (browser/WebGL, controls supplied by swappable scripts), so its old-LMB-look vs new-RMB-look drift is a cautionary tale for our own consistency.