How the System Works
This document explains RCC's architecture at a level useful for someone who wants to use RCC effectively, not someone who wants to rebuild it from scratch. By the end you'll understand which scripts talk to which, where settings come from, and how data flows from a keyboard press all the way to the wheels turning.
The goal here is to build a mental model. Once you have one, every other document in this set becomes easier to read because you'll know where its pieces fit in.
The 30-Second Version
A typical RCC scene has three actors:
- An
RCC_SceneManager— a singleton that tracks who the active player vehicle is. - One or more
RCC_CarControllerV4components — one per vehicle in the scene. - An
RCC_Camera— follows whichever vehicle the SceneManager says is the player.
Each frame:
RCC_InputManagerpolls the Input System and fills anRCC_Inputsstruct (throttle, brake, steer, etc.).RCC_CarControllerV4reads that struct inFixedUpdate, applies engine torque, gearbox logic, steering angles, suspension forces, and pushes the results into Unity'sWheelCollidersystem.- The wheels move;
RCC_WheelColliderreads the resulting velocity / slip / ground material and updates wheel meshes, audio, particles, and skidmarks. RCC_Camerainterpolates toward the active player vehicle.RCC_Lightupdates light intensity (e.g., brake lights brighten when you brake).- The UI dashboard reads the same
speed,engineRPM, andcurrentGearvalues and updates the gauges.
That's the whole loop. Everything else in RCC is supporting infrastructure: settings, customization, damage, AI, etc.
The Three Singletons You'll Touch
RCC uses several singletons. Most you'll never see directly, but two are important to know.
RCC_SceneManager
One per scene. Created automatically by the menu item Create → Managers → Scene Manager, or auto-spawned the first time RCC_SceneManager.Instance is accessed.
It exposes:
activePlayerVehicle— the currently controlled vehicle.activePlayerCamera— the camera following that vehicle.allVehicles— list of every vehicle in the scene.RegisterPlayer(vehicle)— promote a vehicle to be the active player.DeRegisterPlayer()— drop the current player.SetBehavior(int)— change global driving behavior.ChangeCamera()— cycle camera mode.
You never instantiate the SceneManager yourself — touching RCC_SceneManager.Instance creates one if there isn't one already. But you should still drop the Scene Manager prefab into your scene explicitly via the menu, because that way it appears in the Hierarchy where you can inspect it.
RCC_Settings
A ScriptableObject singleton (not a MonoBehaviour). The asset lives at:
Assets/RealisticCarControllerV4/Resources/RCC Assets/RCC_Settings.asset
It loads itself the first time RCC_Settings.Instance is accessed. During Play mode, RCC clones the asset into a runtime copy — this is intentional so that runtime changes (e.g., behavior switching) don't persist to the asset on disk after you exit Play mode. The first time this surprised someone it was treated as a bug; it's now an explicitly-supported design.
RCC_Settings holds:
- Behavior presets (Simulator, Drift, Arcade, Custom).
- FPS / fixedTimeStep / maxAngularVelocity overrides.
- Layer names for vehicle, wheel, detachable parts.
- Prefab references (camera, canvas, telemetry, exhaust gas particles, lens flare).
- Audio mixer routing and shared audio clips (crash, wind, gear shift).
- Mobile controller type (touch / gyro / steering wheel / joystick).
- Optimization toggles (vertex lights, no particles, no skidmarks).
See 21 — RCC Settings Deep Dive for every field.
Base Class: RCC_Core
Almost every RCC MonoBehaviour inherits from RCC_Core instead of directly from MonoBehaviour. RCC_Core provides two important static accessors:
Settings— gives every component cached access toRCC_Settings.Instancewithout each component reloading the resource itself.GroundMaterials— same forRCC_GroundMaterials.Instance.
It also caches a reference to the parent RCC_CarControllerV4 (CarController) — so any wheel, light, exhaust, or detachable part on a vehicle can call CarController from inside OnEnable and get the controller it belongs to. The lookup walks up the transform hierarchy on first access and caches the result. This is the most common foot-gun for new RCC code: don't re-implement parent lookup, just use CarController.
There are four classes that are not RCC_Core-derived and inherit MonoBehaviour directly: RCC_TruckTrailer, RCC_Telemetry, RCC_CheckUp (static utility), and RCC_GetBounds (static utility). These are grandfathered exceptions — don't add new ones.
The Main Vehicle Controller — RCC_CarControllerV4
This is the heart of RCC. One per vehicle. About 3,700 lines of code. It has roughly 140 serialized fields organized into the following groups:
- Wheels — references to the four (or more) wheel transforms and their corresponding
RCC_WheelColliderchildren. - Steering Wheel — optional 3D steering wheel mesh in the interior, plus rotation axis.
- Drivetrain — FWD / RWD / AWD / BIASED, anti-roll bars, brake torque, downforce.
- Engine — torque curve, RPM range, inertia, rev limiter, exhaust flame.
- Gears — number of gears, gear ratios, automatic/manual, clutch inertia, shift threshold.
- Steering Assistance — limiter, counter-steer, sensitivity (all togglable).
- Stability Assists — ABS, ESP, TCS (all togglable).
- Audio — engine sound layers (one to three audio sources), exhaust audio, transmission audio.
- Damage — embedded
RCC_Damagedata structure (mesh deformation, detachable parts, wheel damage). - Behavior Override — per-vehicle override of the global
RCC_Settingsbehavior preset.
The Inspector groups these into foldouts for clarity. See 08 — Vehicle Inspector Reference for a field-by-field walkthrough.
In code, RCC_CarControllerV4 exposes public methods like:
StartEngine(); // Ignite the engine.
KillEngine(); // Stop the engine.
ShiftToGear(int); // Force a specific gear.
ShiftUp(); // Up one gear.
ShiftDown(); // Down one gear.
OverrideInputs(RCC_Inputs); // Inject custom inputs (used by AI, replay, etc.).
SetCanControl(bool); // Enable / disable player input.
Plus dozens of public properties that other systems (UI, lights, damage) read from each frame.
The Wheels — RCC_WheelCollider
Each wheel on a vehicle is a child GameObject with two components:
- Unity's built-in
WheelCollider— provides the raycast-based suspension physics. - RCC's
RCC_WheelCollider— extends it with friction curves, ground detection, audio, particle dust, skidmarks, anti-roll force application, and steer/power/brake distribution flags.
The "wheel mesh transform" (the visible wheel model) is a separate transform referenced on RCC_CarControllerV4. Each frame, the controller reads the wheel collider's pose and applies it to the mesh transform so the mesh rotates and pitches realistically. This pattern is standard Unity wheel collider usage — RCC just automates the wiring.
Friction comes from the active behavior preset (when overrideBehavior is off on the wheel) or from per-wheel custom curves (when it's on). The actual WheelFrictionCurve values that Unity uses are computed in RCC_WheelCollider.Update from a combination of:
- The behavior preset's
forwardExtremumSlip/forwardExtremumValue/forwardAsymptoteSlip/forwardAsymptoteValue(and the sideways equivalents). - The active ground material's
forwardStiffnessandsidewaysStiffnessmultipliers. - Tire deflation state (if a wheel is flat, stiffness reduces).
See 09 — How WheelColliders Work for the friction math in detail.
Input Flow
Input flows in one direction: from the Input System to the controller. Here's the data path:
[ Keyboard / Gamepad / Touch ]
↓
[ RCC_InputActions.inputactions ] ← the Input System binding asset
↓
[ RCC_InputManager (singleton, Update) ]
↓
[ RCC_Inputs struct: throttle, brake, steer, clutch, ... ]
↓
[ RCC_CarControllerV4.FixedUpdate reads inputs ]
↓
[ WheelColliders apply torque / brake / steer ]
↓
[ Vehicle moves ]
On mobile, the path bypasses the Input Manager:
[ Touch UI buttons / steering wheel / joystick / gyro ]
↓
[ RCC_MobileButtons (static class) ]
↓
[ RCC_InputManager reads mobile values instead of Input System ]
↓
[ Same RCC_Inputs struct as before ]
↓
[ ... same as before ... ]
If you want to drive the car yourself (AI, replay, network), there's a third path that bypasses RCC_InputManager entirely:
[ Your code creates an RCC_Inputs struct ]
↓
[ Your code calls carController.OverrideInputs(myInputs) ]
↓
[ RCC_CarControllerV4 uses your inputs instead of polling the manager ]
See 16 — Input System and 17 — Overriding Inputs.
Events System — RCC_Events
Every interesting moment in a vehicle's life triggers a static event on RCC_Events. Examples:
OnRCCPlayerSpawned(RCC_CarControllerV4 vehicle)— fires when a vehicle is registered as the player.OnRCCPlayerCollision(RCC_CarControllerV4 vehicle, Collision collision)— fires inside the vehicle'sOnCollisionEnter.OnGearShiftUp()— fires when the gearbox upshifts.OnIndicatorLeft()— fires when the left turn signal is toggled.OnStartStopEngine()— fires when the engine ignition is pressed.
This is the preferred way to integrate your game code with RCC. Subscribe to the events you care about in OnEnable, unsubscribe in OnDisable, do your custom logic in the handler. You don't need to modify any RCC scripts — your code lives alongside.
See 24 — Events System for the full list and code examples.
Cameras
The camera system is a per-player single component (RCC_Camera) that has multiple modes:
- TPS — third-person follow camera.
- Hood — first-person from a hood anchor inside the vehicle.
- Wheel — close-up on a wheel for cinematic shots.
- Fixed — a scene-wide singleton anchor point.
- Cinematic — a scene-wide singleton that automatically orbits.
- Top-down — orthographic-style high angle.
You cycle modes with the ChangeCamera input (default C) or by calling RCC.ChangeCamera() in code. RCC_Camera finds the active player vehicle from RCC_SceneManager.activePlayerVehicle automatically.
Lights
Each light on a vehicle is a child GameObject with both a Unity Light component and an RCC_Light component. RCC_Light knows whether it's a headlight, brake light, reverse light, indicator, or interior light, and updates its intensity each frame based on what the parent vehicle is doing:
- Brake lights brighten when
carController.brakeInput > 0. - Reverse lights turn on when
carController.direction == -1. - Indicator lights blink at a configurable interval when the corresponding input is toggled.
RCC_Light also handles render-pipeline differences — light intensity in lumens on HDRP, in unitless on URP, etc. — so you don't need three different vehicle prefabs for the three pipelines.
Damage
Damage is embedded as a field on RCC_CarControllerV4 (not a separate component). The field is damage, of type RCC_Damage, marked [System.Serializable]. Inside it:
- Mesh deformation — uses an octree (
RCC_Octree) to spatially query vertices near a collision contact point and pushes them inward. - Detachable parts — child GameObjects with
RCC_DetachablePartcomponents. On enough damage theirConfigurableJointbreaks, the part falls off, and a physics rigidbody takes over. - Wheel damage — the wheel transform can shift away from its rest position, and at extreme damage the wheel pops off entirely.
- Light damage — at high damage, lights stop emitting.
RCC.Repair(carController) resets all of the above instantly. See 12 — Damage System.
Customization
The Customization system is opt-in. To use it, your vehicle prefab needs an RCC_Customizer component, plus child GameObjects for each sub-manager (PaintManager, SpoilerManager, WheelManager, UpgradeManager, DecalManager, NeonManager, SirenManager, CustomizationManager). Each sub-manager reads from its corresponding Customizer_* data class (paint colors available, spoiler prefabs available, wheel prefabs available, etc.).
Loadouts persist via PlayerPrefs — when the player chooses a new paint or wheel, the change is written immediately. On vehicle spawn, the customizer reads the saved state and applies it.
See 20 — Customization.
AI
The AI driver is RCC_AICarController. It uses the same OverrideInputs() path as any other external controller — internally it computes throttle, brake, and steer values based on the next waypoint, then injects them. You don't need separate "physics for AI" — AI uses the same physics as the player.
Waypoints are placed in a scene under an RCC_AIWaypointsContainer. An AI vehicle's targetContainer field points to that container, and the AI cycles through waypoints in order. Optional RCC_AIBrakeZone triggers tell the AI to slow down for sharp corners.
See 19 — AI System.
Ground Materials
When a wheel touches a surface, RCC_WheelCollider reads the surface's PhysicMaterial (or, for Unity Terrain, the active splatmap layer at that point) and looks it up in RCC_GroundMaterials.asset. The lookup returns a GroundMaterialFrictions entry containing:
forwardStiffnessandsidewaysStiffness— multipliers on the friction curve.slip— base slip target.damp— damping force.volume— audio volume scalar for the wheel's slip sound.groundParticles— particle prefab for dust / dirt / snow effects.groundSound— audio clip for this surface.skidmark— optional custom skidmark style.deflate— if true, tires can deflate on this surface (used for puncturing terrain like nails).
Add a new surface by adding a new entry to RCC_GroundMaterials.asset and assigning the corresponding PhysicMaterial to your collider. See 11 — Ground Physics.
Render Pipelines
RCC works on Built-in, URP, and HDRP via two compile-time defines:
BCG_URP— set when the project uses URP.BCG_HDRP— set when the project uses HDRP.
Sixteen RCC scripts have code paths that branch on these defines. The differences are mostly:
- Light intensity units — Built-in uses unitless intensity, URP uses a similar 0–8 scale, HDRP uses lumens (much larger numbers).
- Lens flare API — Built-in uses
LensFlare, URP usesLensFlareComponentSRP, HDRP also usesLensFlareComponentSRPbut with different defaults. - Decal API — Built-in uses Projector; HDRP uses
DecalProjector.
The Render Pipeline Converter (Tools → BoneCracker Games → Realistic Car Controller → Render Pipeline Converter) handles all of this automatically when you switch pipelines. See 22 — URP & HDRP Conversion.
Things You Should Not Try to Change
A few patterns in RCC are intentional and important — changing them will break compatibility with users' projects and isn't necessary for any normal use case:
- Global namespace. Every class is in the global namespace (no
namespace BoneCrackerGames {}wrapper). This is load-bearing — existing customer projects reference these classes by their unqualified names. RCC_prefix. Every class, every public field, every public method starts withRCC_or is a member of a class that does. This is the asset's identity.- No
.asmdeffiles. All RCC scripts live in the defaultAssembly-CSharp. Adding assembly definitions would speed up compile time but breaks user projects that have already taken dependencies on the monolithic assembly. - Embedded
RCC_Damage(not a separate component). It's a[System.Serializable]field on the controller, not aMonoBehaviour. This is by design — separating it would require touching every existing vehicle prefab.
If you need to extend RCC, subscribe to RCC_Events from your own scripts or use the OverrideInputs() path. Don't modify the RCC scripts directly unless you're prepared to redo your changes on every update.
Where to Go Next
Now that you have the mental model, the rest of the documentation makes more sense. Some good next reads:
- 08 — Vehicle Inspector Reference — every field on the main controller.
- 23 — Scripting API — every public
RCCstatic method. - 24 — Events System — the event hub.
- 16 — Input System — how input is configured.
Or just pick whatever feature you're about to use from the Table of Contents.