Events System

RCC_Events is a centralized event hub that fires at every interesting moment in a vehicle's life — spawn, destroy, collision, gear shift, indicator toggle, camera change, and so on. Subscribing to these events is the recommended way to integrate your game code with RCC because it doesn't require modifying any RCC scripts.

This document covers every event, what triggers it, and shows working subscription patterns.

How RCC_Events Works

RCC_Events is a plain static class. Each event is a static C# delegate. You subscribe in OnEnable and unsubscribe in OnDisable:

void OnEnable()  { RCC_Events.OnRCCPlayerSpawned += MyHandler; }
void OnDisable() { RCC_Events.OnRCCPlayerSpawned -= MyHandler; }

void MyHandler(RCC_CarControllerV4 vehicle) {
    Debug.Log("New player vehicle: " + vehicle.name);
}

This is the standard C# event pattern. Always unsubscribe in OnDisable to avoid memory leaks and null-reference exceptions when the subscribing GameObject is destroyed.

Event Categories

The events are grouped into:

Vehicle Lifecycle Events

OnRCCPlayerSpawned

public delegate void onRCCPlayerSpawned(RCC_CarControllerV4 RCC);
public static event onRCCPlayerSpawned OnRCCPlayerSpawned;

Fires after a vehicle is registered as the player. Triggered by RCC.RegisterPlayerVehicle() or by the SceneManager auto-registering a vehicle tagged "Player."

Use cases: bind your HUD to the new vehicle, attach a follow camera, update minimap.

Example: Bind HUD to the spawned player

using UnityEngine;

public class HUDBinder : MonoBehaviour {
    public Text speedometer;
    private RCC_CarControllerV4 currentVehicle;

    void OnEnable()  { RCC_Events.OnRCCPlayerSpawned += BindToVehicle; }
    void OnDisable() { RCC_Events.OnRCCPlayerSpawned -= BindToVehicle; }

    void BindToVehicle(RCC_CarControllerV4 vehicle) {
        currentVehicle = vehicle;
    }

    void Update() {
        if (currentVehicle != null) {
            speedometer.text = $"{currentVehicle.speed:F0} km/h";
        }
    }
}

OnRCCPlayerDestroyed

public delegate void onRCCPlayerDestroyed(RCC_CarControllerV4 RCC);
public static event onRCCPlayerDestroyed OnRCCPlayerDestroyed;

Fires when the active player vehicle is de-registered — either because of RCC.DeRegisterPlayerVehicle() or because the vehicle's OnDestroy was called.

Use cases: clean up HUD bindings, detach the camera, save player state.

OnRCCPlayerCollision

public delegate void onRCCPlayerCollision(RCC_CarControllerV4 RCC, Collision collision);
public static event onRCCPlayerCollision OnRCCPlayerCollision;

Fires inside the player vehicle's OnCollisionEnter. The collision parameter is Unity's standard Collision — you have access to contact points, relative velocity, the other collider, etc.

Use cases: trigger crash audio, subtract health, play damage VFX, track collision count.

Example: Track crashes for a "no-crash" achievement

public class CrashTracker : MonoBehaviour {
    public int crashCount = 0;
    public float crashThresholdSpeed = 5f; // m/s

    void OnEnable()  { RCC_Events.OnRCCPlayerCollision += OnCollision; }
    void OnDisable() { RCC_Events.OnRCCPlayerCollision -= OnCollision; }

    void OnCollision(RCC_CarControllerV4 vehicle, Collision collision) {
        if (collision.relativeVelocity.magnitude > crashThresholdSpeed) {
            crashCount++;
            // achievement failed, etc.
        }
    }
}

AI Lifecycle Events

OnRCCAISpawned

public delegate void onRCCAISpawned(RCC_AICarController RCCAI);
public static event onRCCAISpawned OnRCCAISpawned;

Fires when an AI vehicle becomes active.

Use cases: add to a minimap, populate a hostile list, count active enemies.

OnRCCAIDestroyed

public delegate void onRCCAIDestroyed(RCC_AICarController RCCAI);
public static event onRCCAIDestroyed OnRCCAIDestroyed;

Fires when an AI vehicle is removed.

Use cases: remove from minimap, decrement enemy count, mission completion.

Camera Event

OnBCGCameraSpawned

public delegate void onBCGCameraSpawned(GameObject BCGCamera);
public static event onBCGCameraSpawned OnBCGCameraSpawned;

Fires after the RCC camera is instantiated.

Use cases: attach post-processing, configure FOV, apply custom follow logic.

Scene Management Events

OnBehaviorChanged

public delegate void onBehaviorChanged();
public static event onBehaviorChanged OnBehaviorChanged;

Fires when RCC.SetBehavior() or RCC_SceneManager.Instance.SetBehavior() changes the global driving behavior preset.

Use cases: update UI label to show new mode, play sound, apply secondary tuning.

Example: Show behavior change toast

public class BehaviorToast : MonoBehaviour {
    public Text toastText;
    public float toastDuration = 2f;

