Search Unity

How to Detect Collisions with Unity.Physics ?

Discussion in 'DOTS Physics' started by MixGrey, Sep 11, 2019.

  1. MixGrey

    MixGrey

    Joined:
    Dec 23, 2014
    Posts:
    9
    Some outdated examples show the use of a "CollisionEvents" from StepPhysicsWorld, but this is no longer accessible. Supposedly an ICollisionEventsJob can be used, but it doesn't seem to do anything.

    The physics simulation in my project works, the objects bounce off of each other, and my CollisionSystem runs, but my CollisionJob never gets any collision events.

    Code (CSharp):
    1. [UpdateAfter(typeof(StepPhysicsWorld))]
    2.     [UpdateBefore(typeof(EndFramePhysicsSystem))]
    3.     public class CollisionSystem : JobComponentSystem
    4.     {
    5.  
    6.         struct CollisionJob : ICollisionEventsJob
    7.         {
    8.             [ReadOnly] public PhysicsWorld physicsWorld;
    9.  
    10.             public void Execute(CollisionEvent ev) // this is never called
    11.             {
    12.                 Entity a = physicsWorld.Bodies[ev.BodyIndices.BodyAIndex].Entity;
    13.                 Entity b = physicsWorld.Bodies[ev.BodyIndices.BodyBIndex].Entity;
    14.                 Debug.Log($"collision event: {ev}. Entities: {a}, {b}");
    15.             }
    16.         }
    17.  
    18.  
    19.         BuildPhysicsWorld buildPhysicsWorldSystem;
    20.         StepPhysicsWorld stepPhysicsWorld;
    21.         EndFramePhysicsSystem endFramePhysicsSystem;
    22.  
    23.         protected override void OnCreate()
    24.         {
    25.             buildPhysicsWorldSystem = World.GetOrCreateSystem<BuildPhysicsWorld>();
    26.             stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
    27.         }
    28.        
    29.         protected override JobHandle OnUpdate(JobHandle inputDeps)
    30.         {
    31.             Debug.Log($"collision system running: {Time.time}"); // this runs correctly
    32.  
    33.             inputDeps = JobHandle.CombineDependencies(inputDeps, buildPhysicsWorldSystem.FinalJobHandle);
    34.             inputDeps = JobHandle.CombineDependencies(inputDeps, stepPhysicsWorld.FinalJobHandle);
    35.            
    36.             var physicsWorld = buildPhysicsWorldSystem.PhysicsWorld;
    37.  
    38.             var collisionJob = new CollisionJob
    39.             {
    40.                 physicsWorld = physicsWorld
    41.             };
    42.             JobHandle collisionHandle = collisionJob.Schedule(stepPhysicsWorld.Simulation, ref physicsWorld, inputDeps);
    43.  
    44.             return collisionHandle;
    45.  
    46.         } // OnUpdate
    47.  
    48.     } // System
    I want some way to get pairs of entities from collision events every frame. How can this be done?
     
  2. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    669
    Did you enable "Raises Collision Events" under Advanced in the PhysicsShape options?
     
  3. MixGrey

    MixGrey

    Joined:
    Dec 23, 2014
    Posts:
    9
    I have no PhysicsShape objects. Checking the docs, it looks like PhysicsShape is a MonoBehaviour, which I presume is for hybrid ECS. This is pure ECS and the objects have no relation to GameObjects.

    Is there another way to access that setting for pure ECS?
     
    hilljoh6 likes this.
  4. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    669
    PhysicsShape is a monobehaviour authoring component which gets converted to a pure ecs representation. The examples repo has more examples of its use which I highly suggest taking a look at, as well as a collision event example.
    https://github.com/Unity-Technologies/EntityComponentSystemSamples/tree/master/UnityPhysicsSamples

    If you are creating the colliders from code well at least that script should contain where Raises Collision Events gets turned into an ecs component property
     
  5. schaefsky

    schaefsky

    Joined:
    Aug 11, 2013
    Posts:
    70
    Could you point me to the collision event example?
     
  6. thelebaron

    thelebaron

    Joined:
    Jun 2, 2013
    Posts:
    669
    schaefsky likes this.
  7. MixGrey

    MixGrey

    Joined:
    Dec 23, 2014
    Posts:
    9
    I think I figured it out. When creating the Collider, there's a field for a Unity.Physics.Material which has a field for a Unity.Physics.Material.MaterialFlags, which is an enum. One of the options is EnableCollisionEvents. I'm not sure what the other two are.

    Creating the Collider like this leads to CollisionEvents actually being generated.

    Code (CSharp):
    1. var material = new Unity.Physics.Material
    2.             {
    3.                 CustomTags = Unity.Physics.Material.Default.CustomTags,
    4.                 Flags = Unity.Physics.Material.MaterialFlags.EnableCollisionEvents |
    5.                     Unity.Physics.Material.MaterialFlags.EnableMassFactors |
    6.                     Unity.Physics.Material.MaterialFlags.EnableSurfaceVelocity,
    7.                 Friction = Unity.Physics.Material.Default.Friction,
    8.                 FrictionCombinePolicy = Unity.Physics.Material.Default.FrictionCombinePolicy,
    9.                 Restitution = Unity.Physics.Material.Default.Restitution,
    10.                 RestitutionCombinePolicy = Unity.Physics.Material.Default.RestitutionCombinePolicy,
    11.             };
    12.  
    13.             var boxCollider = Unity.Physics.BoxCollider.Create(
    14.                 new BoxGeometry{
    15.                     Orientation=quaternion.identity,
    16.                     Size=new float3(0.25f,0.25f,0.25f)},
    17.                 CollisionFilter.Default,
    18.                 material
    19.             );
    Then:
    var colliderComponent = new PhysicsCollider { Value = boxCollider };

    EntityManager.AddComponentData<PhysicsCollider>(entity, colliderComponent);

    and so on.

    I haven't tested whether it sees all of the CollisionEvents it should, but at least it sees some, and that's a start.
     
  8. TheKogs

    TheKogs

    Joined:
    Sep 25, 2019
    Posts:
    1
    Hi,

    I have troubles with the code from MixGrey's first post.

    My scene contains one plane with a PhysicsShape inside a subscene and one sphere above it with a PhysicsShape, PhysicsBody and ConvertToEntity component.
    The only script in the scene is the CollisionSystem from the first post.

    The issue I have is, that I always get the following error directly after starting the scene and I don't know why or how I can get rid of it.

    InvalidOperationException: The previously scheduled job CollisionSystem:CollisionJob reads from the NativeArray CollisionJob.UserJobData.physicsWorld.DynamicsWorld.m_MotionDatas. You are trying to schedule a new job Solver:ApplyGravityAndCopyInputVelocitiesJob, which writes to the same NativeArray (via ApplyGravityAndCopyInputVelocitiesJob.MotionDatas). To guarantee safety, you must include CollisionSystem:CollisionJob as a dependency of the newly scheduled job.


    InvalidOperationException: The previously scheduled job CollisionSystem:CollisionJob reads from the NativeArray CollisionJob.UserJobData.physicsWorld.DynamicsWorld.m_MotionDatas. You are trying to schedule a new job Jobs:CreateMotions, which writes to the same NativeArray (via CreateMotions.Data.MotionDatas). To guarantee safety, you must include CollisionSystem:CollisionJob as a dependency of the newly scheduled job.

    I've also tried to change the code a bit like in UnityPhysicsSamples - 2d. Events - CollisionEventImpulseBehaviour.cs
    This example don't pass the PhysicsWorld to the job, instead get the Entities out of the CollissionEvent struct.
    In this case I get a similar error.

    InvalidOperationException: The previously scheduled job CollisionSystem:CollisionJob reads from the NativeArray CollisionJob.InputVelocities. You are trying to schedule a new job Solver:ApplyGravityAndCopyInputVelocitiesJob, which writes to the same NativeArray (via ApplyGravityAndCopyInputVelocitiesJob.InputVelocities). To guarantee safety, you must include CollisionSystem:CollisionJob as a dependency of the newly scheduled job.

    Does anyone have an idea what I'm missing to get this code working?
     
  9. WAYN_Games

    WAYN_Games

    Joined:
    Mar 16, 2019
    Posts:
    723
    You can add a synch point after yout job with collisionHandle.Complete().
    Depending on the logic of your game, maybe you could change to an ITriggerEventsJob which don't need a synchpoint.

    Code (CSharp):
    1. [UpdateAfter(typeof(EndFramePhysicsSystem))]
    2. public class CollisionEventSystem : JobComponentSystem
    3. {
    4.  
    5.  
    6.     BuildPhysicsWorld buildPhysicsWorldSystem;
    7.     StepPhysicsWorld stepPhysicsWorld;
    8.  
    9.     protected override void OnCreate()
    10.     {
    11.         buildPhysicsWorldSystem = World.GetOrCreateSystem<BuildPhysicsWorld>();
    12.         stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
    13.     }
    14.  
    15.     [BurstCompile]
    16.     struct CollisionEventSystemJob : ITriggerEventsJob
    17.     {
    18.         public void Execute(TriggerEvent triggerEvent)
    19.         {
    20.             Debug.Log($"collision event: {triggerEvent}. Entities: {triggerEvent.Entities.EntityA}, {triggerEvent.Entities.EntityB}");
    21.         }
    22.  
    23.      
    24.     }
    25.  
    26.     protected override JobHandle OnUpdate(JobHandle inputDependencies)
    27.     {
    28.         var job = new CollisionEventSystemJob().Schedule(stepPhysicsWorld.Simulation, ref buildPhysicsWorldSystem.PhysicsWorld,
    29.              inputDependencies);
    30.         return job;
    31.     }
    32.  
    33.  
    34. }
     
    anarkiastd and steveeHavok like this.
  10. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    480
    If your system is updated after StepPhysicsWorld and before EndFramePhysicsSystem, you can also add collisionHandle to EndFramePhysicsSystem.HandlesToWaitFor. HandlesToWaitFor is effectively Complete'd on the next time through the BuildPhysicsWorld system update.
     
    WAYN_Games likes this.
  11. crocvr

    crocvr

    Joined:
    Oct 7, 2015
    Posts:
    44
    I also have this problem and it is happening not everywhere. I created Main scene and it was working okay with it but when I added new scene for Play Mode testing this errors appear no matter how I combine handles.

    Here is my code right now:

    Code (CSharp):
    1. [UpdateAfter(typeof(EndFramePhysicsSystem))]
    2.     public class TickDamagePlayerOnCollisionSystem : JobComponentSystem
    3.     {
    4.         private BuildPhysicsWorld _buildPhysicsWorldSystem;
    5.         private StepPhysicsWorld _stepPhysicsWorldSystem;
    6.         private EntityQuery _playerGroup;
    7.  
    8.         protected override void OnCreate()
    9.         {
    10.             _buildPhysicsWorldSystem = World.GetOrCreateSystem<BuildPhysicsWorld>();
    11.             _stepPhysicsWorldSystem = World.GetOrCreateSystem<StepPhysicsWorld>();
    12.             _playerGroup = GetEntityQuery(new EntityQueryDesc
    13.             {
    14.                 All = new ComponentType[]
    15.                 {
    16.                     typeof(PlayerTag),
    17.                     typeof(Health)
    18.                 },
    19.             });
    20.         }
    21.  
    22.         [BurstCompile]
    23.         public struct TickDamagePlayerOnCollisionJob : ICollisionEventsJob
    24.         {
    25.             [ReadOnly] [DeallocateOnJobCompletion] public NativeArray<Entity> players;
    26.  
    27.             public ComponentDataFromEntity<Health> healths;
    28.             public ComponentDataFromEntity<TickDamage> TickDamages;
    29.  
    30.             public void Execute(CollisionEvent collisionEvent)
    31.             {
    32.                 var entityA = collisionEvent.Entities.EntityA;
    33.                 var entityB = collisionEvent.Entities.EntityB;
    34.  
    35.                 if (!TickDamages.Exists(entityA) || !players.Contains(entityB))
    36.                 {
    37.                     var temp = entityA;
    38.                     entityA = entityB;
    39.                     entityB = temp;
    40.                 }
    41.  
    42.                 if (!TickDamages.Exists(entityA) || !players.Contains(entityB))
    43.                 {
    44.                     return;
    45.                 }
    46.  
    47.                 var tick = TickDamages[entityA];
    48.                 if (!(tick.CurrentTime > tick.Time)) return;
    49.                 var health = healths[entityB];
    50.                 health.Value -= tick.Value;
    51.                 healths[entityB] = health;
    52.  
    53.                 tick.CurrentTime = 0;
    54.                 TickDamages[entityA] = tick;
    55.             }
    56.         }
    57.  
    58.         protected override JobHandle OnUpdate(JobHandle inputDeps)
    59.         {
    60.             var jobHandle = new TickDamagePlayerOnCollisionJob
    61.             {
    62.                 players = _playerGroup.ToEntityArray(Allocator.TempJob),
    63.                 healths = GetComponentDataFromEntity<Health>(),
    64.                 TickDamages = GetComponentDataFromEntity<TickDamage>()
    65.             }.Schedule(_stepPhysicsWorldSystem.Simulation, ref _buildPhysicsWorldSystem.PhysicsWorld, inputDeps);
    66.  
    67.             return jobHandle;
    68.         }
    69.     }
     
  12. WAYN_Games

    WAYN_Games

    Joined:
    Mar 16, 2019
    Posts:
    723
    If I sum up the previous post you can either :
    1) use the ITriggerEventsJob if you don't need the collider to interract with physics
    2) shcedule your ICollisionEvtnesJob after the StepPhysicsSystem and before the EndFramePhysicsSytem and either
    a) add a synch point with Complete()
    b) inject your jobhandle to EndFramePhysicsSystem with EndFramePhysicsSystem.HandlesToWaitFor
     
    florianhanke likes this.
  13. crocvr

    crocvr

    Joined:
    Oct 7, 2015
    Posts:
    44
    Thank you!
    So as I understood you must process collider events between StepPhysicsWorld and EndFramePhysicsSystem. I am not sure what is the reason for it but I think it is somehow related to how collider event system is working.
    I got that idea about processing ColliderEvents after EndFramePhysicsSystem from UnityPhysicsSample.

    I added attribute to update system after StepPhysicsWorld and injected handle to EndFrame to make sure that it will complete before physics processing complete.
     
  14. hamokshaelzaki

    hamokshaelzaki

    Joined:
    Nov 6, 2012
    Posts:
    19
    Guys I have a kinda different question,
    where I can learn about this, I've been in Dots for like 2 months and I'm still can't find my way around.
     
  15. gamayun

    gamayun

    Joined:
    Nov 20, 2012
    Posts:
    34
    Hi @ktitov3d would you mind showing the code of your system updated because I can't seem to be able to make it work on my side
    Cheers,
    Thierry
     
  16. qaisbayabani

    qaisbayabani

    Joined:
    Jun 20, 2018
    Posts:
    28
unityunity