Skip to content

VehiclePhysicsProfileAsset — Vehicle Physics Profiles

VehiclePhysicsProfileAsset provides bulk physics overrides for vehicles without requiring asset bundle rebuilds. It extends Asset directly and functions as a companion to VehicleAsset. The profile overrides are applied at runtime to InteractableVehicle instances, modifying root rigidbody properties, wheel collider settings, friction curves, torque multipliers, and drive/brake models. At 441 lines it is a substantial asset class with its own applyTo(InteractableVehicle) method that patches the runtime vehicle.

Source code location: Unturned/Bundles/VehiclePhysicsProfileAsset.cs

Inheritance Chain

Asset
  → VehiclePhysicsProfileAsset

Class Definition

csharp
public class VehiclePhysicsProfileAsset : Asset
{
    public static AssetReference<VehiclePhysicsProfileAsset> defaultProfile_Boat;
    public static AssetReference<VehiclePhysicsProfileAsset> defaultProfile_Car;
    public static AssetReference<VehiclePhysicsProfileAsset> defaultProfile_Helicopter;
    public static AssetReference<VehiclePhysicsProfileAsset> defaultProfile_Plane;

    public float? rootMassOverride;
    public float? rootMassMultiplier;
    public float? rootDragMultiplier;
    public float? rootAngularDragMultiplier;
    public float? carjackForceMultiplier;

    public float? wheelMassOverride;
    public float? wheelMassMultiplier;
    public float? wheelDampingRate;
    public float? wheelStiffnessTractionMultiplier;
    public float? wheelSuspensionForce;
    public float? wheelSuspensionDamper;

    public Friction? forwardFriction;
    public Friction? sidewaysFriction;

    public float? motorTorqueMultiplier;
    public float? motorTorqueClampMultiplier;
    public float? brakeTorqueMultiplier;
    public float? brakeTorqueTractionMultiplier;

    public enum EDriveModel { Front, Rear, All }

    public EDriveModel? wheelDriveModel;
    public EDriveModel? wheelBrakeModel;
}

All physics fields are nullable (float? or struct?). A null value means "no override" — the vehicle's existing value is preserved. This allows profiles to override only specific properties while leaving others untouched.

Default Profiles

Four static default profiles correspond to the engine types:

ProfileGUIDUsed By
defaultProfile_Boat47258d0dcad14cb8be26e24c1ef3449eEEngine.BOAT
defaultProfile_Car6b91a94f01b6472eaca31d9420ec2367EEngine.CAR
defaultProfile_Helicopterbb9f9f0204c4462ca7d976b87d1336d4EEngine.HELICOPTER
defaultProfile_Plane93a47d6d40454335b4784e803628ac54EEngine.PLANE

These profiles are auto-assigned by VehicleAsset.onModelLoaded when:

  1. No explicit Physics_Profile reference is set on the vehicle.
  2. The root rigidbody and wheel colliders have default Unity mass (1.0).
  3. The engine type is not BLIMP or TRAIN.

Root Physics

Field.dat KeyTypeDescription
rootMassOverrideRoot_Massfloat?Exact mass value (overrides current mass)
rootMassMultiplierRoot_Mass_Multiplierfloat?Multiplier on current mass
rootDragMultiplierRoot_Drag_Multiplierfloat?Linear drag multiplier
rootAngularDragMultiplierRoot_Angular_Drag_Multiplierfloat?Angular drag multiplier
carjackForceMultiplierCarjack_Force_Multiplierfloat?Carjack flip force modifier

Mass can be overridden either by exact value or by multiplier. If both are set, the exact override takes precedence (checked first in applyTo):

csharp
if (rootMassOverride.HasValue)
    rootRigidbody.mass = rootMassOverride.Value;
else if (rootMassMultiplier.HasValue)
    rootRigidbody.mass *= rootMassMultiplier.Value;

Wheel Physics

Field.dat KeyTypeDescription
wheelMassOverrideWheel_Massfloat?Exact wheel mass
wheelMassMultiplierWheel_Mass_Multiplierfloat?Wheel mass multiplier
wheelDampingRateWheel_Damping_Ratefloat?Wheel damping rate
wheelStiffnessTractionMultiplierWheel_Stiffness_Traction_Multiplierfloat?Traction multiplier
wheelSuspensionForceWheel_Suspension_Forcefloat?Spring force
wheelSuspensionDamperWheel_Suspension_Damperfloat?Spring damper

