Stacked Colliders Keep Drifting

I'm working with a stack of box objects (think brick wall) and using the Unity Physics. But the stack of dynamic objects start drifting and then topple. This happens even with the Friction set to 1, and the Restitution set to 0. It's the same behaviour I see in the DOF example.

I was thinking I could try and set the PhysicsVelocity to float3.zero, but that doesn't seem to help with the drifting.

I was wondering during what systems collisions occur, and when the PhysicsVelocity is applied. I assume it's between those two systems that I should set the velocity to zero.

[UpdateAfter ( typeof ( EndFramePhysicsSystem ) )]
unsafe public class BlockCollisionSystem : JobComponentSystem
{
    BuildPhysicsWorld _buildPhysicsWorldSystem;
    StepPhysicsWorld _stepPhysicsWorldSystem;
    EntityQuery TriggerGroup;

    protected override void OnCreate ( )
    {
        _buildPhysicsWorldSystem = World.GetOrCreateSystem<BuildPhysicsWorld> ( );
        _stepPhysicsWorldSystem = World.GetOrCreateSystem<StepPhysicsWorld> ( );

        TriggerGroup = GetEntityQuery ( new EntityQueryDesc
        {
            All = new ComponentType [ ] { typeof ( BlockData ), typeof( PhysicsVelocity), typeof(Translation) }
        } );
    }

    protected override JobHandle OnUpdate ( JobHandle inputDeps )
    {
        var job = new DestroyCollisionsJob
        {
            blockdata = GetComponentDataFromEntity<BlockData> ( ),
            velocity = GetComponentDataFromEntity<PhysicsVelocity> ( ),
            position = GetComponentDataFromEntity<Translation> ( )
        }.Schedule ( _stepPhysicsWorldSystem.Simulation, ref _buildPhysicsWorldSystem.PhysicsWorld, inputDeps );

        return job;
    }

    [BurstCompile]
    struct DestroyCollisionsJob : ICollisionEventsJob
    {
        [ReadOnly] public ComponentDataFromEntity<BlockData> blockdata;
        public ComponentDataFromEntity<PhysicsVelocity> velocity;
        public ComponentDataFromEntity<Translation> position;

        public void Execute ( CollisionEvent collisionEvent )
        {
            var a = collisionEvent.Entities.EntityA;
            var b = collisionEvent.Entities.EntityB;

            if ( velocity.Exists ( a ) && blockdata [ a ].Static == 1 )
            {
                velocity [ a ] = new PhysicsVelocity { Angular = float3.zero, Linear = float3.zero };
            }

            if ( velocity.Exists ( b ) && blockdata [ b ].Static == 1 )
            {
                velocity [ b ] = new PhysicsVelocity { Angular = float3.zero, Linear = float3.zero };
            }
        }
    }
}

Any help with this problem would be hugely appreciated. Thanks.

If you are focused on stacking you should move over to the Havok Physics simulation backend (https://docs.unity3d.com/Packages/com.havok.physics@0.1/manual/quickstart.html).

That said, the sliding comes from the friction model dealing with information in the current frame rather than with Havok Physics caching that information frame to frame. Different design requirements and all that.
In order to cull some of the drift in Unity Physics folk have been checking the linear and angular energy of bodies and setting them to zero if they are below a certain threshold.

The trick to this is to clip these small velocities at the right time in the simulation pipeline. You really need to do this inside the StepPhysicsWorld update, specifically after the Contact Jacobians have been solved but before the velocity changes have been applied. This is where the SimulationCallbacks.Phase.PostSolveJacobians callback comes into. This callback happens just before the simulation integrates the velocities.

[spoiler]

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Systems;


//[DisableAutoCreation]
[AlwaysUpdateSystem]
[UpdateBefore(typeof(StepPhysicsWorld))]
public class ClipMotionSystem : JobComponentSystem
{
    public float LinearThreshold = 1E-4f;
    public float AngularThreshold = 1E-6f;

    StepPhysicsWorld m_stepPhysicsWorld;

    protected override void OnCreate()
    {
        m_stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
    }

    [BurstCompile]
    struct ClipMotionDataJob : IJobParallelFor
    {
        public float linearThreshold;
        public float angularThreshold;
        public float3 gravityDir;

        public NativeSlice<MotionVelocity> MotionVelocities;

        public void Execute(int index)
        {
            var pm = MotionVelocities[index];

            // if inverse mass is larger, resting threshold should be smaller
            float3 linearEnergy = pm.LinearVelocity * pm.LinearVelocity / pm.InverseInertiaAndMass.w;
            float3 angularEnergy = pm.AngularVelocity * pm.AngularVelocity / pm.InverseInertiaAndMass.xyz;

            if (math.lengthsq(linearEnergy) < linearThreshold &&
                math.lengthsq(angularEnergy) < angularThreshold)
            {
                var gravityComponent = math.dot(pm.LinearVelocity, gravityDir) * gravityDir;
                pm.LinearVelocity = gravityComponent;
                pm.AngularVelocity = float3.zero;
            }

            MotionVelocities[index] = pm;
        }
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        SimulationCallbacks.Callback clipMotionsCallback = (ref ISimulation simulation, ref PhysicsWorld world, JobHandle inDeps) =>
        {
            return new ClipMotionDataJob
            {
                gravityDir = math.normalizesafe(GetSingleton<PhysicsStep>().Gravity),
                linearThreshold = LinearThreshold,
                angularThreshold = AngularThreshold,
                MotionVelocities = world.MotionVelocities,
            }.Schedule(world.NumDynamicBodies, 64, inDeps);
        };
        m_stepPhysicsWorld.EnqueueCallback(SimulationCallbacks.Phase.PostSolveJacobians, clipMotionsCallback, inputDeps);

        return inputDeps;
    }
}

[/spoiler]

This works ok for the Pyramids test scene in the samples. You'll likely need to tweak the thresholds for you own simulation. It's by no means perfect, as bodies can be stopped at unrealistic orientations if rotated slowly, but it might be enough for you.

Otherwise, grab Havok Physics from the Package Manager.

2 Likes

Thanks mate, I'll give that a whirl!