Controls and Input

This document explains how you fly an RHCP helicopter: the four core control axes a helicopter pilot uses, the complete default key/button bindings for keyboard & mouse, gamepad, and touch, and the systems that sit behind them — the Input Actions asset, mouse-cursor capture, the input-locking system menus use, and the sensitivity/invert options. Everything here is verified against the shipped RHCP_InputActions.inputactions asset and the Runtime/Input/ source so the names, paths, and values match the asset exactly. If you only want to fly, read Control Concepts and Default Bindings; the rest is for when you wire your own UI, AI, or rebinding.

Control Concepts

A helicopter is flown with three flight axes plus the engine, and RHCP maps each to one input action. Understanding what each axis physically does makes the bindings below self-explanatory, because the action names (Collective, Pedals, Cyclic, EngineToggle) are the aviation terms. RHCP normalizes every device down to the same small set of signed values, so the same flight model code runs whether you fly with a keyboard, a gamepad, or on-screen touch sticks.

The four controls are:

Two more concepts appear in flight tuning and in the Flight Tuning doc but are not separate inputs: ground effect is the extra lift you get when hovering close to the ground (a cushion of air under the disc), and translational lift (ETL) is the efficiency gain the rotor gets once you have some forward speed. They are produced automatically by the flight model from your collective and cyclic inputs.

Camera control is a fourth input group. It is a separate action map (Camera) so it can stay live independently of flight — you can look around or switch view even while flight input is locked. See Cameras for what each camera mode does; this doc covers only the input side (Look, FreeLook, CameraSwitch, Zoom).

Default Bindings

The tables below list every action and its default binding, read directly from RHCP_InputActions.inputactions. The asset has three action maps — Flight, Camera, and Demo — and the Demo map's actions are convenience controls used only by the demo scene, not by the runtime flight code. All of these are remappable; see Rebinding.

Default controls — keyboard/mouse and gamepad mappings for collective, cyclic, pedals, engine, and camera.

Keyboard and Mouse

Action Map Binding What it does
Collective Flight W (up) / S (down) Climb / descend (1D axis composite, -1…+1)
Pedals Flight A (left) / D (right) Yaw nose left / right (1D axis composite)
Cyclic Flight Mouse movement, plus arrow keys /// Pitch & roll. Mouse delta drives a virtual stick (see Cursor Capture); arrow keys are a mouse-free fallback
EngineToggle Flight E Start / shut down the engine
Look Camera Hold Right Mouse Button + move mouse Free-look / orbit around the aircraft
FreeLook Camera Right Mouse Button (hold) Modifier that switches the mouse from cyclic to camera look
CameraSwitch Camera C Cycle camera mode (Chase → Cockpit → Orbit)
Zoom Camera Mouse Scroll Wheel (Y) Zoom / dolly the camera in and out
RestartDemo Demo R Respawn the helicopter at its spawn point
ToggleHUD Demo H Show / hide the HUD
OpenSettings Demo Esc Open the demo settings / pause panel

The mouse Cyclic binding (the CyclicDelta source) pre-scales the raw mouse delta by a built-in ScaleVector2 factor of 0.05 per axis, and the scroll Zoom binding scales by 0.02, before your sensitivity settings are applied (the Look mouse-delta binding has no static scale — its magnitude is shaped entirely by lookSensitivity). Holding the right mouse button is what tells RHCP "this mouse motion is for the camera, not the cyclic" — while it is held the cyclic is frozen at its last value (see The Input Actions Asset).

Gamepad

Action Map Binding What it does
Collective Flight Left Stick Y Climb / descend (center = neutral)
Pedals Flight Left Stick X Yaw nose left / right
Cyclic Flight Right Stick Pitch & roll
EngineToggle Flight West button (X / Square) Start / shut down the engine
Look Camera Hold Left Shoulder (LB) + Right Stick Free-look / orbit
FreeLook Camera Left Shoulder (LB) (hold) Modifier: switches the right stick from cyclic to camera orbit
CameraSwitch Camera North button (Y / Triangle) Cycle camera mode
ZoomAxis Camera D-Pad Y (up/down) Zoom / dolly the camera in and out
OpenSettings Demo Start Open the demo settings / pause panel
ToggleHUD Demo D-Pad Left Show / hide the HUD

