On this page
Reference

How RCCP is built

Goal: understand where RCCP's parts live and how they work together. Start with the short overview below; the implementation tables are for developers extending RCCP.

To build a car in the Inspector, use Vehicle Setup. To tune its handling, use How the Car Drives.

The short version

Part Think of it as…
Vehicle root The top object for one car, carrying its main controller and Rigidbody.
Module A component with one job, such as engine sound or steering input.
Scene manager The scene's record of which vehicle the player is driving.
Shared settings Project-wide defaults; runtime copies allow changes while playing.
Public API Methods your script can call to ask RCCP to do something.
Event A notification your script can receive when something happens.

Follow these three rules when writing code:

  1. Get modules through the car controller, for example car.Engine. Avoid depending on their names or exact positions in the Hierarchy.
  2. Drive through the input API. This lets the drivetrain, tires and assists participate.
  3. Use runtime settings copies for changes that should last only while playing.

Check: you know whether your change belongs to one car, the scene, or shared settings. Use the sections below when you need the implementation details.

Choose a topic

The component model

A vehicle is one RCCP_CarController on the root GameObject plus subsystem components on child objects. Each subsystem finds the controller above it and registers itself into a named slot. This lookup is automatic; vehicle setup still includes assigning wheels and connecting differentials to axles.

Base classes provide shared behavior for concrete components. You add the concrete component, such as an Engine, rather than an abstract base class such as RCCP_Component.

Developer detail: base types
Base class Used by What it provides
RCCP_GenericComponent Scene-level managers Cached RCCPSettings, RCCPGroundMaterials, RCCPSceneManager
RCCP_Singleton<T> (extends RCCP_GenericComponent) RCCP_SceneManager, RCCP_InputManager, RCCP_SkidmarksManager An Instance accessor that finds an existing instance or creates a GameObject for one
RCCP_MainComponent RCCP_CarController [SelectionBase], [DisallowMultipleComponent], the cached Rigid, and every subsystem slot
RCCP_Component (implements IRCCP_Component) Every vehicle subsystem Auto-discovery of the parent controller, plus Register()
RCCP_UpgradeComponent (extends RCCP_Component) Customization parts Loadout, Save(), Load() through the vehicle's RCCP_Customizer
RCCP_UIComponent Canvas components Cached RCCPSettings and RCCPSceneManager

RCCP_Component is deliberately not [DisallowMultipleComponent]. Unity applies that attribute to a type and its subtypes, which made two different subclasses mutually exclusive on one GameObject — AddComponent silently returned null. The attribute sits on individual concrete components that are genuinely one-per-GameObject instead.

How a component registers itself

RCCP_Component.CarController is a lazy property. On first access it runs GetComponentInParent<RCCP_CarController>(true) and, if it finds one, calls Register(carController, this).

Register is a type switch. It assigns the component into the matching slot on the controller:

Developer detail: registration table and hierarchy layouts
Component Lands in
RCCP_Engine CarController.Engine
RCCP_Clutch CarController.Clutch
RCCP_Gearbox CarController.Gearbox
RCCP_Differential Triggers CarController.UpdateDifferentials(), which re-scans into Differentials
RCCP_Axles CarController.AxleManager
RCCP_Axle Appended to AxleManager.Axles
RCCP_WheelCollider No slot — a wheel is reached through its axle
RCCP_AeroDynamics CarController.AeroDynamics
RCCP_Audio CarController.Audio
RCCP_Input CarController.Inputs
RCCP_Lights CarController.Lights
RCCP_Light Lights.RegisterLight(...)
RCCP_Stability CarController.Stability
RCCP_Damage CarController.Damage
RCCP_DamageMechanics CarController.DamageMechanics
RCCP_DeformationSolver CarController.DeformationSolver
RCCP_Particles CarController.Particles
RCCP_Customizer CarController.Customizer
RCCP_Lod CarController.LOD
RCCP_Modules CarController.ModulesManager
RCCP_OtherAddons CarController.OtherAddonsManager