Wheel mass is applied with a special condition: wheelMassOverride only applies if the vehicle asset does NOT have its own wheelColliderMassOverride set. This prevents the profile from overriding a vehicle-specific wheel mass setting.

Friction System

The Friction struct defines friction curves for wheel contact:

csharp
public struct Friction
{
    public float extremumSlip;
    public float extremumValue;
    public float asymptoteSlip;
    public float asymptoteValue;
    public float stiffness;
}
Field.dat KeyTypeDescription
forwardFrictionWheel_Friction_ForwardFriction?Acceleration/braking friction
sidewaysFrictionWheel_Friction_SidewaysFriction?Lateral/cornering friction

Friction is parsed from a nested .dat dictionary:

csharp
protected Friction? readFriction(IDatDictionary data, string key)
{
    if (data.ContainsKey(key))
    {
        IDatDictionary frictionReader = data.GetDictionary(key);
        Friction friction = new Friction();
        friction.extremumSlip = frictionReader.ParseFloat("Extremum_Slip");
        friction.extremumValue = frictionReader.ParseFloat("Extremum_Value");
        friction.asymptoteSlip = frictionReader.ParseFloat("Asymptote_Slip");
        friction.asymptoteValue = frictionReader.ParseFloat("Asymptote_Value");
        friction.stiffness = frictionReader.ParseFloat("Stiffness");
        return friction;
    }
    return null;
}

applyTo Method

The friction values are applied to both the custom Wheel struct and the Unity WheelCollider:

csharp
if (sidewaysFriction.HasValue)
{
    tire.stiffnessSideways = sidewaysFriction.Value.stiffness;

    WheelFrictionCurve colliderSidewaysFriction = tire.wheel.sidewaysFriction;
    sidewaysFriction.Value.applyTo(ref colliderSidewaysFriction);
    tire.wheel.sidewaysFriction = colliderSidewaysFriction;
}

Torque and Braking

Field.dat KeyTypeDescription
motorTorqueMultiplierMotor_Torque_Multiplierfloat?Acceleration torque
motorTorqueClampMultiplierMotor_Torque_Clamp_Multiplierfloat?Torque clamp
brakeTorqueMultiplierBrake_Torque_Multiplierfloat?Brake force
brakeTorqueTractionMultiplierBrake_Torque_Traction_Multiplierfloat?Brake traction

These override the per-wheel torque multipliers. The motorTorqueMultiplier scales the drive force applied to powered wheels. The brakeTorqueMultiplier scales braking force. Both are applied per-wheel in the applyTo loop.

Drive and Brake Models

csharp
public enum EDriveModel { Front, Rear, All }
ModelPowered/Brake Distribution
FrontWheel index < 2 (front wheels only)
RearWheel index >= 2 (rear wheels only)
AllAll wheels (4WD / 4-wheel brakes)
csharp
if (wheelDriveModel.HasValue && tire.index >= 0)
{
    switch (wheelDriveModel.Value)
    {
        case EDriveModel.Front: tire.isPowered = tire.index < 2; break;
        case EDriveModel.Rear:  tire.isPowered = tire.index >= 2; break;
        case EDriveModel.All:   tire.isPowered = true; break;
    }
}
Field.dat KeyTypeDescription
wheelDriveModelWheel_Drive_ModelEDriveModel?Which wheels receive torque
wheelBrakeModelWheel_Brake_ModelEDriveModel?Which wheels have brakes

The applyTo(InteractableVehicle) Method

The profile is applied to a live vehicle's physics components:

  1. Root rigidbody: Mass override/multiplier, drag multiplier, angular drag multiplier.
  2. Each wheel (loop through vehicle.tires):
    • Stiffness traction multiplier.
    • Wheel damping rate.
    • Suspension spring force and damper.
    • Sideways friction curve (stiffness + collider curve).
    • Forward friction curve (stiffness + collider curve).
    • Wheel mass override/multiplier (with vehicle.asset.wheelColliderMassOverride guard).
    • Motor torque multiplier.
    • Motor torque clamp multiplier.
    • Brake torque multiplier.
    • Brake torque traction multiplier.
    • Drive model (powered state per wheel).
    • Brake model (hasBrakes state per wheel).