The gamepad right stick is bound to both Cyclic and (with LB held) the camera Look orbit. RHCP resolves the overlap by freezing the cyclic while LB is held, so the stick orbits the camera without also lurching the helicopter. Gamepad zoom uses a separate action (ZoomAxis) from the mouse scroll Zoom because a held D-Pad direction is integrated as a rate over time (so dolly speed stays the same regardless of frame rate), whereas the scroll wheel delivers discrete deltas.

Note: RestartDemo is bound on the keyboard (R) but has no default gamepad binding in the shipped asset, and D-Pad Up is intentionally left free (zoom uses D-Pad Y). If you want a gamepad restart button, add a binding in the Demo map (see Rebinding).

Touch

RHCP's mobile controls do not use a separate touch binding scheme. Instead, the on-screen controls (under Runtime/Mobile/) create a virtual gamepad device and feed the same gamepad control paths the table above already binds, so there is no touch-specific input code — one code path serves all three input methods. See HUD and Mobile for the on-screen layout, sizing, and how to enable it.

On-screen control Feeds gamepad path Resulting action(s)
Collective control Left Stick Y Collective
Pedal control Left Stick X Pedals
Cyclic stick Right Stick Cyclic
Engine button West button EngineToggle
Camera button North button CameraSwitch

Because the on-screen controls write virtual-gamepad paths, anything you rebind on the Gamepad scheme also changes how the touch controls map. Camera free-look is not bound by default on mobile (the chase camera is the default mobile view); add a swipe-look region only if your project needs it.

The Input Actions Asset

All input is defined in one asset, RHCP_InputActions.inputactions, located at Assets/Realistic Helicopter Controller Pro/Runtime/Input/. It contains the three action maps described above — Flight, Camera, and Demo — and each map can be enabled or disabled independently, which is what lets RHCP freeze flight while keeping the camera live, or run the demo-only actions only in the demo scene. The maps and their actions are exactly:

Flight   : Collective, Pedals, Cyclic, CyclicDelta, EngineToggle
Camera   : Look, FreeLook, CameraSwitch, Zoom, ZoomAxis
Demo     : RestartDemo, ToggleHUD, OpenSettings

CyclicDelta is the raw mouse-delta source for the cyclic; the provider integrates it into a virtual stick rather than reading the mouse directly elsewhere. Zoom (mouse scroll) and ZoomAxis (gamepad D-Pad) are two separate actions that both feed one zoom value, for the frame-rate reason noted above. Both are optional — if they are missing from a modified copy of the asset, the camera simply has no zoom and the rest of input still works.

RHCP loads its own asset; it does not use Unity's project-wide Input System "default actions." The RHCP_InputManager component on each helicopter holds a reference to the asset in its actionsAsset field, and the Setup Wizard assigns the shipped default automatically. At startup, RHCP_InputProvider_InputSystem resolves each action by name once and caches the references, so there are no per-frame string lookups while flying. If the actionsAsset reference is missing, or required Flight/Camera actions cannot be found in a hand-edited copy, the provider logs a single clear error naming the Setup Wizard and then treats all input as neutral — the helicopter stays physical and the engine stays off, with no per-frame log spam.

