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 guard — wheelMassOverride 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.