Check if two colliders intersect for ignore collision feature?

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:

var colliderLookup = GetComponentDataFromEntity<PhysicsCollider>(true);
var localToWorldData = GetComponentDataFromEntity<LocalToWorld>(true);

Entities
    .WithBurst()
    .WithReadOnly(colliderLookup)
    .WithReadOnly(localToWorldData)
    .ForEach((Entity entity, PhysicsCollider collider, ref IgnoreCollision ignore) =>
    {
            if (ignore.Entity == Entity.Null)
            {
                Debug.Log("returning");
                return;
            }

            var other = colliderLookup[ignore.Entity];
            var intersects = collider.Value.Value.CalculateDistance(new ColliderDistanceInput
            {
                Collider = other.ColliderPtr,
                MaxDistance = 10,
                Transform = new RigidTransform(math.mul(
                    math.inverse(localToWorldData[entity].Value),
                    localToWorldData[ignore.Entity].Value
                ))
            });

            if (!intersects)
            {
                Debug.Log("unignoring");
                ignore.Entity = Entity.Null;
            }
            else
            {
                Debug.Log("still intersecting");
            }
    })
    .Schedule();

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

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

unsafe class ClearIgnoreCollisionSystem : SystemBase
{
    protected override void OnUpdate()
    {
        var colliderLookup = GetComponentDataFromEntity<PhysicsCollider>(true);
        var localToWorldData = GetComponentDataFromEntity<LocalToWorld>(true);

        Entities
            .WithBurst()
            .WithReadOnly(colliderLookup)
            .WithReadOnly(localToWorldData)
            .ForEach((Entity entity, PhysicsCollider collider, ref IgnoreCollision ignore) =>
            {
                    if (ignore.Entity == Entity.Null)
                    {
                        Debug.Log("returning");
                        return;
                    }

                    var other = colliderLookup[ignore.Entity];
                    var intersects = collider.Value.Value.CalculateDistance(new ColliderDistanceInput
                    {
                        Collider = other.ColliderPtr,
                        MaxDistance = 10,
                        Transform = new RigidTransform(math.mul(
                            math.inverse(localToWorldData[entity].Value),
                            localToWorldData[ignore.Entity].Value
                        ))
                    });

                    if (!intersects)
                    {
                        Debug.Log("unignoring");
                        ignore.Entity = Entity.Null;
                    }
                    else
                    {
                        Debug.Log("still intersecting");
                    }
            })
            .Schedule();
    }
}

public struct IgnoreCollision : IComponentData
{
    public Entity Entity;

    public IgnoreCollision(Entity entity)
    {
        Entity = entity;
    }
}

[UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
[UpdateBefore(typeof(StepPhysicsWorld))]
public class IgnoreCollisionSystem : SystemBase
{
    StepPhysicsWorld _stepPhysicsWorld;
    SimulationCallbacks.Callback _jacobianModificationCallback;

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

        _jacobianModificationCallback = (ref ISimulation simulation, ref PhysicsWorld world, JobHandle inDeps) => new ModifyJacobiansJob
            {
                Ignored = GetComponentDataFromEntity<IgnoreCollision>(true)
            }
            .Schedule(simulation, ref world, inDeps);

        RequireForUpdate(GetEntityQuery(new EntityQueryDesc
        {
            All = new ComponentType[] {typeof(IgnoreCollision)}
        }));
    }

    protected override void OnUpdate()
    {
        if (_stepPhysicsWorld.Simulation.Type == SimulationType.NoPhysics)
            return;

        _stepPhysicsWorld.EnqueueCallback(SimulationCallbacks.Phase.PostCreateContactJacobians, _jacobianModificationCallback, Dependency);
    }

    [BurstCompile]
    struct ModifyJacobiansJob : IJacobiansJob
    {
        [ReadOnly]
        public ComponentDataFromEntity<IgnoreCollision> Ignored;

        // triggers
        public void Execute(ref ModifiableJacobianHeader h, ref ModifiableTriggerJacobian j)
        {
        }

        public void Execute(ref ModifiableJacobianHeader jacHeader, ref ModifiableContactJacobian contactJacobian)
        {
            var entityA = jacHeader.EntityA;
            var entityB = jacHeader.EntityB;

            if (Ignored.HasComponent(entityA) && Ignored[entityA].Entity == entityB ||
                Ignored.HasComponent(entityB) && Ignored[entityB].Entity == entityA)
            {
                jacHeader.Flags |= JacobianFlags.Disabled;
            }
        }
    }
}

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. :slight_smile: In general, check collision type of both colliders, and then do a convex vs composite always, not composite vs convex.

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?

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…

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?

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.

It does :slight_smile:

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.

1 Like

IsTrigger has been removed in latest versions, you now have the CollisionResponse flag which you can set to “Raise Trigger Events”.

1 Like