Class MotorcycleModule
Turns a standard VehicleController into a motorcycle by actively balancing it: steering input sets a target lean angle and a PID controller applies the roll torque to reach and hold it. Cornering force comes from the lean angle and is capped at tire grip. Active "point-and-lean" model: no gyroscopic precession, no trail/rake, no true counter-steering (the steered wheel does not move opposite to the turn). The PID stands in for the rider.
Inherited Members
Namespace: NWH.VehiclePhysics2.Modules.MotorcycleModule
Assembly: NWH.VehiclePhysics2.dll
Syntax
[Serializable]
public class MotorcycleModule : VehicleComponent
Remarks
Requires exactly 2 wheels ordered [0] = front, [1] = rear, and no differentials; the transmission outputs straight to the rear wheel. The three PID gains are all multiplied by leanPIDCoefficient before use, so that coefficient works as an overall stability slider. Manual: https://nwhcoding.com/VehiclePhysics/manual/MotorcycleModule.html
Fields
gainDerivative
PID controller derivative gain for lean stabilization.
Declaration
[Tooltip("Lean PID controller derivative gain.")]
public float gainDerivative
Field Value
| Type | Description |
|---|---|
| float |
Remarks
Damps the roll rate. Usable range 0.5 to 2; below that the bike wobbles, above it lean transitions feel sluggish. The P/D ratio sets the roll rate the loop settles at: torque reaches zero when roll rate = (gainProportional / gainDerivative) * lean error, 5 deg/s per degree at the defaults. leanPIDCoefficient scales both, so it cannot change that ratio; lower gainDerivative to allow faster lean transitions.
gainIntegral
PID controller integral gain for lean error correction.
Declaration
[Tooltip("Lean PID controller integral gain.")]
public float gainIntegral
Field Value
| Type | Description |
|---|---|
| float |
Remarks
Removes the steady-state lean offset. Usable range 3 to 8; below that the bike never quite reaches the target lean, above it corrections turn aggressive.
gainProportional
PID controller proportional gain for immediate lean response.
Declaration
[Tooltip("Lean PID controller proportional gain.")]
public float gainProportional
Field Value
| Type | Description |
|---|---|
| float |
Remarks
Immediate response to lean angle error, and the first gain to reach for when tuning. Usable range 2 to 6; below that the bike falls over in turns, above it overcorrects into a magnetic-feeling balance.
gripLimitedCamberThrust
Grip-limit the lean cornering force. When true, the lateral force from lean is tan(lean)*load capped at the tire's peak mu and at the friction ellipse left over after drive/brake force, and the target lean is capped at atan(mu) and at the lean the steered path needs - so cornering saturates and the bike runs wide / washes out on low grip, like a real bike. When false, the legacy arcade injection (sin(lean)*load, never grip-limited, "on rails") is used.
Declaration
[Tooltip("Cap lean cornering force at tire grip (tan(lean), clamped to mu). Off = legacy arcade on-rails feel.")]
public bool gripLimitedCamberThrust
Field Value
| Type | Description |
|---|---|
| bool |
handlebarsTransform
Transform representing the upper forks and handlebars for visual steering animation.
Declaration
[Tooltip("Transform representing the upper forks and handlebars.")]
public Transform handlebarsTransform
Field Value
| Type | Description |
|---|---|
| Transform |
Remarks
Rotated to match the front wheel steer angle. The pivot must sit on the steering axis; the initial local rotation is cached at init and used as the reference.
integralLimitFraction
Integral windup cap as a fraction of maxLeanTorque. Lower removes rubber-band lag/overshoot on quick lean reversals (chicanes) without reducing proportional authority.
Declaration
[Tooltip("Integral windup cap as a fraction of maxLeanTorque.")]
[Range(0.1, 1)]
public float integralLimitFraction
Field Value
| Type | Description |
|---|---|
| float |
leanAngleMaxDelta
Maximum lean angle change rate (degrees/second) at different speeds.
Declaration
[Tooltip("Maximum angle delta in [deg] per [s] for given speed [m/s].")]
public AnimationCurve leanAngleMaxDelta
Field Value
| Type | Description |
|---|---|
| AnimationCurve |
Remarks
X-axis is speed in m/s, Y-axis max lean rate in deg/s. Default is 220 deg/s at 0 m/s falling to 100 deg/s at 90 m/s: a rider can shift weight quickly at walking pace, while at speed gyroscopic forces resist a fast lean change. Raise Y for an arcade feel.
leanAngleSlipCoefficient
Additional lean angle applied based on rear wheel lateral slip.
Declaration
[Tooltip("Lean angle addition given the lateral tire slip.\r\nAllows the motorcycle to lean and slide sideways when drifting,\r\ninstead of highsiding.")]
public float leanAngleSlipCoefficient
Field Value
| Type | Description |
|---|---|
| float |
Remarks
Rear-wheel lateral slip adds corrective lean, standing in for the rider catching a slide. Negative leans into the slide (realistic); positive leans away. -40 to -20 is forgiving, -15 to 0 is realistic but harder. The sum is still capped at atan(mu) when gripLimitedCamberThrust is on, since more lean than grip can balance just falls over. Most of the effect is therefore at part lean, where there is headroom; at full lean there is only a couple of degrees.
leanPIDCoefficient
Global multiplier for all PID gain values.
Declaration
[Tooltip("Global multiplier for all PID gains. Higher = more stable, lower = more realistic.")]
public float leanPIDCoefficient
Field Value
| Type | Description |
|---|---|
| float |
Remarks
Scales all three gains at once, preserving their ratios, so it works as a difficulty slider. 10-30 is light assistance and the bike can fall over, 40-60 balanced, 70-100 arcade-stable.
maxLeanAngle
Maximum achievable lean angle (degrees) at different speeds.
Declaration
[Tooltip("Maximum lean angle [deg] for given speed [m/s]. Runtime-capped by atan(mu) when grip-limited.")]
public AnimationCurve maxLeanAngle
Field Value
| Type | Description |
|---|---|
| AnimationCurve |
Remarks
X-axis is speed in m/s, Y-axis max lean in degrees. Real-world reference: street bikes 35-45 deg, sport 45-55 deg, race 55-65 deg. Default is 43 deg at rest to 40 deg at 90 m/s, a street bike feel. With gripLimitedCamberThrust on it is additionally capped at the lean the steered path actually needs, so a full-lock crawl leans a few degrees rather than 43.
maxLeanTorque
Maximum corrective torque (Nm) the balance system can apply.
Declaration
[Tooltip("Maximum torque the lean controller can apply to the Rigidbody.\r\nToo small value will result in lack of lean control on the vehicle\r\nin extreme cases, but can be more realistic as the motorcycle will be able to\r\nfall over, highside, etc.")]
public float maxLeanTorque
Field Value
| Type | Description |
|---|---|
| float |
Remarks
Saturation limit on PID authority. Size it by mass: 2500-4000 Nm at 150 kg, 4000-6000 Nm at 250 kg, 6000-10000 Nm at 400 kg and up. Set just high enough to cover normal riding, so the bike can still crash in extreme situations.
selfRightStrength
Strength of the optional self-righting torque applied when the bike has fallen past the balance threshold. 0 (default) leaves a fallen bike down (realistic); raise for arcade recovery.
Declaration
[Tooltip("Self-right torque strength once fallen over. 0 = stays down (realistic), higher = arcade recovery.")]
[Range(0, 1)]
public float selfRightStrength
Field Value
| Type | Description |
|---|---|
| float |
slipCompensationDeadZone
Minimum lateral slip value before slip compensation is applied.
Declaration
[Tooltip("Minimum slip value before it affects lean. Filters numerical noise.")]
[Range(0, 0.2)]
public float slipCompensationDeadZone
Field Value
| Type | Description |
|---|---|
| float |
slipCompensationSteeringThreshold
Minimum steering input required before slip compensation affects lean target.
Declaration
[Tooltip("Minimum steering input before slip compensation affects lean. Prevents straight-line drift.")]
[Range(0, 0.3)]
public float slipCompensationSteeringThreshold
Field Value
| Type | Description |
|---|---|
| float |
swingarmTransform
Transform representing the rear swingarm for visual suspension animation.
Declaration
[Tooltip("Transform representing the rear swingarm.")]
public Transform swingarmTransform
Field Value
| Type | Description |
|---|---|
| Transform |
Remarks
LookAt-tracks the rear wheel position so the arm follows suspension travel. The pivot must sit on the swingarm rotation axis.
useHitNormalAsUp
Use ground normal instead of world up for lean angle calculation.
Declaration
[Tooltip("Should the wheel hit normal be used as the up reference when calculating the lean angle?\r\nNormally world up is used, but this can be problematic with loops, leaning track, etc.")]
public bool useHitNormalAsUp
Field Value
| Type | Description |
|---|---|
| bool |
Remarks
On: lean is measured against the ground surface, which is what banked tracks, loops, wall rides and sloped off-road need. Off: measured against world up, only correct on flat tracks or when world-relative lean is wanted.
Properties
AnyWheelGrounded
True when at least one wheel is in contact with the ground. Balance authority keys off this, not isGrounded - a wheelie or stoppie still has a contact patch to react a roll moment against.
Declaration
public bool AnyWheelGrounded { get; }
Property Value
| Type | Description |
|---|---|
| bool |
FrontWheelGrounded
True when the front wheel is in contact with the ground.
Declaration
public bool FrontWheelGrounded { get; }
Property Value
| Type | Description |
|---|---|
| bool |
IsGrounded
Pre-14.0 name of isGrounded.
Declaration
[Obsolete("Renamed to isGrounded.")]
public bool IsGrounded { get; }
Property Value
| Type | Description |
|---|---|
| bool |
IsStoppie
True when only the front wheel is grounded (rear wheel lifted).
Declaration
public bool IsStoppie { get; }
Property Value
| Type | Description |
|---|---|
| bool |
IsWheelie
True when only the rear wheel is grounded (front wheel lifted).
Declaration
public bool IsWheelie { get; }
Property Value
| Type | Description |
|---|---|
| bool |
RearWheelGrounded
True when the rear wheel is in contact with the ground.
Declaration
public bool RearWheelGrounded { get; }
Property Value
| Type | Description |
|---|---|
| bool |
Remarks
This is the driven wheel, so losing contact here also means losing drive.
isGrounded
True when both wheels are in contact with the ground.
Declaration
public bool isGrounded { get; }
Property Value
| Type | Description |
|---|---|
| bool |
Methods
VC_Disable(bool)
Disables the motorcycle module and applies high angular damping to prevent tipping.
Declaration
public override bool VC_Disable(bool calledByParent)
Parameters
| Type | Name | Description |
|---|---|---|
| bool | calledByParent | Whether this was called by the parent component or LOD system. |
Returns
| Type | Description |
|---|---|
| bool | True if successfully disabled, false otherwise. |
Overrides
VC_Enable(bool)
Enables the motorcycle module and removes angular damping for proper lean physics.
Declaration
public override bool VC_Enable(bool calledByParent)
Parameters
| Type | Name | Description |
|---|---|---|
| bool | calledByParent | Whether this was called by the parent component or LOD system. |
Returns
| Type | Description |
|---|---|
| bool | True if successfully enabled, false otherwise. |
Overrides
VC_FixedUpdate(float)
Physics update that applies lean control torque.
Declaration
public override void VC_FixedUpdate(float DeltaTime)
Parameters
| Type | Name | Description |
|---|---|---|
| float | DeltaTime | Fixed timestep delta time in seconds. |
Overrides
VC_Initialize()
Caches the front and rear wheel references, builds the lean PID controller from the configured gains and stores the initial handlebar rotation. Fails if the vehicle does not have exactly two wheels with WheelControllers.
Declaration
protected override void VC_Initialize()
Overrides
VC_Update(float)
Per-frame update for input, visuals and UI. Only called while the component is active. Physics belongs in VC_FixedUpdate, not here.
Declaration
public override void VC_Update(float DeltaTime)
Parameters
| Type | Name | Description |
|---|---|---|
| float | DeltaTime | Time elapsed since last Update in seconds |
Overrides
VC_Validate(VehicleController)
Editor-side configuration check: null references, out-of-range values, missing dependencies. Reports through Debug.LogWarning/LogError.
Declaration
public override void VC_Validate(VehicleController vc)
Parameters
| Type | Name | Description |
|---|---|---|
| VehicleController | vc | Parent VehicleController to validate against |