Architecture Overview
NWH Vehicle Physics 2 divides vehicle simulation into components coordinated by VehicleController.
Component Hierarchy
The components are arranged as follows:
VehicleController (Main Hub)
├── Powertrain
│ ├── EngineComponent (power generation)
│ ├── ClutchComponent (power transfer control)
│ ├── TransmissionComponent (gear ratios)
│ ├── DifferentialComponent(s) (torque distribution)
│ └── WheelComponent(s) (power sinks → WheelController)
├── Steering (angle calculations, speed-dependent reduction)
├── Brakes (torque application, brake distribution)
├── Input
│ └── VehicleInputHandler
│ ├── InputSystemVehicleInputProvider
│ ├── InputManagerVehicleInputProvider
│ └── MobileVehicleInputProvider
├── EffectManager
│ ├── LightsManager (headlights, brake, indicators)
│ ├── SkidmarkManager (tire marks)
│ ├── SurfaceParticleManager (dust, dirt, spray)
│ └── Exhausts (smoke, flash)
├── SoundManager
│ ├── EngineRunningComponent
│ ├── EngineStartStopComponent
│ ├── TransmissionWhineComponent
│ ├── WheelSkidComponent
│ ├── WheelTireNoiseComponent
│ └── [other sound components]
├── GroundDetection (surface type identification)
├── DamageHandler (collision damage)
└── ModuleManager
├── ABSModule
├── TCSModule
├── ESCModule
├── CruiseControlModule
├── ArcadeModule
├── MotorcycleModule
├── TrailerModule / TrailerHitchModule
├── AerodynamicsModule
├── NOSModule
└── [15+ optional modules]
VehicleComponent Lifecycle
All vehicle systems share the lifecycle defined by VehicleComponent. The VC_ method prefix avoids conflicts with Unity callbacks.
Lifecycle Stages
┌─────────────────────────────────────┐
│ INITIALIZATION │
└─────────────────────────────────────┘
│
VC_Initialize(VehicleController vc)
Sets parent reference
│
VC_Initialize()
Component-specific setup
│
VC_LoadStateFromStateSettings()
Apply LOD-based initial state
│
┌─────────────────────────────────────┐
│ RUNTIME LOOP │
└─────────────────────────────────────┘
│
┌───────────────┴───────────────┐
│ │
VC_Enable() VC_Disable()
Component active Component inactive
│ │
├───────────────────────────────┤
│ │
VC_FixedUpdate(dt) │
Physics updates │
│ │
VC_Update(dt) │
Visual updates │
│ │
└───────────────────────────────┘
Lifecycle Methods
| Method | When Called | Purpose |
|---|---|---|
VC_Initialize(vc) |
Once on startup | Sets VehicleController reference |
VC_Initialize() |
Once on startup | Component-specific initialization |
VC_SetDefaults() |
On first setup | Apply default configuration values |
VC_Validate(VehicleController vc) |
Editor/runtime | Validate component configuration |
VC_LoadStateFromStateSettings() |
On state change | Apply LOD-based state |
VC_Enable() |
When enabled | Activate component |
VC_Disable() |
When disabled | Deactivate component |
VC_FixedUpdate(dt) |
Each FixedUpdate | Physics calculations |
VC_Update(dt) |
Each Update | Visual/audio updates |
Example Implementation
using NWH.VehiclePhysics2;
public class CustomComponent : VehicleComponent
{
protected override void VC_Initialize()
{
base.VC_Initialize();
// Setup code here
}
public override bool VC_Enable(bool calledByParent)
{
base.VC_Enable(calledByParent);
// Called when component becomes active
return true;
}
public override void VC_FixedUpdate(float dt)
{
base.VC_FixedUpdate(dt);
if (!IsActive) return;
// Physics calculations here
}
public override void VC_Update(float dt)
{
base.VC_Update(dt);
if (!IsActive) return;
// Visual updates here
}
}
Update Order
Execution-order attributes
The current runtime uses these Unity execution-order attributes:
| Order | Type | Current responsibility |
|---|---|---|
| 90 | VehicleController |
Dispatch vehicle component updates and attach the powertrain callback |
| 100 | WheelController |
Register wheel instances before manager processing |
| 110 | WheelControllerManager |
Run the grouped wheel substep loop |
Physics flow
VehicleController.FixedUpdate() iterates its cached Components list and calls
VC_FixedUpdate(fixedDeltaTime) on each active component in list order. It does not expose a
fixed category sequence such as ground detection, input, powertrain, steering, and brakes.
Powertrain integration occurs through VehicleController.OnBeforeSubstep(), which the
WheelControllerManager invokes before each wheel substep for that Rigidbody group. The manager
then advances ground detection, suspension, friction, static-friction coordination, and final
Rigidbody state for the group. See the Wheel Controller
manager guide for
that current pipeline.
Render and LOD flow
VehicleController.Update() iterates the same active component list and calls
VC_Update(deltaTime). VehicleController has no LateUpdate() lifecycle stage. LOD distance
checks run separately in LODCheckCoroutine() at approximately 0.2-second intervals.
Physics Substepping
The powertrain and wheel physics run at a higher frequency than Unity's FixedUpdate through the substepping system.
How Substepping Works
Unity FixedUpdate (60Hz / 0.01667s)
│
└── WheelControllerManager
├── Substep 0 (0.00417s)
│ ├── Powertrain calculates torques
│ └── Wheel calculates forces
│
├── Substep 1 (0.00417s)
│ ├── Powertrain responds to wheel state
│ └── Wheel recalculates with new torque
│
├── Substep 2 (0.00417s)
│ └── ...
│
└── Substep 3 (0.00417s)
└── Final forces applied to Rigidbody
Configuration
The effective physics rate is calculated as:
Effective Rate = (1 / fixedDeltaTime) * substepCount
Example:
- Fixed Timestep: 0.01667s (60Hz desktop, recommended)
- Substeps: 4
- Effective Rate: 60 * 4 = 240Hz
See WheelControllerManager in the WheelController3D documentation for more on substepping.
State System (LOD)
StateSettings enables or disables components by distance to reduce simulation cost.
LOD Levels
| Level | Distance | Description |
|---|---|---|
| LOD 0 | Close | Full simulation, all effects/sounds |
| LOD 1 | Medium | Reduced quality, simplified physics |
| LOD 2 | Far | Minimal simulation, no effects |
| LOD 3 | Very Far | Component sleeping, minimal overhead |
State Transitions
Distance increases →
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ LOD 0 │ → │ LOD 1 │ → │ LOD 2 │ → │ LOD 3 │
│ Full │ │ Reduced │ │ Minimal │ │ Sleep │
└─────────┘ └─────────┘ └─────────┘ └─────────┘
← Distance decreases
Per-Component State
Each VehicleComponent has a StateDefinition controlling its enabled state and LOD index. These are configured through the StateSettings ScriptableObject.
See StateSettings and LOD for configuration details.
Input System Architecture
The input system uses a shared registry with separate provider families for vehicle controls and scene controls.
Input Flow
Hardware Input
│
├──────────────── vehicle branch ────────────────┐
│ ▼
│ VehicleInputProviderBase instance(s) -> VehicleInputHandler
│ ├── InputSystemVehicleInputProvider │
│ ├── InputManagerVehicleInputProvider ▼
│ ├── MobileVehicleInputProvider VehicleInputStates
│ └── FFBInputProvider (optional sample) ├── throttle (0-1)
│ ├── brakes (0-1)
│ ├── steering (-1 to 1)
│ ├── clutch (0-1)
│ ├── handbrake (0-1)
│ ├── shiftUp/shiftDown (bool)
│ └── other vehicle inputs
│
└───────────────── scene branch ──────────────────────┐
▼
SceneInputProviderBase instance(s) -> camera, UI, and vehicle switching
├── InputSystemSceneInputProvider
├── InputManagerSceneInputProvider
└── MobileSceneInputProvider
Input Provider Types
| Provider family | Shipped implementations | Consumer |
|---|---|---|
VehicleInputProviderBase |
InputSystemVehicleInputProvider, InputManagerVehicleInputProvider, MobileVehicleInputProvider |
Per-vehicle VehicleInputHandler |
SceneInputProviderBase |
InputSystemSceneInputProvider, InputManagerSceneInputProvider, MobileSceneInputProvider |
Scene camera, UI, and vehicle-switching systems |
VehicleInputProviderBase sample |
FFBInputProvider in the optional DirectInput FFB sample |
Per-vehicle VehicleInputHandler |
Module System
Modules are optional vehicle features that can be added, removed, or configured independently.
Module Categories
Driver Assistance:
- ABSModule - Anti-lock Braking System
- TCSModule - Traction Control System
- ESCModule - Electronic Stability Control
- CruiseControlModule - Speed maintenance
- SpeedLimiterModule - Maximum speed enforcement
Vehicle Types:
- MotorcycleModule - Lean physics
- ArcadeModule - Simplified physics
Features:
- TrailerModule / TrailerHitchModule - Towing
- AerodynamicsModule - Downforce
- NOSModule - Nitrous boost
- FuelModule - Fuel consumption
- FlipOverModule - Auto-recovery
- MetricsModule - Performance tracking
- RiggingModule - Visual bone animation
- AirSteerModule - In-air steering
Module Lifecycle
Modules follow the same VehicleComponent lifecycle but are managed through ModuleManager:
Partial example (assumes an existing VehicleController vc reference):
// Access module through wrapper component
var absWrapper = vc.GetComponent<ABSModuleWrapper>();
if (absWrapper != null)
{
ABSModule absModule = absWrapper.module;
absModule.VC_Enable(false); // Enable module
absModule.VC_Disable(false); // Disable module
bool isActive = absModule.IsActive;
}
Design Patterns Used
Component Pattern
All vehicle systems inherit from VehicleComponent for consistent lifecycle management.
Provider Pattern
Input providers can be swapped to support different platforms.
Manager Pattern
Each manager coordinates a group of related components:
ModuleManager- Optional modulesSoundManager- Audio playbackEffectManager- Visual effectsWheelControllerManager- Physics substepping
Observer Pattern
These events report state changes:
onLODChanged- LOD level transitions (on VehicleController)onVehicleChanged- Active vehicle switching (on VehicleChanger, in NWH.Common)onActiveVehicleChanged- Global vehicle tracking (static, on Vehicle base class in NWH.Common)
State Pattern
LOD system manages component states based on distance.
Best Practices
Initialization
- Use
VC_Initialize()for setup, not Unity'sAwake()orStart() - Access other components only after initialization
- Use
VC_SetDefaults()for default values, not field initializers
Updates
- Physics calculations go in
VC_FixedUpdate(dt) - Visual updates go in
VC_Update(dt) - Always check
if (!IsActive) return;at the start
Performance
- Enable LOD system for multiple vehicles
- Disable unused modules
- Use appropriate substep count for platform
- Pool custom effects where appropriate. The built-in skidmark generator creates its own GameObjects and meshes rather than using an object pool.
Debugging
- Enable gizmos to visualize wheel positions and forces
- Inspect runtime wheel values in the
WheelControllerDebug tab; the Base Sample also includes its own telemetry UI - Check Unity Profiler for bottlenecks
- Recommended Fixed Timestep: 0.01667s (60Hz) desktop, 0.0333s (30Hz) mobile
Related Documentation
- VehicleController - Main controller component
- VehicleComponent - Base component class
- Powertrain - Powertrain system
- WheelControllerManager (WheelController3D package) - Physics substepping
- StateSettings - LOD configuration
- ModuleManager - Module system