Skip conditions:

  • Wheels with null colliders (purely visual wheels) are skipped.
  • wheelMassOverride is skipped if the vehicle has its own wheelColliderMassOverride.
  • carjackForceMultiplier is applied to the vehicle's field directly, not in the wheel loop.

Conditional Logging

The applyTo method includes extensive logging gated behind the LOG_VEHICLE_PHYSICS_PROFILE preprocessor directive:

csharp
[System.Diagnostics.Conditional("LOG_VEHICLE_PHYSICS_PROFILE")]
private void log(InteractableVehicle vehicle, string format, params object[] args)
{
    UnturnedLog.info(vehicle.asset.name + ": " + format, args);
}

When enabled, every physics change is logged with before/after values for debugging.

PopulateAsset

csharp
public override void PopulateAsset(in PopulateAssetParameters p)
{
    base.PopulateAsset(in p);

    if (p.data.ContainsKey("Root_Mass"))
        rootMassOverride = p.data.ParseFloat("Root_Mass");
    if (p.data.ContainsKey("Root_Mass_Multiplier"))
        rootMassMultiplier = p.data.ParseFloat("Root_Mass_Multiplier");
    // ... same pattern for all fields ...

    sidewaysFriction = readFriction(p.data, "Wheel_Friction_Sideways");
    forwardFriction = readFriction(p.data, "Wheel_Friction_Forward");

    // ... torque, drive model, brake model ...
}

Each field uses ContainsKey for presence detection — the value is only parsed and set if the key exists. Missing keys leave the field as null (no override). This enables sparse profiles that change only a few values.

Use Case: Bulk Tuning Without Bundles

The primary benefit of VehiclePhysicsProfileAsset is that it allows tuning vehicle physics without rebuilding asset bundles. A modder can:

  1. Create a physics profile asset (.dat file with Type=VehiclePhysicsProfile).
  2. Reference it from vehicle assets via Physics_Profile.
  3. Tweak values in the profile file without repacking textures, models, or audio.
  4. Share profiles across multiple vehicles for consistent handling.

Comparison to VehicleAsset Physics Fields

Physics AspectVehicleAssetVehiclePhysicsProfileAsset
MasswheelColliderMassOverride, hasCenterOfMassOverriderootMassOverride, wheelMassOverride, mass multipliers
DragNot configurablerootDragMultiplier, rootAngularDragMultiplier
FrictionNot configurableFull friction curves (forward + sideways)
SuspensionNot configurablewheelSuspensionForce, wheelSuspensionDamper
TorqueNot configurable4 torque/brake multipliers
Drive modelImplicit (front/rear by index)Explicit EDriveModel (Front/Rear/All)

The profile provides granular physics tuning that VehicleAsset itself does not expose. Most fine-tuning should go in the profile; the vehicle asset should define the structural relationships (which wheels exist, which are powered/steered).

Common Issues

  1. Default profile only for mass-1.0 vehicles — If the prefab has a non-default mass (e.g., 500kg), the default profile is not auto-assigned. All physics values remain as set in the prefab.
  2. Wheel mass override guardwheelMassOverride is ignored if the vehicle has its own wheelColliderMassOverride. Use wheelMassMultiplier instead for vehicles with custom wheel mass.
  3. Friction applies stiffness only — The applyTo method sets tire.stiffnessSideways and tire.stiffnessForward from the friction stiffness value, but the collider friction curve from applyTo(ref frictionCurve) only transfers extremumSlip, extremumValue, and asymptoteValue — it does NOT transfer asymptoteSlip (due to a bug retaining frictionCurve.asymptoteSlip).
  4. Null checks on wheel loop — Visual-only wheels (no WheelCollider) are skipped in the loop with if (tire.wheel == null) continue;. This means suspension, friction, and mass modifications don't apply to visual wheels — they affect collider physics only.
  5. Blimp and train excluded — Blimps and trains don't get default profiles. They require explicit Physics_Profile settings or accept prefab defaults.