    void OnEnable()  { RCC_Events.OnBehaviorChanged += ShowToast; }
    void OnDisable() { RCC_Events.OnBehaviorChanged -= ShowToast; }

    void ShowToast() {
        string name = RCC_Settings.Instance.selectedBehaviorType?.behaviorName ?? "Default";
        toastText.text = $"Behavior: {name}";
        CancelInvoke(nameof(HideToast));
        toastText.enabled = true;
        Invoke(nameof(HideToast), toastDuration);
    }

    void HideToast() {
        toastText.enabled = false;
    }
}

OnVehicleChanged

public delegate void onVehicleChanged();
public static event onVehicleChanged OnVehicleChanged;

Fires when the active player vehicle is swapped (e.g., car selection menu, vehicle change at runtime).

Use cases: refresh HUD bindings, telemetry, AI targets.

Input Events

These fire once each time the corresponding input action is triggered. They're great for one-shot reactions.

OnStartStopEngine

Fires when the engine ignition input is pressed.

Use cases: ignition audio, dashboard "ignition" indicator, custom engine state.

OnLowBeamHeadlights / OnHighBeamHeadlights

Fires when low or high beam input is toggled.

Use cases: HUD indicator update, AI visibility logic.

OnChangeCamera

Fires when the change-camera input is pressed.

Use cases: cycle custom camera modes, update HUD overlay per view, play camera transition sound.

OnIndicatorLeft / OnIndicatorRight / OnIndicatorHazard

Fires when each indicator input is toggled.

Use cases: HUD blinkers, indicator click sound, driving-school style validation.

Example: Play tick-tock sound on indicator toggle

public class IndicatorTickSound : MonoBehaviour {
    public AudioSource tickAudio;

    void OnEnable() {
        RCC_Events.OnIndicatorLeft   += PlayTick;
        RCC_Events.OnIndicatorRight  += PlayTick;
        RCC_Events.OnIndicatorHazard += PlayTick;
    }

    void OnDisable() {
        RCC_Events.OnIndicatorLeft   -= PlayTick;
        RCC_Events.OnIndicatorRight  -= PlayTick;
        RCC_Events.OnIndicatorHazard -= PlayTick;
    }

    void PlayTick() {
        tickAudio.Play();
    }
}

OnInteriorlights

Fires when the interior light input is toggled.

OnGearShiftUp / OnGearShiftDown

Fires when the gearbox shifts up or down.

Use cases: shift audio (custom variations), force feedback, telemetry logging.

OnNGear

public delegate void onNGear(bool state);

Fires when the neutral gear input toggles. state is true when neutral is engaged, false when released.

OnSlowMotion

public delegate void onSlowMotion(bool state);

Fires when slow motion is toggled. state is true when active.

Use cases: scale audio pitch alongside Time.timeScale, trigger cinematic VFX.

OnRecord / OnReplay

Fires when record / replay input is pressed.

Use cases: HUD overlays for record/replay status, disable user controls during playback.

OnLookBack

public delegate void onLookBack(bool state);

Fires when the look-back input changes state. state is true while held.

OnTrailerDetach

Fires when the trailer-detach input is pressed.

Use cases: HUD update, custom trailer-release logic (cargo drops, mission triggers).

Defensive Subscribe Pattern

If your game has multiple scenes and the subscribing GameObject might be created before or after the event firer, use a defensive pattern:

void OnEnable() {
    RCC_Events.OnRCCPlayerSpawned += HandleSpawn;
    // also handle the case where the player is already spawned:
    var current = RCC_SceneManager.Instance.activePlayerVehicle;
    if (current != null) HandleSpawn(current);
}

This way, your handler runs both for vehicles that spawn after you subscribe and for the existing player vehicle if there is one.

Firing Events Yourself

Each event has a corresponding Event_OnXxx static method that fires it:

RCC_Events.Event_OnRCCPlayerSpawned(myVehicle);

You normally don't call these — they're called internally by RCC's controller and SceneManager. But if you're writing a custom integration (e.g., a different way to register player vehicles), you can call them to keep the event system consistent.

Conditional Events (BCG_ENTEREXIT)

Some events only exist when the BCG_ENTEREXIT define is set. These are for the legacy BCG Shared Assets Enter/Exit system, which has been removed in V5. If you see references to OnPlayerEnteredVehicle or OnPlayerExitedVehicle, those are vestiges of the removed system — they won't fire in a stock V5 install.

Common Event Pitfalls

"Subscribed but my handler isn't called"

Most common: you subscribed in Start() instead of OnEnable(). If the event fires during scene load (before Start), you miss it. Use OnEnable and / or query the current state on subscription.

"My handler is called multiple times"

You subscribed multiple times without unsubscribing. Always pair += with a -= in OnDisable.

"NullReferenceException in my handler after scene unload"

You forgot to unsubscribe in OnDisable. After scene change, the static event still holds a reference to your destroyed GameObject's handler — and calling it throws.

"Event fires but the vehicle parameter is null"

If a vehicle is being destroyed at the moment the event fires, it might already be null by the time your handler runs. Always null-check.

Next Steps