Controller Types & Input

Traffic Overtaker is a one-button hold-to-overtake game, so its input model is unusual and worth understanding before you reskin it. An on-car AI driver (TO_OvertakeAI) computes every driving input — steering along the curving road, throttle, and brake — while the human player supplies a single signal: hold the Overtake control to pull into the oncoming lane and pass, release it to ease back. Because of this, the "controller type" options you see in the menu (Touchscreen, Accelerometer, Steering Wheel, Joystick) are inherited from the bundled Realistic Car Controller Pro (RCCP) physics layer and the Highway Racer 2 lineage, but they no longer change how the car steers — the AI overrides all of that. This page explains what input actually reaches the car, how the legacy controller-type setting is still wired and persisted, and how to switch the build between Desktop and Mobile input modes.

The Actual Player Input: The Overtake Control

In gameplay the player never steers, accelerates, or brakes. Those inputs are authored by TO_OvertakeAI (Assets/TO/Scripts/AI/TO_OvertakeAI.cs), which writes RCCP input structs every physics step with externalControl enabled. The only thing the player controls is the public boolean overtakeHeld on that AI component. The on-screen button and its desktop key both resolve to this one flag, which is why the game is approachable on any device — there is no steering skill to deliver, only timing.

The control itself is TO_UI_OvertakeButton (Assets/TO/Scripts/UI/TO_UI_OvertakeButton.cs). It implements IPointerDownHandler and IPointerUpHandler so a touch or mouse press-and-hold sets overtakeHeld = true, and release sets it back to false. It also exposes a desktop fallback key, public Key overtakeKey = Key.Space (the New Input System's Key enum, from UnityEngine.InputSystem), so on a PC the player can simply hold Space. Each frame the component combines both sources, reading the keyboard through Keyboard.current:

// TO_UI_OvertakeButton.Update()
Keyboard keyboard = Keyboard.current;
ai.overtakeHeld = isPressing || (keyboard != null && keyboard[overtakeKey].isPressed);

Keyboard.current is null on devices with no keyboard (e.g. mobile), so the fallback key is skipped there and only the on-screen button drives the flag.

Two design details matter when reskinning. First, the button polls for the active player car every frame via RCCP_SceneManager.Instance.activePlayerVehicle rather than subscribing to a spawn event, so it survives respawns and is order-independent. Second, unlike the legacy horn button this control is always active — there is no mobile-controller gate on it, and it works identically on touch, mouse, and keyboard. If you replace the HUD art, keep this script on your new button and the input pipeline keeps working.

Desktop Keyboard Shortcuts

The project targets the New Input System exclusively — Player Settings ▸ Active Input Handling is set to Input System Package (New), and every keyboard read in Assets/TO/ goes through UnityEngine.InputSystem.Keyboard.current. The legacy UnityEngine.Input manager is no longer referenced by any TO script, so the old ProjectSettings/InputManager.asset button entries are now vestigial.

During a run, TO_GamePlayManager.Update() polls three desktop keys. They are live only while the run is active — not during the countdown or on the game-over screen — but pause stays reachable because the game still counts as "started" while paused, so Tab can also un-pause:

Key Action Routes to
Space (hold) Overtake — pull into the oncoming lane; release to return TO_UI_OvertakeButton.overtakeKeyTO_OvertakeAI.overtakeHeld
C Cycle the gameplay camera (Top → TPS → TPS_Fixed → FPS → Top) TO_Camera.ChangeCameraMode()
Tab Pause / resume TO_GamePlayManager.Paused()

Each key mirrors an on-screen button, so touch and keyboard stay in sync. Menu navigation is handled separately by TO_UI_KeyEnter (Assets/TO/Scripts/UI/TO_UI_KeyEnter.cs), which fires a Button.onClick from a named UI action. The action names preserve the project's original legacy Input Manager bindings, now expressed against Keyboard.current:

inputName Keys
Submit Enter, Numpad Enter, Space
Cancel Escape
Left Left Arrow, A
Right Right Arrow, D

The Controller Type Setting

The Options panel still offers the classic four-way input selector inherited from RCCP. Selecting an option calls TO_UIOptionsManager.SetControllerType(Button button) (Assets/TO/Scripts/UI/TO_UIOptionsManager.cs), which dispatches on the button's GameObject name and forwards an index to TO_API.SetControllerType(int). That static API method (Assets/TO/Scripts/TO_API.cs) persists the choice and pushes it into RCCP. The mapping below is the verified, end-to-end wiring — the integer index, the RCCP enum member it maps to, and the Options button name that triggers it:

Index RCCP_Settings.MobileController Options button name Meaning
0 TouchScreen Touchscreen On-screen touch buttons (default)
1 Gyro Accelerometer Tilt the device to steer (uses gyroSensitivity, default 2.5)
2 SteeringWheel SteeringWheel On-screen steering-wheel widget
3 Joystick Joystick On-screen virtual joystick

The persistence and round-trip are split across two TO_API methods. SetControllerType writes PlayerPrefs.SetInt("ControllerType", index) and calls RCCP.SetMobileController(...) with the matching enum member, then fires TO_Events.Event_OnOptionsChanged(). The companion GetControllerType() does not read the PlayerPrefs value back — it reads the live enum off RCCP_Settings.Instance.mobileController and converts it to the same 0–3 index. In practice the two stay in sync because SetControllerType updates both, but if you script the setting directly, change it through TO_API.SetControllerType so RCCP and the saved value match.

Why It No Longer Affects Driving

This is the key takeaway for anyone porting the game: because TO_OvertakeAI runs the car under externalControl, the chosen controller type does not change how the vehicle drives. The steering method (touch buttons vs. tilt vs. wheel vs. joystick) only governs RCCP's own mobile steering input, which the AI bypasses entirely. The selector is therefore vestigial for the core loop — the practical input on every platform is the single Overtake control plus pointer-based menu navigation. You can safely hide the controller-type buttons in a reskin without breaking gameplay; they are kept because the underlying RCCP infrastructure and the options UI were reused wholesale. The setting would matter again only if you re-enabled manual driving on the player car.

Options Panel Indicators

TO_UIOptionsManager exposes three highlight images — touch, tilt, and steeringWheel — that show which controller is active, lit on OnEnable() from TO_API.GetControllerType(). Note there is intentionally no indicator image wired for the Joystick (index 3) case; selecting it persists the value but lights none of the three indicators. If you add a joystick indicator during a reskin, extend EnableTargetControllerImage and the OnEnable highlight logic accordingly.

Desktop vs Mobile Build Modes

Separate from the per-player controller type is the project-wide Desktop / Mobile mode, governed by the single boolean RCCP_Settings.mobileControllerEnabled (default false, defined in Assets/TO/Realistic Car Controller Pro/Scripts/Scriptable Objects/RCCP_Settings.cs). This flag turns RCCP's on-screen mobile controller UI on or off for the whole project. Because the Overtake button is its own always-active control rather than part of RCCP's mobile rig, switching this flag does not disable the Overtake button — it governs RCCP's auxiliary mobile UI and input path.

You flip this mode from the editor rather than at runtime, using the Quick Switch menu items (Assets/TO/Editor/TO_EditorWindows.cs):

Menu item Effect
Tools > BoneCracker Games > Traffic Overtaker > Quick Switch To Desktop Sets mobileControllerEnabled = false
Tools > BoneCracker Games > Traffic Overtaker > Quick Switch To Mobile Sets mobileControllerEnabled = true (shows a confirmation dialog)

The same two actions are surfaced as Desktop / Mobile buttons in the Welcome Window, which highlights the currently active mode. Both routes record an Undo on RCCP_Settings so the change is reversible. Use Quick Switch as part of your platform setup before building — see 02_Installation.md for the full platform-preparation checklist.

Two more input scripts exist and are easy to confuse with the driving controls:

PlayerPrefs Key

The controller-type selection persists under one key, consistent with the rest of the settings layer:

Key Type Values Written by
ControllerType int 0 Touch, 1 Gyro, 2 SteeringWheel, 3 Joystick TO_API.SetControllerType()

All other audio and graphics options use sibling keys (AudioVolume, MusicVolume, DrawDistance, Shadows, PP) on the same PlayerPrefs-backed settings layer described in 14_APIReference.md. Because everything routes through TO_API, you can change the controller type from code with TO_API.SetControllerType(1) and the Options UI will reflect it the next time the panel opens.

See Also