Eleven addon components register one level deeper, into OtherAddonsManager: RCCP_Recorder → .Recorder, RCCP_Exhausts → .Exhausts, RCCP_Limiter → .Limiter, RCCP_Nos → .Nos, RCCP_TrailerAttacher → .TrailAttacher, RCCP_Visual_Dashboard → .Dashboard, RCCP_Exterior_Cameras → .ExteriorCameras, RCCP_AI → .AI, RCCP_WheelBlur → .WheelBlur, RCCP_FuelTank → .FuelTank, RCCP_BodyTilt → .BodyTilt.

Two failure modes are worth knowing. An addon whose OtherAddonsManager is missing disables itself rather than throwing, and so does an axle with no axle manager. And a component with no RCCP_CarController anywhere in its parents logs an error and disables itself — except RCCP_Light, RCCP_TrailerAttacher and RCCP_WheelSlipParticles, which are allowed to live outside a vehicle.

Two hierarchy layouts, both supported

Vehicles created by current versions keep every module inside one RCCP_Modules container under the root. Older vehicles keep the modules directly under the root. RCCP_Modules holds no settings and runs no code; it is organizational only, and it deliberately carries no execution order because nothing registers into it.

Because a module can be at either depth, never look one up by path. transform.Find("RCCP_Engine") works on a flat vehicle and returns null on a grouped one. Read the slot instead (carController.Engine), or use GetComponentInChildren<T>().

The slot getters resolve through ResolveModule<T>(), which searches two levels below the vehicle root, then — if the container exists — two levels below the container. That is what makes both layouts behave identically.

Where the torque goes

Power travels through five stages, each one passing an RCCP_Output object carrying two floats: NM (torque in Newton-meters) and RPM.

Stage Component What it does with the output
1 RCCP_Engine Produces torque from RPM and throttle, then invokes outputEvent
2 RCCP_Clutch ReceiveOutput stores it; its own FixedUpdate applies clutch slip and re-fires
3 RCCP_Gearbox Multiplies by the current gear ratio and re-fires
4 RCCP_Differential Splits torque left/right and calls RCCP_Axle.ReceiveOutput(left, right)
5 RCCP_Axle Pushes per-wheel torque into the wheel accumulators

