Search Unity

Stacked Colliders Keep Drifting

Discussion in 'Physics for ECS' started by VOTRUBEC, Nov 14, 2019.

  1. VOTRUBEC

    VOTRUBEC

    Joined:
    Dec 17, 2014
    Posts:
    106
    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.


    Code (CSharp):
    1.  
    2.  
    3. [UpdateAfter ( typeof ( EndFramePhysicsSystem ) )]
    4. unsafe public class BlockCollisionSystem : JobComponentSystem
    5. {
    6.     BuildPhysicsWorld _buildPhysicsWorldSystem;
    7.     StepPhysicsWorld _stepPhysicsWorldSystem;
    8.     EntityQuery TriggerGroup;
    9.  
    10.     protected override void OnCreate ( )
    11.     {
    12.         _buildPhysicsWorldSystem = World.GetOrCreateSystem<BuildPhysicsWorld> ( );
    13.         _stepPhysicsWorldSystem = World.GetOrCreateSystem<StepPhysicsWorld> ( );
    14.  
    15.         TriggerGroup = GetEntityQuery ( new EntityQueryDesc
    16.         {
    17.             All = new ComponentType [ ] { typeof ( BlockData ), typeof( PhysicsVelocity), typeof(Translation) }
    18.         } );
    19.     }
    20.  
    21.     protected override JobHandle OnUpdate ( JobHandle inputDeps )
    22.     {
    23.         var job = new DestroyCollisionsJob
    24.         {
    25.             blockdata = GetComponentDataFromEntity<BlockData> ( ),
    26.             velocity = GetComponentDataFromEntity<PhysicsVelocity> ( ),
    27.             position = GetComponentDataFromEntity<Translation> ( )
    28.         }.Schedule ( _stepPhysicsWorldSystem.Simulation, ref _buildPhysicsWorldSystem.PhysicsWorld, inputDeps );
    29.  
    30.         return job;
    31.     }
    32.  
    33.     [BurstCompile]
    34.     struct DestroyCollisionsJob : ICollisionEventsJob
    35.     {
    36.         [ReadOnly] public ComponentDataFromEntity<BlockData> blockdata;
    37.         public ComponentDataFromEntity<PhysicsVelocity> velocity;
    38.         public ComponentDataFromEntity<Translation> position;
    39.  
    40.         public void Execute ( CollisionEvent collisionEvent )
    41.         {
    42.             var a = collisionEvent.Entities.EntityA;
    43.             var b = collisionEvent.Entities.EntityB;
    44.  
    45.             if ( velocity.Exists ( a ) && blockdata [ a ].Static == 1 )
    46.             {
    47.                 velocity [ a ] = new PhysicsVelocity { Angular = float3.zero, Linear = float3.zero };
    48.             }
    49.  
    50.             if ( velocity.Exists ( b ) && blockdata [ b ].Static == 1 )
    51.             {
    52.                 velocity [ b ] = new PhysicsVelocity { Angular = float3.zero, Linear = float3.zero };
    53.             }
    54.         }
    55.     }
    56. }

    Any help with this problem would be hugely appreciated. Thanks.
     
    Last edited: Nov 14, 2019
  2. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
    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.

    Code (CSharp):
    1. using Unity.Burst;
    2. using Unity.Collections;
    3. using Unity.Entities;
    4. using Unity.Jobs;
    5. using Unity.Mathematics;
    6. using Unity.Physics;
    7. using Unity.Physics.Systems;
    8.  
    9.  
    10. //[DisableAutoCreation]
    11. [AlwaysUpdateSystem]
    12. [UpdateBefore(typeof(StepPhysicsWorld))]
    13. public class ClipMotionSystem : JobComponentSystem
    14. {
    15.     public float LinearThreshold = 1E-4f;
    16.     public float AngularThreshold = 1E-6f;
    17.  
    18.     StepPhysicsWorld m_stepPhysicsWorld;
    19.  
    20.     protected override void OnCreate()
    21.     {
    22.         m_stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
    23.     }
    24.  
    25.     [BurstCompile]
    26.     struct ClipMotionDataJob : IJobParallelFor
    27.     {
    28.         public float linearThreshold;
    29.         public float angularThreshold;
    30.         public float3 gravityDir;
    31.  
    32.         public NativeSlice<MotionVelocity> MotionVelocities;
    33.  
    34.         public void Execute(int index)
    35.         {
    36.             var pm = MotionVelocities[index];
    37.  
    38.             // if inverse mass is larger, resting threshold should be smaller
    39.             float3 linearEnergy = pm.LinearVelocity * pm.LinearVelocity / pm.InverseInertiaAndMass.w;
    40.             float3 angularEnergy = pm.AngularVelocity * pm.AngularVelocity / pm.InverseInertiaAndMass.xyz;
    41.  
    42.             if (math.lengthsq(linearEnergy) < linearThreshold &&
    43.                 math.lengthsq(angularEnergy) < angularThreshold)
    44.             {
    45.                 var gravityComponent = math.dot(pm.LinearVelocity, gravityDir) * gravityDir;
    46.                 pm.LinearVelocity = gravityComponent;
    47.                 pm.AngularVelocity = float3.zero;
    48.             }
    49.  
    50.             MotionVelocities[index] = pm;
    51.         }
    52.     }
    53.  
    54.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    55.     {
    56.         SimulationCallbacks.Callback clipMotionsCallback = (ref ISimulation simulation, ref PhysicsWorld world, JobHandle inDeps) =>
    57.         {
    58.             return new ClipMotionDataJob
    59.             {
    60.                 gravityDir = math.normalizesafe(GetSingleton<PhysicsStep>().Gravity),
    61.                 linearThreshold = LinearThreshold,
    62.                 angularThreshold = AngularThreshold,
    63.                 MotionVelocities = world.MotionVelocities,
    64.             }.Schedule(world.NumDynamicBodies, 64, inDeps);
    65.         };
    66.         m_stepPhysicsWorld.EnqueueCallback(SimulationCallbacks.Phase.PostSolveJacobians, clipMotionsCallback, inputDeps);
    67.  
    68.         return inputDeps;
    69.     }
    70. }
    71.  

    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.
     
    suzuke and florianhanke like this.
  3. VOTRUBEC

    VOTRUBEC

    Joined:
    Dec 17, 2014
    Posts:
    106
    Thanks mate, I'll give that a whirl!