Search Unity

[OUTDATED THREAD] Easy-to-use trigger/collision events system

Discussion in 'Physics for ECS' started by PhilSA, Apr 28, 2020.

  1. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    EDIT: This system is now outdated and can be replaced by similar collision event systems found in the physics samples project

    I created these simple TriggerEventsSystem and CollisionEventsSystem to solve ease-of-use issues I had with the current approaches for handling physics events. Here's how it works:
    • You add a TriggerEventsReceiverAuthoring to the entities that can receive trigger events
      • This adds a DynamicBuffer of trigger events on that entity
    • The TriggerEventsSystem launches a ITriggerEventsJob whose purpose is to go fill all of the DynamicBuffer of trigger events on entities that have them
    • You also get information on whether it's a Enter/Exit/Stay event
    That way you never have to create any other ITriggerEventsJob. You just have to check your event buffers on your entities to see if events happened. The same principles apply to the collision events version.

    Here's what a teleporter system would look like using this:
    Code (CSharp):
    1.  
    2. public class ExampleTeleporterSystem : SystemBase
    3. {
    4.     protected override void OnUpdate()
    5.     {
    6.         // for each teleporter with a trigger event buffer...
    7.         Dependency = Entities.ForEach((in Teleporter teleporter, in DynamicBuffer<TriggerEventBufferElement> triggerEventsBuffer) =>
    8.         {
    9.             // iterate on all trigger events that happened this frame...
    10.             for (int i = 0; i < triggerEventsBuffer.Length; i++)
    11.             {
    12.                 TriggerEventBufferElement triggerEvent = triggerEventsBuffer[i];
    13.  
    14.                 // If a character has entered the trigger, move its translation to the destination
    15.                 if (triggerEvent.State == PhysicsEventState.Enter &&
    16.                     HasComponent<Character>(triggerEvent.Entity))
    17.                 {
    18.                     Translation t = GetComponent<Translation>(teleporter.DestinationEntity);
    19.                     SetComponent(triggerEvent.Entity, t);
    20.                 }
    21.             }
    22.         }).Schedule(Dependency);
    23.     }
    24. }
    25.  

    It's not quite as performant as handling every trigger event of your game manually in a single ITriggerEventsJob, but I think it's a pretty useful timesaver that would be totally fine for most cases.

    Optimization/Design suggestions are welcome
     

    Attached Files:

    Last edited: Feb 19, 2021
  2. steveeHavok

    steveeHavok

    Joined:
    Mar 19, 2019
    Posts:
    481
    Very nice and serendipitous!
    In looking to expose the CharacterController plane constraints, we ended up extending out to implement a very similar system including general collision and trigger events. Its due out in the next release.
     
  3. imaginadio

    imaginadio

    Joined:
    Apr 5, 2020
    Posts:
    60
    Im trying to use your system, but got error:
    The type or namespace name 'PhysicsEventState' could not be found

    Can you share PhysicsEventState class(or what is that)?
     
  4. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    My bad, I did a refactor when I did the collision version but forgot to re-upload the triggers version

    You can redownload the files and it'll be fixed
     
    imaginadio likes this.
  5. imaginadio

    imaginadio

    Joined:
    Apr 5, 2020
    Posts:
    60
    I redownload, thanx. But i cant make it work.. i add CollisionEventsReceiverAuthoring to my entities, then i create simple system:
    Code (CSharp):
    1. public class DamageSystem : SystemBase
    2. {
    3.     private EndSimulationEntityCommandBufferSystem m_endSimulationEntityCommandBufferSystem;
    4.  
    5.     protected override void OnCreate()
    6.     {
    7.         base.OnCreate();
    8.         m_endSimulationEntityCommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    9.     }
    10.     protected override void OnUpdate()
    11.     {
    12.         var ecb = m_endSimulationEntityCommandBufferSystem.CreateCommandBuffer().ToConcurrent();
    13.  
    14.         Entities.ForEach((Entity entity, int entityInQueryIndex, in DynamicBuffer<CollisionEventBufferElement> collisionEventBuffer) =>
    15.         {
    16.             for (int i = 0; i < collisionEventBuffer.Length; i++)
    17.             {
    18.                 CollisionEventBufferElement bufferElement = collisionEventBuffer[i];
    19.                 ecb.DestroyEntity(entityInQueryIndex, bufferElement.Entity);
    20.             }
    21.         }).ScheduleParallel();
    22.  
    23.         m_endSimulationEntityCommandBufferSystem.AddJobHandleForProducer(this.Dependency);
    24.     }
    25. }
    26.  
    And when my entities collides with each other - nothing happens. I check in entity debugger, my entities has both CollisionEventsReceiverProperties and CollisionEventBufferElement components. Also i check "Used by Systems" section, and it used by my DamageSystem and CollisionEventsSystem. What im doing wrong?
     
  6. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    is "Raises Collision Events" enabled on at least one of your PhysicsShapes (in the authoring component, under "Advanced")?
     
    florianhanke and imaginadio like this.
  7. imaginadio

    imaginadio

    Joined:
    Apr 5, 2020
    Posts:
    60
    No... that was my problem :D thank you!
     
  8. Cynicat

    Cynicat

    Joined:
    Jun 12, 2013
    Posts:
    290
    Wow, really nice! I've been testing it and it's wonderful. Only problem i've run into so far is it seems to break when sleeping is active in Havok. I'm not actually sure how to tell if an entity is asleep or not in havok so i'll have to look into that later.
     
  9. Thygrrr

    Thygrrr

    Joined:
    Sep 23, 2013
    Posts:
    700
    When following your approach of building a Collision Events System, I get the following error:

    I've been stumped by this for a few days now, and it's blocking me hard at this point.

    What might I be doing that even a trivial system (it does nothing!) can't schedule its job (or rather, it prevents the physics jobs from being scheduled).

    Code (CSharp):
    1.  
    2. using Unity.Entities;
    3. using Unity.Physics;
    4. using Unity.Physics.Systems;
    5.  
    6. namespace Jovian.Systems.Physics
    7. {
    8.     [UpdateAfter(typeof(EndFramePhysicsSystem))]
    9.     public class CollisionEventSystem : SystemBase
    10.     {
    11.         public BuildPhysicsWorld _physicsWorld;
    12.         public StepPhysicsWorld _stepPhysicsWorld;
    13.         protected override void OnCreate()
    14.         {
    15.             _physicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
    16.             _stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
    17.         }
    18.  
    19.         //[BurstCompile]
    20.         private struct CollisionEventSystemJob : ICollisionEventsJob
    21.         {
    22.             public void Execute(CollisionEvent collisionEvent)
    23.             {
    24.                 var entityA = collisionEvent.Entities.EntityA;
    25.                 var entityB = collisionEvent.Entities.EntityB;
    26.             }
    27.         }
    28.  
    29.         protected override void OnUpdate()
    30.         {
    31.             Dependency = new CollisionEventSystemJob
    32.             {
    33.             }.Schedule(_stepPhysicsWorld.Simulation, ref _physicsWorld.PhysicsWorld, Dependency);
    34.         }
    35.     }
    36. }
    It did run in this form while it was a JobComponentSystem, but as soon as I tried to add any ComponentDataFromEntity, it would collide with the physics systems again (in the context of that specific ComponentData), strangely enough regardless of whether that data was even used for Physics.

    Supplemental edit - these are my systems, I wonder if I have too many of them in SimulationSystemGroup, or maybe if some don't belong there?
    https://imgur.com/a/6Q6hAIS
     
    Last edited: Jun 7, 2020
  10. KwahuNashoba

    KwahuNashoba

    Joined:
    Mar 30, 2015
    Posts:
    110
    Just came across the latest video form tutorial series that address DOTS. It seams like you need to complete and return dependencies for some reason.

     
    Thygrrr likes this.
  11. Thygrrr

    Thygrrr

    Joined:
    Sep 23, 2013
    Posts:
    700
    Thanks so much, KwahuNashoba. Completing the job did the trick, and it makes some degree of sense.

    However, OP's system doesn't need to do that. Why?
     
  12. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    If you are scheduling something after EndFramePhysicsSystem, there's no guarantee that it would finish before BuildPhysicsWorld of the next frame. Now, you never really need to schedule it after this system, instead, schedule it after ExportPhysicsWorld and before EndFramePhysicsSystem, and also add the job handle of your job to HandlesToWaitFor list of the EndFramePhysicsSystem.

    In the next release we will add the ability to define your job handle as a dependency of any of the physics systems, so at that point you could schedule stuff after EndFramePhysicsSystem and before the next BuildPhysicsWorld. Let me know if this works in the meantime!
     
  13. Thygrrr

    Thygrrr

    Joined:
    Sep 23, 2013
    Posts:
    700
    Thank you, I figured the "system might cross the frame boundary" was a thing, but I didn't know where to properly integrate it.
     
    petarmHavok likes this.
  14. LudiKha

    LudiKha

    Joined:
    Feb 15, 2014
    Posts:
    140
    Thanks for this @PhilSA . Would you care to elaborate why you run the systems in IJobChunk jobs?
     
  15. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    So if we take the TriggerEventsSystem for example, it runs 3 jobs:
    • TriggerEventsPreProcessJob (IJobChunk)
    • TriggerEventsJob (ITriggerEventsJob)
    • TriggerEventsPostProcessJob (IJobChunk)
    Most of the real work happens in the middle job, but the Pre/Post process jobs are only there to do the detection of Enter/Exit/Stay state. They are IJobChunks because they iterate on all entities that have the "TriggerEventBufferElement" component type

    But if you mean why IJobChunk rather than Entities.ForEach; it's just a matter of personal preference. For a low-level system that's gonna be re-used a lot, I prefer going with IJobChunk because that's the most performant & most extensible thing
     
    Last edited: Jun 30, 2020
    LudiKha likes this.
  16. LudiKha

    LudiKha

    Joined:
    Feb 15, 2014
    Posts:
    140
    Been using this buffer-based events implementation quite a bit now, and it works quite great! Appreciate you putting it up just like that.

    Also thanks for the explanation! Considering there's very few resources available for this - can you please explain in what way IJobChunk is more performant than Entities.ForEach in this instance?
     
  17. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I'm actually not 100% sure about IJobChunk being more performant. In fact, in this case, they are both probably the same performance-wise. But IJobChunk gives you more opportunities to do operations per-chunk rather than per-entities if you ever need to
     
    NotaNaN and LudiKha like this.
  18. JoaoSantos

    JoaoSantos

    Joined:
    Mar 31, 2014
    Posts:
    20
    I'm trying to understand one problem that I replay in another thread:


    In some situations, we can use the SystemBase as the base class of the System and in others, we can use JobComponentSystem. But in both situations, the ITriggerEventsJob just works in the initial execution of the collision. After that, it's like the trigger "sleep" (don't rise the collisions anymore).

    I see the Debug.Log that the Execute method in the ITriggerEventsJob doesn't show any message.

    I already set the Collision Response to Raise Trigger Events in PhysicsShape of the collectable element but still, work as a comment before.

    In the link a make a description of the scripts, Unity and package versions.
     
  19. goodnewsjimdotcom

    goodnewsjimdotcom

    Joined:
    May 24, 2017
    Posts:
    342
    Is this the best way to do collisions now?
     
  20. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    This code is outdated and should be replaced by similar collision event systems found in the physics samples. Link is in top post
     
  21. JMPM-UNITY

    JMPM-UNITY

    Unity Technologies

    Joined:
    Jun 16, 2021
    Posts:
    94