Each stage stores the incoming value, then processes it during its own FixedUpdate (Unity's physics update). RCCP orders these updates from engine to wheel so the result can travel through the chain during the same physics step.

RCCP_Differential sets isPower on the axle it is connected to; you do not set that flag yourself. The other three axle flags — isSteer, isBrake, isHandbrake — are authored per axle.

Developer detail: wheel torque

The accumulator pipeline at the wheel

RCCP_WheelCollider does not receive torque directly. It exposes additive accumulators — AddMotorTorque, AddBrakeTorque, AddHandbrakeTorque, AddEngineBrakeTorque, AddNegativeFeedback — that anything may add into during a fixed frame. The wheel consumes them in its own FixedUpdate, writes the result into Unity's WheelCollider, and resets them for the next frame.

That is why the last three orders are load-bearing. The axle fills the accumulators at -2, stability modifies them at -1 (ESP brake torque added, motor torque cut), the wheel consumes them at 0. Reverse any two and motor torque and ESP brake torque fight on the same driven wheel.

Execution order

Every order-sensitive class carries a [DefaultExecutionOrder] attribute, so the ordering holds even in a project where the editor helper is absent. RCCP_ScriptExecutionOrderManager is an [InitializeOnLoad] editor class that writes the same values into each script's MonoImporter on every assembly reload, so the Project Settings list agrees with the attributes.

Developer detail: execution-order table
Order Components Why here
-50 RCCP_SceneManager, RCCP_InputManager, RCCP_SkidmarksManager Singletons must exist before anything reads them
-13 RCCP_ReplayDirector Pins recorded poses and feeds recorded input before every input writer
-12 RCCP_AIDynamicObstacleAvoidance Produces steer and brake corrections before the AI consumes them at -11
-11 RCCP_AI, RCCP_Recorder Call Inputs.OverrideInputs(...) before the controller polls at -10
-10 RCCP_CarController Main controller
-8 RCCP_Limiter, RCCP_DamageMechanics Write Engine.cutFuel and Engine.damageTorqueMultiplier before the engine reads them
-7 RCCP_Engine Starts the torque chain
-6 RCCP_Clutch
-5 RCCP_Gearbox, RCCP_Axles, RCCP_Lights, RCCP_Exhausts, RCCP_OtherAddons Parent containers must register before their children; none of them has a FixedUpdate, so sharing the slot with the gearbox is safe
-4 RCCP_Differential
-3 RCCP_AeroDynamics Writes Rigidbody.centerOfMass and linearDamping before the suspension solver reads them
-2 RCCP_Axle Fills the wheel accumulators
-1 RCCP_Stability, RCCP_ReplayLatePhase Modifies the accumulators; ABS, ESP and TCS cuts
0 RCCP_WheelCollider Consumes the accumulators and applies them
5 RCCP_Camera Runs after the vehicle has moved
10 RCCP_Customizer, RCCP_Lod, RCCP_BodyTilt, RCCP_DeformationSolver Visual and late work; the deformation solver writes render meshes only

Components left at the default 0 are the order-insensitive ones, including RCCP_Audio, RCCP_DetachablePart and RCCP_CrashCamera.

The net effect is that input-to-wheel-torque latency is bounded to a single fixed frame. At undefined order the same chain pipelines across five.

Ordering your own component

If your component… Give it an order of
Pushes input through Inputs.OverrideInputs(...) -11 or earlier
Implements a new physics subsystem Study the accumulator order above; ordinary driving scripts should use the input API
Reads settled vehicle state and does not write physics 0 or later

Events

Use events when your script needs to react to RCCP. For example, a race manager might listen for a car spawning, while a score system might listen for an impact.

Subscribe in OnEnable and unsubscribe in OnDisable. The events are static, so a subscription can outlive the scene object that created it if you do not remove it.

Check: enabling your component adds its listener once, and disabling it removes that listener. The API Reference contains the subscription example and the complete event/signature tables.

Vehicle and AI lifecycle notifications are separate. Collision notifications also differ: some happen on every contact, while the gameplay-impact event filters small or repeated hits. Choose the event by its description in the reference rather than by its name alone.

Settings, and why you get a copy

RCCP_Settings, RCCP_GroundMaterials and RCCP_ChangableWheels are ScriptableObjects loaded from Resources. Reading .Instance gives you the asset on disk, and writing to it in Play Mode persists after you stop.

RCCP_RuntimeSettings is a static class that hands out ScriptableObject.Instantiate clones instead, created on first access. The base-class accessors RCCPSettings, RCCPGroundMaterials and RCCPChangableWheels use these copies. Use .Instance in editor code only. RCCP_RuntimeSettings.Clear() clears the static references so later access can create fresh copies. Components that already cached a copy still hold that reference; it is not a way to refresh every live component.

The scene-level singletons work the other way: RCCP_SceneManager.Instance, RCCP_InputManager.Instance and RCCP_SkidmarksManager.Instance search the scene and, finding nothing, create a GameObject named after the type and add the component to it.

  • The Public API — the facade methods for spawning, controlling and transporting vehicles, which is usually what you want before reaching for a component directly.
  • Controlling Vehicles from Code — a scripted driving example and input ownership rules.
  • Field Reference — every setting on every component, with its default and range.
  • How the Car Drives — the same drivetrain chain described for someone tuning it rather than coding against it.
BoneCracker Games · Realistic Car Controller ProBack to top ↑
Document details

Generated from: 28_reference_architecture.md
Date: 2026-09-16 02:11
Generated by md-to-html