Search Unity

Toggling collision events via script?

Discussion in 'Physics for ECS' started by thelebaron, Mar 22, 2020.

  1. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    857
    I didnt see that the docs or samples cover this, so I was wondering how(if so) would one toggle receiving collision events on/off from code?
     
  2. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
    Collision events can be toggled in 2 places based on the granularity you want. Either (a) they can be enabled in a specific contact jacobian or (b) enabled in the MaterialFlags in the Unity.Physics.Material of a ConvexCollider (referenced in a PhysicsCollider ComponentData)

    (a) If you want an event for a specific collision between only specific bodies, I recommend you check out the ModidyContactJacobians sample. This code for that sample tweaks the Jacobian flags to get different results. For example, it may enable surface velocity by setting
    manifold.JacobianFlags |= JacobianFlags.EnableSurfaceVelocity;
    . You could similiarly call
    manifold.JacobianFlags |= JacobianFlags.EnableCollisionEvent;
    to raise an event for that specific contact manifold.

    (b) A PhysicsCollider component data holds a Collider BlobAssetReference. If the Collider is cast to a ConvexCollider (checking its CollisionType == CollisionType.Convex), then you can get access to its Material and the MaterialFlags associated with that.
    I knocked up some PhysicsColliderExtensions that might be handy, though tbh I haven't fully tested this sample code.

    Code (CSharp):
    1. using Unity.Physics;
    2. using UnityEngine.Assertions;
    3. using static Unity.Physics.Material;
    4. using Material = Unity.Physics.Material;
    5.  
    6. public static class PhysicsColliderExtensions
    7. {
    8.     public static Material GetColliderMaterial(this ref PhysicsCollider collider)
    9.     {
    10.         Material material = Unity.Physics.Material.Default;
    11.         Assert.IsTrue(collider.Value.Value.CollisionType == CollisionType.Convex);
    12.         unsafe
    13.         {
    14.             var colliderPtr = (ConvexCollider*)collider.ColliderPtr;
    15.             material = colliderPtr->Material;
    16.         }
    17.         return material;
    18.     }
    19.     public static void SetColliderMaterial(this ref PhysicsCollider collider, Material newMaterial)
    20.     {
    21.         Material material = Material.Default;
    22.         Assert.IsTrue(collider.Value.Value.CollisionType == CollisionType.Convex);
    23.         unsafe
    24.         {
    25.             var colliderPtr = (ConvexCollider*)collider.ColliderPtr;
    26.             colliderPtr->Material = newMaterial;
    27.         }
    28.     }
    29.     public static void SetColliderMaterialCollisionEvent(this ref PhysicsCollider collider, bool enableCollisionEvents)
    30.     {
    31.         var material = collider.GetColliderMaterial();
    32.         if (enableCollisionEvents)
    33.             material.Flags |= MaterialFlags.EnableCollisionEvents;
    34.         else
    35.             material.Flags &= ~MaterialFlags.EnableCollisionEvents;
    36.  
    37.         collider.SetColliderMaterial(material);
    38.     }
    39. }
    40.  
     
  3. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    857
    Thanks stevee, option b was exactly what I was looking for, thanks for the explanation and especially the code!
     
  4. Interjection

    Interjection

    Joined:
    Jun 18, 2020
    Posts:
    63
    Is it possible to access the Unity.Physics.Material of a Unity.Physics.PhysicsCollider without using
    unsafe
    code?

    I noticed that you can access the CollisionFilter through
    myPhysicsCollider.Value.Value.Filter
    but I can't find anything similar for accessing the Material.
     
  5. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Unfortunately, that is not possible without unsafe code. The reason is that material can be different per instance (say in compound collider, same as filter), but it's not easy to "combine" materials of individual instances to easily have a hierarchy and have one at the top level (in the header), like we do for filters.
     
  6. Interjection

    Interjection

    Joined:
    Jun 18, 2020
    Posts:
    63
    @petarmHavok: Thanks, hope it will be possible in a few years. Maybe at least some helper methods to read-only values.
     
    petarmHavok likes this.
  7. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    So if we want to use the second method and I want the function to be generic for all collider types (BoxCollider, SphereCollider, ConvexCollider, CapsuleCollider, Triangle, Quad, Cylinder, Mesh, etc)... does that mean we have to write GetColliderMaterial() and SetColliderMaterial() functions with big fat switch statements that cover every single collider type?
     
  8. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    It only boils down to 4 colliders - convex, compound, mesh, and terrain. For convex and terrain you can get the material instantly, for mesh and compound you need a leaf collider (which will be convex again). Sphere, box and others are all convex colliders and have the same layout, so the material can be grabbed in the similar way.

    Even further, because all colliders support the same interfaces, you can do something like CharacterControllerUtilities.IsTrigger() method is doing:

    collider.Value.GetLeaf(colliderKey, out ChildCollider leafCollider); // get the leaf collider for the collider, supported even by convex colliders

    Unity.Physics.Material material = UnsafeUtility.AsRef<ConvexColliderHeader>(leafCollider.Collider).Material; // leaf collider is guaranteed to be convex
     
  9. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    I took a look at CharacterControllerUtilities.IsTrigger(), but I'm failing to adapt it to the GetColliderMaterial() function.

    Code (CSharp):
    1. public static Unity.Physics.Material GetColliderMaterial2(in PhysicsCollider collider)
    2. {
    3.     unsafe {
    4.         collider.Value.GetLeaf(colliderKey, out ChildCollider leafCollider);
    5.         return Unity.Collections.LowLevel.Unsafe.UnsafeUtility.AsRef<ConvexColliderHeader>(leafCollider.Collider).Material;
    6.     }
    7. }
    I'm running into two problems...

    1) Where would we get colliderKey in this situation? CharacterControllerUtilities is getting it from a hit collision, but in this case we're not dealing with any collisions or raycasts.

    2) GetLeaf isn't a member of collider.Value. I did find it in collider.Value.Value.GetLeaf(). I don't know how it works in IsTrigger() by using just Value instead of Value.Value. Maybe it's because of different package versions?
     
  10. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    fyi... this is what i'm currently using...

    Code (CSharp):
    1.     public static Unity.Physics.Material GetColliderMaterial(in PhysicsCollider collider)
    2.     {
    3.         unsafe {
    4.             switch (collider.ColliderPtr->Type) {
    5.                 case ColliderType.Box: return ((Unity.Physics.BoxCollider*)collider.ColliderPtr)->Material;
    6.                 case ColliderType.Sphere: return ((Unity.Physics.SphereCollider*)collider.ColliderPtr)->Material;
    7.                 case ColliderType.Convex: return ((Unity.Physics.ConvexCollider*)collider.ColliderPtr)->Material;
    8.                 case ColliderType.Capsule: return ((Unity.Physics.CapsuleCollider*)collider.ColliderPtr)->Material;
    9.                 case ColliderType.Triangle: return ((Unity.Physics.PolygonCollider*)collider.ColliderPtr)->Material;
    10.                 case ColliderType.Quad: return ((Unity.Physics.PolygonCollider*)collider.ColliderPtr)->Material;
    11.                 case ColliderType.Cylinder: return ((Unity.Physics.CylinderCollider*)collider.ColliderPtr)->Material;
    12.                 case ColliderType.Terrain: return ((Unity.Physics.TerrainCollider*)collider.ColliderPtr)->Material;
    13.             }
    14.             // NOTE - MESH AND COMPOUND DON'T HAVE A MATERIAL
    15.         }
    16.         return Unity.Physics.Material.Default;
    17.     }
     
  11. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Yeah I get what you are saying, collider key is there to identify children of compounds/meshes. You can treat all of the convex shapes in your function (other than terrain) in the same manner using the UnsafeUtility.AsRef<ConvexColliderHeader>, for simplicity. But yeah, your function is the way to go.

    Btw, character controller works on BlobAssetReference<Collider>, this is one level of indirection less than PhysicsCollider, thus the extra .Value in your code. It has nothing to do with package versions.