The flight model never reads devices directly. The provider produces a normalized RHCP_InputState snapshot (collective, pedals, cyclic, look, zoom, plus latched button edges), the manager publishes it onto the helicopter hub each physics step, and every other system — cameras, HUD, the cockpit stick visual — reads that snapshot. Input is sampled every frame in Update and consumed once per FixedUpdate, so a button press between physics steps is registered exactly once (never missed at high frame rates, never double-fired at low ones). For the programmer-facing details of RHCP_InputState, IRHCP_InputProvider, and RHCP_InputManager, see the XML documentation comments on those types (surfaced by your IDE's IntelliSense).

Mouse Cursor Capture

Because mouse motion drives the cyclic (and, with the right button held, the camera), RHCP locks and hides the OS mouse cursor for the local player while the camera is live. Without this, the system pointer would wander off the game view and your mouse movement would stop reaching the helicopter. When the cursor is captured it is centered and invisible, and all mouse motion becomes cyclic or camera input.

The capture is keyed to the Camera input channel, not the Flight channel. That is deliberate: the cursor stays captured even when flight input is locked (for example, a paused cinematic that still lets you orbit), and it is released only when the camera itself is locked — which is what a full menu does. The cursor auto-releases in these situations, then re-captures when conditions allow:

Situation Cursor behavior
Helicopter spawns, camera live, app focused Captured (locked + hidden)
The Camera channel is locked (a full menu such as the demo Settings panel) Released (free + visible)
App loses focus (alt-tab) or is unfocused Released; re-captured when focus returns
Editor Esc releases the cursor (Play Mode) Released; RHCP does not fight it — it re-captures only when you click back into the game view
Menu closed / focus regained Re-captured automatically
Helicopter despawns or you exit Play Mode Released — the cursor is never left captured and invisible

You can turn capture off entirely with the lockCursorDuringFlight field on the RHCP_InputConfig asset. It defaults to on; set it off for gamepad-only or touch builds where you never want the OS cursor hidden. Capture also applies only to the helicopter flagged as the local player (isPlayer on RHCP_InputManager) — AI and remote helicopters never touch the cursor.

Input Locking

When a menu opens, flight must stop without losing the helicopter's state and without one system clobbering another's "input enabled" flag. RHCP solves this with a counted lock system on RHCP_InputManager, organized by channel: Flight, Camera, and Demo. A channel is suppressed while any live lock holds it, and it resumes only when the last one is released — this is a count, not a last-writer-wins flag, so two systems can lock the same channel safely.

The API is token-based. You call AcquireLock(owner, channel) and get back a disposable RHCP_InputLockToken; disposing the token (or calling ReleaseLock(token)) releases one count. While the Flight channel is locked, the collective, pedals, and cyclic all read neutral (0), the cyclic accumulator resets, and the EngineToggle edge is suppressed — so a menu cannot accidentally start the engine. Locking the Camera channel additionally frees the OS cursor, which is exactly what a full menu wants. The owner you pass is carried for diagnostics so the editor tooling can show which object is holding a lock.

The system is robust against leaks. If a lock owner is destroyed without releasing its token (for example, a UI panel that is deleted), the manager sweeps the dead reference each frame and reclaims the count, so a despawned menu can never leave the helicopter permanently frozen. In the shipped demo, the Settings panel acquires both a Flight and a Camera lock while open (the Camera lock is what frees the cursor for the menu); the crash/respawn flow takes a different route — RHCP_Damage disables a configured list of behaviours (its disableWhileCrashed list, which can include the input manager) during the death window rather than acquiring a Flight lock. You use the same AcquireLock/ReleaseLock pair for your own menus. Note that free-look needs no lock — it is resolved structurally by the FreeLook modifier described in The Input Actions Asset.

Sensitivity and Invert

Control feel — inversion and sensitivity — lives in a single ScriptableObject, RHCP_InputConfig, separate from flight handling (rates and assists, which live in RHCP_FlightConfig; see Flight Tuning). The boundary is simple: anything a player would call a "controls option" is here, anything a designer would call "handling" is in the flight config. A default config ships and the Setup Wizard assigns it; if a helicopter has no config, the provider falls back to these same defaults. Create your own via Assets ▸ Create ▸ BoneCracker Games ▸ RHCP ▸ Input Config.

The full field set, with the exact defaults from RHCP_InputConfig.cs:

Field Type Default Range Effect
invertCyclicPitch bool false Inverts the cyclic pitch axis (nose up/down)
invertCyclicRoll bool false Inverts the cyclic roll axis (bank left/right)
invertLookY bool false Inverts the vertical free-look axis
mouseCyclicSensitivity float 1.0 0.15 Mouse-delta → cyclic gain; higher = cyclic moves faster per mouse motion
lookSensitivity float 1.0 0.15 Free-look delta gain
cyclicRecenterRate float 5.0 010 How fast the mouse virtual-stick re-centers (1/s). High = arcade "raw rate" feel; low = deliberate sim-style held cyclic
gamepadCyclicExpo float 0.3 01 Response curve on gamepad cyclic — softens the stick center, keeps full deflection
deadzoneOverride float -1 Optional per-user deadzone widening; -1 = use the binding's built-in deadzone
lockCursorDuringFlight bool true Cursor capture toggle (see Cursor Capture)

A key design point: inversion and sensitivity are never baked into the bindings. They are applied at runtime by the provider, after the binding's static processors and before the cyclic is accumulated and clamped. This keeps one source of truth and means a future rebinding UI never has to round-trip processor state.

For end users, the demo Settings panel (opened with Esc) exposes the common feel options at runtime — invert and sensitivity — and writes them as runtime overrides rather than modifying the shared config asset. This matters: writing to a ScriptableObject in Play Mode would persist and corrupt the shipped defaults, so the panel instead calls SetInvertPitch and SetCyclicSensitivity on RHCP_InputManager, which the provider reads in preference to the asset until you call ClearControlOverrides. See HUD and Mobile for the Settings panel's full row list.

Rebinding Notes

Because all bindings live in the one RHCP_InputActions.inputactions asset, you can edit them in Unity's Input Actions editor (double-click the asset) to change keys, add gamepad bindings, or support additional devices — the runtime resolves actions by name, so any binding you add to the Collective action (for example) is picked up automatically. The fact that the touch controls reuse the gamepad paths means a gamepad rebinding also re-maps the on-screen controls; keep that in mind if you remap the sticks.

For in-game, player-facing rebinding, use Unity's Input System runtime rebinding (PerformInteractiveRebinding) against the same asset, and persist overrides with SaveBindingOverridesAsJson. Because RHCP keeps inversion and sensitivity out of the bindings, your rebinding UI only ever has to deal with the bindings themselves, not processor state. Composite parts (the up/down halves of a 1D-axis composite, for instance) need a part-aware rebind flow; that is standard Input System work, not RHCP-specific.

If you need to drive a helicopter from a system that is not the New Input System — legacy Input Manager, Rewired, an AI controller, or a network replication layer — you do not modify RHCP's shipped input code. Instead you implement the IRHCP_InputProvider interface in your own assembly and assign it with RHCP_InputManager.SetProvider(...). Your provider produces RHCP_InputState snapshots and nothing else; only RHCP's own provider references UnityEngine.InputSystem, so a custom provider has zero coupling to it. The contract is documented in the XML doc comments on IRHCP_InputProvider.

Troubleshooting

Most input problems come down to the project's input setting, a missing asset reference, or the cursor-capture behavior surprising you. The table below covers the common ones; see also Troubleshooting and the Setup Wizard / Project Doctor, which detect several of these automatically.

Symptom Likely cause Fix
Nothing responds; one error mentioning "Input disabled" in the console Project's Active Input Handling is set to legacy-only, or the actionsAsset reference is missing on RHCP_InputManager RHCP requires the New Input System (Active Input Handling = "Input System Package" or "Both"). Run the Project Doctor, or use the Setup Wizard to assign the actions asset
Mouse moves the helicopter but I can't see the cursor Cursor capture is working as designed (mouse = cyclic) Press Esc to open Settings (releases the cursor), or set lockCursorDuringFlight to false on the RHCP_InputConfig for a gamepad/touch build
Cursor stays visible and mouse doesn't fly App is unfocused, the Camera channel is locked (a menu is open), or lockCursorDuringFlight is off Click back into the game view to re-capture; close any menu; check the config toggle
Helicopter holds attitude when I expect cyclic to respond You are holding the free-look modifier (right mouse / left shoulder) — cyclic is frozen during look Release the modifier; cyclic resumes with no lurch
Flight is dead but the camera still moves The Flight channel is locked by a menu (e.g. the Settings panel acquired a lock), or the input manager was disabled during a crash (RHCP_Damage disables it while crashed) Close the menu / wait for respawn; if it is stuck, check for an undisposed RHCP_InputLockToken
Engine never starts Engine not toggled, or EngineToggle is being suppressed by a Flight lock Press E (keyboard) / West button (gamepad); make sure no menu is holding a Flight lock
Gamepad unplugged mid-flight left a stuck axis Device hot-swap The Input System rebinds actions automatically when you re-plug the pad, so input resumes; if an axis stays stuck while the pad is out, open a menu (which locks Flight and resets the provider state via ResetState) or re-plug the controller

If the demo-only controls (R, H) do nothing, that is expected outside the demo scene — the Demo action map is enabled only by the demo components and is not part of the runtime flight loop. To use those convenience actions in your own scene, enable the Demo map and read the latched restartPressed / toggleHudPressed flags from RHCP_InputManager.CurrentState, as the demo controller does.