Search Unity

Check if two colliders intersect for ignore collision feature?

Discussion in 'Physics for ECS' started by Bas-Smit, Oct 15, 2020.

  1. Bas-Smit

    Bas-Smit

    Joined:
    Dec 23, 2012
    Posts:
    274
    I'm working on a simple ignore collision system. I trimmed down the ModidyContactJacobians sample, instead using a component containing an entity to ignore but the general idea is the same.

    As soon as the two colliders are no longer intersecting I want to unignore the pair, but Im not quite sure how to do it, this is what I have currently:

    Code (CSharp):
    1.  
    2. var colliderLookup = GetComponentDataFromEntity<PhysicsCollider>(true);
    3. var localToWorldData = GetComponentDataFromEntity<LocalToWorld>(true);
    4.  
    5. Entities
    6.     .WithBurst()
    7.     .WithReadOnly(colliderLookup)
    8.     .WithReadOnly(localToWorldData)
    9.     .ForEach((Entity entity, PhysicsCollider collider, ref IgnoreCollision ignore) =>
    10.     {
    11.             if (ignore.Entity == Entity.Null)
    12.             {
    13.                 Debug.Log("returning");
    14.                 return;
    15.             }
    16.  
    17.             var other = colliderLookup[ignore.Entity];
    18.             var intersects = collider.Value.Value.CalculateDistance(new ColliderDistanceInput
    19.             {
    20.                 Collider = other.ColliderPtr,
    21.                 MaxDistance = 10,
    22.                 Transform = new RigidTransform(math.mul(
    23.                     math.inverse(localToWorldData[entity].Value),
    24.                     localToWorldData[ignore.Entity].Value
    25.                 ))
    26.             });
    27.  
    28.             if (!intersects)
    29.             {
    30.                 Debug.Log("unignoring");
    31.                 ignore.Entity = Entity.Null;
    32.             }
    33.             else
    34.             {
    35.                 Debug.Log("still intersecting");
    36.             }
    37.     })
    38.     .Schedule();
    39.  
    Am I on the right track? I get an exception from Distance.ColliderCollider:

    case CollisionType.Composite:
    case CollisionType.Terrain:
    // no support for composite query shapes
    SafetyChecks.ThrowNotImplementedException();


    EDIT: I'm guessing the problem is the gameobject hierarchy used for conversion has two colliders (one used as a trigger) and I should find the correct leaf before calculating the distance:

    GetLeaf(ColliderKey key, out ChildCollider leaf)


    Im not sure how to get the key though.


    Here's the full code

    Code (CSharp):
    1. using Unity.Burst;
    2. using Unity.Burst;
    3. using Unity.Collections;
    4. using Unity.Entities;
    5. using Unity.Jobs;
    6. using Unity.Mathematics;
    7. using Unity.Physics;
    8. using Unity.Physics.Systems;
    9. using Unity.Transforms;
    10. using UnityEngine;
    11.  
    12. unsafe class ClearIgnoreCollisionSystem : SystemBase
    13. {
    14.     protected override void OnUpdate()
    15.     {
    16.         var colliderLookup = GetComponentDataFromEntity<PhysicsCollider>(true);
    17.         var localToWorldData = GetComponentDataFromEntity<LocalToWorld>(true);
    18.  
    19.         Entities
    20.             .WithBurst()
    21.             .WithReadOnly(colliderLookup)
    22.             .WithReadOnly(localToWorldData)
    23.             .ForEach((Entity entity, PhysicsCollider collider, ref IgnoreCollision ignore) =>
    24.             {
    25.                     if (ignore.Entity == Entity.Null)
    26.                     {
    27.                         Debug.Log("returning");
    28.                         return;
    29.                     }
    30.  
    31.                     var other = colliderLookup[ignore.Entity];
    32.                     var intersects = collider.Value.Value.CalculateDistance(new ColliderDistanceInput
    33.                     {
    34.                         Collider = other.ColliderPtr,
    35.                         MaxDistance = 10,
    36.                         Transform = new RigidTransform(math.mul(
    37.                             math.inverse(localToWorldData[entity].Value),
    38.                             localToWorldData[ignore.Entity].Value
    39.                         ))
    40.                     });
    41.  
    42.                     if (!intersects)
    43.                     {
    44.                         Debug.Log("unignoring");
    45.                         ignore.Entity = Entity.Null;
    46.                     }
    47.                     else
    48.                     {
    49.                         Debug.Log("still intersecting");
    50.                     }
    51.             })
    52.             .Schedule();
    53.     }
    54. }
    55.  
    56. public struct IgnoreCollision : IComponentData
    57. {
    58.     public Entity Entity;
    59.  
    60.     public IgnoreCollision(Entity entity)
    61.     {
    62.         Entity = entity;
    63.     }
    64. }
    65.  
    66. [UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
    67. [UpdateBefore(typeof(StepPhysicsWorld))]
    68. public class IgnoreCollisionSystem : SystemBase
    69. {
    70.     StepPhysicsWorld _stepPhysicsWorld;
    71.     SimulationCallbacks.Callback _jacobianModificationCallback;
    72.  
    73.     protected override void OnCreate()
    74.     {
    75.         _stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
    76.  
    77.         _jacobianModificationCallback = (ref ISimulation simulation, ref PhysicsWorld world, JobHandle inDeps) => new ModifyJacobiansJob
    78.             {
    79.                 Ignored = GetComponentDataFromEntity<IgnoreCollision>(true)
    80.             }
    81.             .Schedule(simulation, ref world, inDeps);
    82.  
    83.         RequireForUpdate(GetEntityQuery(new EntityQueryDesc
    84.         {
    85.             All = new ComponentType[] {typeof(IgnoreCollision)}
    86.         }));
    87.     }
    88.  
    89.     protected override void OnUpdate()
    90.     {
    91.         if (_stepPhysicsWorld.Simulation.Type == SimulationType.NoPhysics)
    92.             return;
    93.  
    94.         _stepPhysicsWorld.EnqueueCallback(SimulationCallbacks.Phase.PostCreateContactJacobians, _jacobianModificationCallback, Dependency);
    95.     }
    96.  
    97.     [BurstCompile]
    98.     struct ModifyJacobiansJob : IJacobiansJob
    99.     {
    100.         [ReadOnly]
    101.         public ComponentDataFromEntity<IgnoreCollision> Ignored;
    102.  
    103.         // triggers
    104.         public void Execute(ref ModifiableJacobianHeader h, ref ModifiableTriggerJacobian j)
    105.         {
    106.         }
    107.  
    108.         public void Execute(ref ModifiableJacobianHeader jacHeader, ref ModifiableContactJacobian contactJacobian)
    109.         {
    110.             var entityA = jacHeader.EntityA;
    111.             var entityB = jacHeader.EntityB;
    112.  
    113.             if (Ignored.HasComponent(entityA) && Ignored[entityA].Entity == entityB ||
    114.                 Ignored.HasComponent(entityB) && Ignored[entityB].Entity == entityA)
    115.             {
    116.                 jacHeader.Flags |= JacobianFlags.Disabled;
    117.             }
    118.         }
    119.     }
    120. }
    121.  
     
    Last edited: Oct 15, 2020
  2. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    So you are trying to calculate distance between a composite shape and another shape. Since this is not supported, you can try to do the inverse, calculate distance between that other shape and the composite. That will work unless both are composite. :) In general, check collision type of both colliders, and then do a convex vs composite always, not composite vs convex.
     
  3. Bas-Smit

    Bas-Smit

    Joined:
    Dec 23, 2012
    Posts:
    274
    thanks, that solves the exception. Is this the recommended way to check if two Colliders intersect though? If the distance is negative this means they intersect?

    The composit collider is made up of two capsules, one set up to be a trigger. The convex collider is set up to interact with both. I would like to exclude the trigger, should I drill down to the non trigger leaf collider to ensure it is not included in CalculateDistance? And if so how would I obtain the key to get to the leaf collider?
     
  4. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Why would you want to exclude the trigger? You don't want events from it? I mean, you won't collide with the trigger anyway, it will only raise events, you can filter those if you need to... Maybe I'm missing something here...
     
  5. Bas-Smit

    Bas-Smit

    Joined:
    Dec 23, 2012
    Posts:
    274
    Let me give some more context, I might be barking up the wrong tree. I have a character that needs to be able to pick up and throw things. The character has a tight fitting capsule collider, and a larger capsule trigger. If an object enters the trigger it is picked up. The problem is that when throwing the object starts inside the collider. For this reason I add the IgnoreCollision component to the object and set Entity to the entity containing the collider (the trigger events I already filter). In the IJacobiansJob I set the Disabled flag for the pair. So far so good. I am looking for a good way to know when to clear the IgnoreCollision component. Ideally this would happen the first frame the pair no longer intersect. This is why I'm trying to check if two colliders currently intersect.

    I could try using the IsTrigger flag instead and clear the component when no trigger events were generated? Or maybe there is a better way altogether?
     
    Last edited: Oct 16, 2020
  6. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Ok so you need some sort of collision filtering, but only for a certain amount of time. I'd say rely on the trigger events, and in the first frame you don't collect the trigger event just stop ignoring the collision.

    Alternatively, since you have a compound, I can give you more advice. CompoundCollider has Children property, which gives you access to all children. In your case, 2 capsules, one is the trigger, the other one isn't. You can access these as Children[0] and Children[1], and after that a .Collider access gives you your capsule. That's all collider key does anyway, but for a general compound collider. When you know exactly what's in there, feel free to use direct access.

    Hope this helps.
     
  7. Bas-Smit

    Bas-Smit

    Joined:
    Dec 23, 2012
    Posts:
    274
    It does :)

    I tried using the IsTrigger flag, assuming it would set the collision response to raise trigger events, but I did not receive any. I also found no usages of IsTrigger oddly enough.

    In the end I added a trigger the size of the collider and went back to the Disable flag which does work.
     
    petarmHavok likes this.
  8. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    IsTrigger has been removed in latest versions, you now have the CollisionResponse flag which you can set to "Raise Trigger Events".
     
    Bas-Smit likes this.