Flight controller input

The flight scene accepts the shell's normalized Xbox-style gamepad snapshot through view.handleGamepad(pad, realDt). The shell polls once per animation frame, including while animation is paused. Stored flight axes are consumed by the next fixed update; direct camera changes invalidate the next render. Inactive frames deliver a neutral snapshot. Hardware polling, deadzones, arming, Menu/pause, and View/help remain owned by the shell.

InputFlightLandscape inspection
Left stickPitch and rollPan in camera right/up directions
Right stickLook around cockpit or chase viewOrbit
LB / RBRudder left / rightNo aircraft input
RT / LTIncrease / decrease throttleZoom in / out
ASwitch cockpit / chaseResume the previous flight view
XToggle landing gearNo aircraft input
YToggle guided / manualNo aircraft input
B or right-stick clickCenter flight lookFlight look is reset for later use
D-pad up / downRetract / extend flaps by 0.5No aircraft input
D-pad left / rightToggle navigation / landing lightsNo aircraft input
Left-stick clickLevel wings and enter manual modeNo aircraft input

Default pitch is conventional aircraft control: pull the left stick down to raise the nose. settings.invertFlightPitch = true reverses that axis. settings.gamepadSensitivity defaults to 1 and adjusts stick and view response; it is bounded to 0.25–3. Trigger throttle rate is independent of stick sensitivity, at 0.32 of full range per second. Elapsed time is capped at 0.1 seconds per input frame to avoid jumps after stalls.

Pitch, roll, rudder, or net throttle input takes control from the guided tour automatically. Camera movement and gear/light/flap buttons do not. If a guided toggle and active flight-axis input arrive together, manual input takes precedence. view.setTour(value) and controller transitions report state changes through settings.onFlightTour(value). view.tourEnabled exposes the current state. Aircraft button/throttle changes notify settings.onAircraftControl(name, value).

Paused controller frames can change views and camera look, including inspection pan/orbit/zoom. They cannot change aircraft controls, guided state, or body orientation. Flight-axis state clears on pause and on every disconnected, unsupported, unarmed, or otherwise inactive controller snapshot. The caller must send its neutral snapshot on focus loss/disconnection; there is no polling timer inside the scene. No paused button actions are queued for resumption.

Gamepad button actions use pressed edges, so held buttons do not repeat. Existing keyboard and mouse controls remain available. Keyboard and gamepad flight axes are added and clamped before angular rates are applied; opposing commands cancel. Mouse cockpit look and controller look share the same yaw/pitch limits. The existing keyboard V/G toggles now also ignore keyboard-repeat events.

The pure mapping and combination functions live in shell/lib/flight-controls.js. Focused Node tests in tests/flight-controls.test.mjs cover body-coordinate signs, inverted pitch, frame-rate-independent throttle, pause/inspection gating, neutral/disconnect states, held buttons, sensitivity, and combined-axis limits. These tests do not claim verification with a physical controller.