Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

How do I use ColliderCast properly?

Discussion in 'Physics for ECS' started by colin_young, Nov 7, 2021.

  1. colin_young


    Jun 1, 2017
    I am trying to convert a raycast vehicle system to use cylinder colliders for the wheels. I cannot figure out how to get the collider from the GO hierarchy and then use it in the physics system. I have my vehicle wheels set up as in the Unity ECS Raycast Vehicle Demo (Vehicle -> Suspension -> Wheel -> Wheel Graphic) and have added a Physics Shape to the wheel graphic:

    Screenshot - editor.jpg

    In my GameObjectConversionSystem script I create a Cylinder Collider:

    Code (CSharp):
    1. var wheelAuthoring = wheelRepresentation.GetComponent<PhysicsShapeAuthoring>();
    2. var wheelGeometry = wheelAuthoring?.GetCylinderProperties();
    4. if (wheelGeometry.HasValue) {
    5.     var filter = new CollisionFilter
    6.     {
    7.         BelongsTo = ~0u,
    8.         CollidesWith = 0u,
    9.         GroupIndex = 0
    10.     };
    11.     var wheelCollider = CylinderCollider.Create(wheelGeometry.Value);
    12.     DstEntityManager.AddComponentData(wheelEntity, new PhysicsCollider { Value = wheelCollider });
    13. }
    In the vehicle physics system, I attempt to use ColliderCast:

    Code (CSharp):
    1. var orientation = quaternion.identity;
    2. var input = new ColliderCastInput()
    3. {
    4.     Collider = collider.ColliderPtr,
    5.     Orientation = orientation,
    6.     Start = rayStart,
    7.     End = rayEnd
    8. };
    10. var colliderHit = world.CastCollider(input, out var rayResult);
    11. var hit = rayResult.Entity == Entity.Null;
    13. var hitBody = world.Bodies[rayResult.RigidBodyIndex];
    14. var hitEntity = hitBody.Entity;
    15. var hitColliderKey = rayResult.ColliderKey.Value;
    17. if (mechanics.drawDebugInformation != 0)
    18. {
    19.     Debug.Log($"Entity: {entity} - Hit: {hitEntity} @ {rayResult.Fraction}, {rayResult.Position}: {hitColliderKey}");
    20.     Debug.DrawRay(rayStart, rayResult.Position - rayStart, Color.magenta, 0.01f);
    21. }
    But the output of the debug always looks like this:

    Entity(70:1) - Hit: Entity(70:1) @ 0, float3(194.284f, -0.04390742f, 171.9057f): 4294967295 

    Either world.Bodies[rayResult.RigidBodyIndex] is returning the current entity, or the collider I am trying to cast is colliding with itself. If I try to set up a filter on the collider when I am converting, things blow up almost immediately (my vehicle is launched into space and the colliders claim to be colliding with things well outside of the parameters of the cast length), but at least that confirms my suspicion that it seems to be colliding with itself, along with the fraction = 0 of the result.

    I am certain I am missing something fundamental here. This seems like a fairly standard use-case (i.e. one wants to create a hierarchy in the editor, assign physics shapes and then use those shapes in collision tests), but I can't find a simple, straightforward example that does that. Can anyone point out what I've missed, or what I need to do to get this fairly simple scenario working?
    Last edited: Nov 7, 2021
    mcroswell and Botaurus like this.
  2. thelebaron


    Jun 2, 2013
  3. CookieStealer2


    Jun 25, 2018
    Here's an example of a collector that can ignore entities:
    Code (CSharp):
    2. public struct AnyHitWithIgnoreCollector<T> : ICollector<T> where T : struct, IQueryResult
    3. {
    4.     public bool EarlyOutOnFirstHit => true;
    5.     public float MaxFraction { get; }
    6.     public int NumHits => 0;
    7.     public FixedList32<Entity> entitiesToIgnore;
    8.     public AnyHitWithIgnoreCollector(float maxFraction, FixedList32<Entity> entitiesToIgnore) : this()
    9.     {
    10.         this.MaxFraction = maxFraction;
    11.         this.entitiesToIgnore = entitiesToIgnore;
    12.     }
    13.     public AnyHitWithIgnoreCollector(float maxFraction, Entity entityToIgnore) : this()
    14.     {
    15.         MaxFraction = maxFraction;
    16.         entitiesToIgnore = new FixedList32<Entity>();
    17.         entitiesToIgnore.Add(entityToIgnore);
    18.     }
    19.     public AnyHitWithIgnoreCollector(float maxFraction, Entity entityToIgnore1, Entity entityToIgnore2) : this()
    20.     {
    21.         MaxFraction = maxFraction;
    22.         entitiesToIgnore = new FixedList32<Entity>();
    23.         entitiesToIgnore.Add(entityToIgnore1);
    24.         entitiesToIgnore.Add(entityToIgnore2);
    25.     }
    26.     public bool AddHit(T hit)
    27.     {
    28.         if (entitiesToIgnore.Contains(hit.Entity)) return false;
    29.         Assert.IsTrue(hit.Fraction < MaxFraction);
    30.         return true;
    31.     }
    32. }
    mcroswell likes this.
  4. colin_young


    Jun 1, 2017
    Thanks! Late last night I discovered I was using PhysicsWorld, not ColliderWorld to run the CastCollider. Fixing that and adding GroupIndex = -1 solved the self-collision problem.

    But is that really how we are intended to do it? Is self-colliding really the default? These questions are more directed to Unity I suppose, since only they know what the intended use is. That just doesn't seem sensible to me. In my case, when I'm converting prefab instances to entities, if I want wheels of different vehicles to collide with each other, I need to assign a unique GroupId to each vehicle? And what if I want the wheels on a vehicle to be able to collide with each other? I need to assign an id that is unique within a single vehicle and across all vehicles? I can't help but feel like I've done something wrong or am missing something.

    In any case, I am now colliding with the ground correctly. I just need to figure out why my normal forces are all zero now, and, as a result, there is no traction to drive the vehicle.
    mcroswell, Botaurus and Clonkex like this.
  5. Botaurus


    Feb 20, 2013
    I ran into this problem too and it was pretty confusing.
    in Unity's Collector.cs I found this collector:

    Code (CSharp):
    1. // A collector used to provide filtering for QueryInteraction enum and a specified entity (usually used for self-hit filtering).
    2.     // This is a wrapper of the user provided collector, which serves to enable
    3.     // filtering based on the QueryInteraction parameter.
    4.     internal unsafe struct QueryInteractionCollector<T, C> : ICollector<T>
    5.         where T : struct, IQueryResult
    6.         where C : struct, ICollector<T>
    Would be great for this to be included in the docs since it's a problem most people are likely to encounter, and the solution exists but is somewhat hidden.
    mcroswell and JosepMariaPujol like this.
  6. JosepMariaPujol


    Unity Technologies

    Jun 16, 2021
    Hey @Botaurus, thanks for your feedback I will open a new user request regarding the matter and likely include this on the physics documentation page.