NWH Common
Search Results for

    Variable Center of Mass

    NWH.Common.CoM.VariableCenterOfMass applies mass, local center of mass, and inertia settings to the Rigidbody on the same GameObject. It can also include child components that implement IMassAffector.

    Setup

    1. Add VariableCenterOfMass to the physics root. Unity also ensures that a Rigidbody exists.
    2. Choose whether each Rigidbody value comes from Unity or from this component:
      • useDefaultMass
      • useDefaultCenterOfMass
      • useDefaultInertia
    3. When a default option is disabled, configure its corresponding value:
      • baseMass
      • centerOfMass
      • inertiaTensor
    4. Enable useMassAffectors when child IMassAffector components should contribute, and turn useDefaultMass off. With both on, affector mass never reaches the Rigidbody and the component logs a warning when it initializes.

    The centerOfMass value is in the component's local space. The current API has no centerOfMassOffset or autoCalculate field.

    Serialized Fields

    Field Current behavior
    baseMass Base Rigidbody mass before active mass affectors are added
    centerOfMass Base local center of mass when useDefaultCenterOfMass is false
    inertiaTensor Base inertia tensor when useDefaultInertia is false
    dimensions Dimensions detected by Reset and used by the static cuboid CalculateInertia helper
    useDefaultMass Leaves Rigidbody mass under Unity/default ownership
    useDefaultCenterOfMass Leaves Rigidbody center of mass under Unity/default ownership
    useDefaultInertia Leaves Rigidbody inertia under Unity/default ownership
    useMassAffectors Includes active child IMassAffector values in custom calculations
    isDirty Requests recalculation during the next FixedUpdate

    combinedMass, combinedCenterOfMass, and combinedInertiaTensor expose the most recently calculated custom values. They are not replacement setup fields.

    Initialization and Updates

    During Awake, the component:

    1. caches the local Rigidbody;
    2. copies default Rigidbody values into the corresponding base fields when their default flags are enabled;
    3. discovers child IMassAffector implementations, including inactive children; and
    4. calls UpdateAllProperties().

    At runtime, recalculation happens in FixedUpdate only when isDirty is true. The same FixedUpdate also compares centerOfMass, inertiaTensor, baseMass and the total affector mass against the values it last applied, and sets isDirty itself when any of them moved. A script that writes those fields directly therefore takes effect on the next physics step without calling MarkDirty().

    MarkDirty() remains the cheaper and more explicit path, and is still the right call after adding or removing an affector.

    using NWH.Common.CoM;
    using UnityEngine;
    
    public sealed class CenterOfMassSetup : MonoBehaviour
    {
        [SerializeField] private VariableCenterOfMass variableCenterOfMass;
    
        public void SetLocalCenterOfMass(Vector3 localCenter)
        {
            variableCenterOfMass.useDefaultCenterOfMass = false;
            variableCenterOfMass.centerOfMass = localCenter;
            variableCenterOfMass.MarkDirty();
        }
    
        public void RefreshAffectors()
        {
            variableCenterOfMass.GetMassAffectors();
            variableCenterOfMass.MarkDirty();
        }
    
        public void UseFixedCenterOfMass(Vector3 localCenter)
        {
            variableCenterOfMass.useDefaultCenterOfMass = false;
            variableCenterOfMass.useMassAffectors = false;
            variableCenterOfMass.centerOfMass = localCenter;
            variableCenterOfMass.MarkDirty();
        }
    }
    

    Mass Affectors

    When useMassAffectors is enabled:

    • CalculateMass() adds the mass of every active discovered affector to baseMass;
    • CalculateRelativeCenterOfMassOffset() produces their weighted local offset;
    • CalculateCompositeInertiaTensorOffset(...) applies their parallel-axis contributions; and
    • inactive or missing affectors are skipped.

    If affectors are added or removed at runtime, call GetMassAffectors() before marking the component dirty, as shown by RefreshAffectors() in the complete example above. The method both rescans the children and stores the result in affectors, so its return value does not have to be assigned.

    An existing affector that only changes mass does not need to be rediscovered. FixedUpdate watches the affector mass total and recalculates on its own.

    Public Update Methods

    Method Effect
    MarkDirty() Recalculate enabled custom properties on the next physics update
    UpdateAllProperties() Immediately update every property whose default flag is disabled
    UpdateMass() Calculate and assign Rigidbody.mass
    UpdateCoM() Calculate and assign Rigidbody.centerOfMass
    UpdateInertia() Calculate and assign a positive inertia tensor
    UpdateInertia(bool) Obsolete compatibility overload; the argument was never used
    GetMassAffectors() Rescan child IMassAffector implementations, store them in affectors and return them
    GetWorldCenterOfMass() Transform combinedCenterOfMass to world space
    CalculateInertia(Vector3, float) Return cuboid inertia for dimensions and mass
    CalculateCompositeInertiaTensorOffset(Vector3) Correct parallel-axis contribution about a local combined center of mass
    CalculateInertiaTensorOffset(Vector3) Obsolete pre-14.2 calculation; its argument remains ignored and its legacy result is preserved

    Prefer MarkDirty() for ordinary runtime changes so all enabled custom properties update together at the component's normal physics point.

    The obsolete inertia-offset method deliberately does not forward to the corrected calculation: doing so would silently change physics for compiled upgrade code. Migrate explicitly to CalculateCompositeInertiaTensorOffset(variableCenterOfMass.combinedCenterOfMass).

    Default Versus Custom Values

    When a useDefault... flag is true, UpdateAllProperties() deliberately skips that property. Changing baseMass, centerOfMass, or inertiaTensor while its default flag remains enabled does not overwrite the Rigidbody value.

    For a fixed custom center of mass without affectors, use the same sequence as UseFixedCenterOfMass() in the complete example above.

    For Unity-calculated mass and inertia with only a custom center of mass, leave useDefaultMass and useDefaultInertia enabled.

    Visualization

    The component draws the center of mass as a yellow sphere while gizmos are visible. Mass affectors are drawn in cyan. When custom inertia is enabled, colored dimension lines are also drawn.

    Troubleshooting

    A custom value has no effect

    Disable the matching useDefault... flag. The value is picked up on the next physics step.

    A runtime-created affector is ignored

    Call GetMassAffectors(), then MarkDirty().

    Affector mass does not change the Rigidbody

    useDefaultMass is still enabled. Turn it off; the warning logged during initialization names the same cause.

    The center of mass appears in the wrong place

    centerOfMass is local to the GameObject that holds the Rigidbody. GetWorldCenterOfMass() transforms the most recently calculated custom combinedCenterOfMass. When useDefaultCenterOfMass is enabled, use the attached Rigidbody's worldCenterOfMass instead.

    In this article
    Back to top Copyright © NWH - Vehicle Physics, Aerodynamics, Dynamic Water Physics