Search Unity

  1. Unity 2020.1 has been released.
    Dismiss Notice
  2. We are looking for feedback on the experimental Unity Safe Mode which is aiming to help you resolve compilation errors faster during project startup.
    Dismiss Notice
  3. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

From kinematic to dynamic after a collision

Discussion in 'Physics Previews' started by FlorianBernard, Apr 3, 2020.

  1. FlorianBernard

    FlorianBernard

    Joined:
    Jun 9, 2015
    Posts:
    107
    Hey guys,

    I'm new to DOTS and starting to experiment with physics. I'm trying to build a simple wall built from many blocks. Those blocks should be influenced by gravity only after a collision with a cannonball. For that purpose, I'm trying to switch from motion type "Kinematic" to "Dynamic".

    The code below works almost as expected except that, for some reasons I don't understand, the angular velocity of each block remains 0 after being set to Dynamic. Also, and this is very likely for another reason, I would like these block to correctly react to the collision with the cannonball, while at the moment, they just fall straight.

    Any help would be much appreciated :)


    Code (CSharp):
    1. [UpdateAfter(typeof(EndFramePhysicsSystem))]
    2. public class CollisionEventSystem : JobComponentSystem
    3. {
    4.     BuildPhysicsWorld BuildPhysicsWorldSystem;
    5.     StepPhysicsWorld StepPhysicsWorldSystem;
    6.  
    7.     protected override void OnCreate()
    8.     {
    9.         BuildPhysicsWorldSystem = World.GetOrCreateSystem<BuildPhysicsWorld>();
    10.         StepPhysicsWorldSystem = World.GetOrCreateSystem<StepPhysicsWorld>();
    11.     }
    12.  
    13.     [BurstCompile]
    14.     struct CollisionEventDataJob : ICollisionEventsJob
    15.     {
    16.         [ReadOnly] public PhysicsWorld PhysicsWorld;
    17.         public ComponentDataFromEntity<CollisionEventData> CollisionEventDataGroup;
    18.  
    19.         public void Execute(CollisionEvent collisionEvent)
    20.         {
    21.             Entity entity = collisionEvent.Entities.EntityA;
    22.  
    23.             if (CollisionEventDataGroup.Exists(entity))
    24.             {
    25.                 CollisionEvent.Details d = collisionEvent.CalculateDetails(ref PhysicsWorld);
    26.  
    27.                 if (d.EstimatedImpulse > 0f)
    28.                 {
    29.                     var data = CollisionEventDataGroup[entity];
    30.                     data.MotionType = BodyMotionType.Dynamic;
    31.                     CollisionEventDataGroup[entity] = data;
    32.                 }
    33.             }
    34.         }
    35.     }
    36.  
    37.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    38.     {
    39.         var job = new CollisionEventDataJob
    40.         {
    41.             PhysicsWorld = BuildPhysicsWorldSystem.PhysicsWorld,
    42.             CollisionEventDataGroup = GetComponentDataFromEntity<CollisionEventData>()
    43.         };
    44.  
    45.         var jobHandle = job.Schedule(StepPhysicsWorldSystem.Simulation, ref BuildPhysicsWorldSystem.PhysicsWorld, inputDeps);
    46.        
    47.         jobHandle.Complete();
    48.  
    49.         return default;
    50.     }
    51. }
    52.  
    53. [UpdateAfter(typeof(BuildPhysicsWorld))]
    54. public class ChangeMotionTypeSystem : ComponentSystem
    55. {
    56.     protected override void OnUpdate()
    57.     {
    58.         Entities.ForEach((Entity entity, ref CollisionEventData modifier) =>
    59.         {
    60.             if (modifier.MotionType == BodyMotionType.Dynamic)
    61.             {
    62.                 PhysicsMass m = EntityManager.GetComponentData<PhysicsMass>(entity);
    63.                 float desiredDynamicMass = math.rcp(1f);
    64.                
    65.                 if(m.InverseMass != desiredDynamicMass)
    66.                 {
    67.                     m.InverseMass = desiredDynamicMass;
    68.                    
    69.                     PhysicsGravityFactor g = EntityManager.GetComponentData<PhysicsGravityFactor>(entity);
    70.                     g.Value = 1f;
    71.  
    72.                     PostUpdateCommands.AddComponent(entity, m);
    73.                     PostUpdateCommands.AddComponent(entity, g);
    74.                 }
    75.             }
    76.         });
    77.     }
    78. }
    79.  
     
  2. AlanMattano

    AlanMattano

    Joined:
    Aug 22, 2013
    Posts:
    1,240
    Hello,
    Did you try to make the physics script work first in the traditional way and later convert that to DOTS?
     
  3. FlorianBernard

    FlorianBernard

    Joined:
    Jun 9, 2015
    Posts:
    107
    I did not. The way physics events work (using OnCollisionEnter) seems to me so different that I don't think it would really help. Would it?
     
  4. Rory_Havok

    Rory_Havok

    Joined:
    Jun 25, 2018
    Posts:
    70
    Hello,

    The boxes are not rotating because you are only setting InverseMass, you are leaving InverseInertia as zero. However I also suggest a different approach to your code that might be easier - currently you are switching from kinematic to dynamic by adding a PhysicsMass component. You could achieve the same end result by switching from static to dynamic by adding a PhyiscsVelocity component. The advantage being that you can have your PhysicsMass and PhysicsGravityFactor components present with appropriate precalculated values, even while it is static.

    Another small note - you don't need to check if (d.EstimatedImpulse > 0f), the existence of a CollisionEvent implies that there was some impulse applied.

    To you second question - how to make the boxes react "correctly" to the collisions. That is more tricky. Your setup is such that the solver will never apply impulses to the boxes since it sees them as having infinite mass (kinematic or static). The simplest solution is to just make the boxes dynamic with zero gravity factor from the start. So they would just float in place and can be pushed normally. You could then set the gravity factor to 1 in your collision event handler so they fall when anything touches them.

    Other advanced options (only if the above doesn't work):
    - If you need finer control you could look into overriding the masses the solvers see on a pairwise basis by modifying the Jacobians - the sample scenes have examples of that).
    - You could clone the physics world and re-run the simulation step with the original inputs, after changing some boxes to dynamic.

    PS there is a dedicated subforum for DOTS Physics here: https://forum.unity.com/forums/dots-physics.422/
     
    Sima_Havok and FlorianBernard like this.
  5. FlorianBernard

    FlorianBernard

    Joined:
    Jun 9, 2015
    Posts:
    107
    Hi Rory,

    Thank you so much for your reply!

    Actually, I was just trying that - changing only the gravity after a collision - and this indeed seems to be a much better solution actually :)

    Then I thought collision was not even necessary. I could just activate gravity after the block moved by a certain threshold away from its original position and this seems to work great too.

    I now just need to figure out how to handle isolated "islands" but that's a different story :)
     
unityunity