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.
  2. Dismiss Notice

Question How do I detect collisions?

Discussion in 'Physics for ECS' started by Deleted User, Mar 23, 2022.

  1. Deleted User

    Deleted User

    Guest

    How do I detect collisions between entities so I can destroy them on impact?
    I'm very new to DOTS, but I managed to get a scene running with about 500 entities floating about and bumping into each other in dots with the Unity Physics package. I also managed to get projectiles to shoot from the camera and the projectiles bump into the entities.
     
    anhnguyennh02 likes this.
  2. TRS6123

    TRS6123

    Joined:
    May 16, 2015
    Posts:
    246
    The easiest way is to run or schedule a job implementing ICollisionEventsJob or ITriggerEventsJob
     
  3. JosepMariaPujol

    JosepMariaPujol

    Unity Technologies

    Joined:
    Jun 16, 2021
    Posts:
    89
    Firstly, I need to add a "PhysicsShape" component to objects you want to interact with.

    Let's say Entity A and entity B.

    Next, search for Collision Response (dropdown field) and select the "Raise Trigger Events" option for entity A&B.

    Now, in the same component check, you have in Collision Filter > Belongs To & Collides With to Everything.

    At this point, you will need to make 2 different tags using IComponentData and attach one to each entity. (Entity A could be PlayerTag, and Entity B PickupTag)
    Example given:


    Code (CSharp):
    1. using Unity.Entities;
    2.  
    3. [GenerateAuthoringComponent]
    4. public struct PlayerTag : IComponentData
    5. {
    6. }
    7.  
    Code (CSharp):
    1. using Unity.Entities;
    2.  
    3. [GenerateAuthoringComponent]
    4. public struct PickupTag : IComponentData
    5. {
    6. }
    7.  
    Last but not least, a TriggerSystem will be needed, see code:


    Code (CSharp):
    1. using Unity.Burst;
    2. using Unity.Collections;
    3. using Unity.Entities;
    4. using Unity.Physics;
    5. using Unity.Physics.Systems;
    6.  
    7. public partial class TriggerSystem : SystemBase
    8. {
    9.     private EndSimulationEntityCommandBufferSystem endECBSystem;
    10.     private StepPhysicsWorld stepPhysicsWorld;
    11.    
    12.     protected override void OnCreate()
    13.     {
    14.         endECBSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    15.         stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
    16.     }
    17.  
    18.     protected override void OnUpdate()
    19.     {
    20.         var triggerJob = new TriggerJob
    21.         {
    22.             allPickups = GetComponentDataFromEntity<PickupTag>(true),
    23.             allPlayers = GetComponentDataFromEntity<PlayerTag>(),
    24.             ecb = endECBSystem.CreateCommandBuffer()
    25.         };
    26.  
    27.         Dependency = triggerJob.Schedule(stepPhysicsWorld.Simulation, Dependency);
    28.         endECBSystem.AddJobHandleForProducer(Dependency);
    29.     }
    30. }
    31.  
    32.  
    33. [BurstCompile]
    34. struct TriggerJob : ITriggerEventsJob
    35. {
    36.     [ReadOnly] public ComponentDataFromEntity<PickupTag> allPickups;
    37.     public ComponentDataFromEntity<PlayerTag> allPlayers;
    38.     public EntityCommandBuffer ecb;
    39.  
    40.     public void Execute(TriggerEvent triggerEvent)
    41.     {
    42.         Entity entityA = triggerEvent.EntityA;
    43.         Entity entityB = triggerEvent.EntityB;
    44.        
    45.         if (allPickups.HasComponent(entityA) && allPickups.HasComponent(entityB)) return;
    46.  
    47.         if (allPickups.HasComponent(entityA) && allPlayers.HasComponent(entityB))
    48.         {
    49.             ecb.DestroyEntity(entityA);
    50.         }
    51.         else if(allPickups.HasComponent(entityB) && allPlayers.HasComponent(entityA))
    52.         {
    53.             ecb.DestroyEntity(entityB);
    54.         }
    55.     }
    56. }
    57.  
    That should destroy ALL the entities that have the PickupTag script attached to them.
     
  4. JosepMariaPujol

    JosepMariaPujol

    Unity Technologies

    Joined:
    Jun 16, 2021
    Posts:
    89
    This is how components look like in the editor.
     

    Attached Files:

    Egad_McDad, Emanx140, kilo32 and 3 others like this.
  5. Deleted User

    Deleted User

    Guest

    Thank you so much. That was pretty straightforward. I got it working right away!
     
  6. vildauget

    vildauget

    Joined:
    Mar 10, 2014
    Posts:
    120
    Why that code isn't part of the documentation among the other examples is beyond me. Thank you for showing us, saved me some time fiddling around.

    I still had to troubleshoot why errors are thrown when entities are destroyed in other systems, even though care is being taken to destroy in later entity command buffer systems. Add the following to the example above to fix it:

    Code (CSharp):
    1.     [UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
    2.     [UpdateAfter(typeof(StepPhysicsWorld))]
    3.     [UpdateBefore(typeof(ExportPhysicsWorld))]
    4.     public partial class TriggerSystem : SystemBase
    and

    Code (CSharp):
    1.         protected override void OnStartRunning()
    2.         {
    3.             this.RegisterPhysicsRuntimeSystemReadOnly();
    4.         }
    5.  
     
  7. Arnold_2013

    Arnold_2013

    Joined:
    Nov 24, 2013
    Posts:
    262
    TL;DR; How to do this correctly (with max performance/parallel) in a real game? and as a nice to have without "huge 'if statement' script"?

    This seems to be the most recent tread on the topic. I use the physics system for all interactions, collisions, within fire range, AI (should I move to/away from the target), area of effect damage... and the code just becomes a complete mess because you need to filter all these interactions to determine what type of interaction this is. Followed by either creating a 'processEntity' what will trigger a different system to take care of the logic, or processing it in this already bulky piece of code.
    And fair enough determining the interaction will always take some CPU cycles, so doing it yourself is not a big problem. But it seems to schedule on a single thread??

    1. At the bottom I included my current ITriggerEventsJob splitter/processor code. With ifElse structure to determine the interaction that occurred. If I look at this above example of this forum thread... "this is the way" it should be.(? true ?)

    2. To process in Parrallel, I schedule the ITriggerEventsJob and procces nothing only put entity A and B in a NativeList. Followed by a ": IJobParallelFor" to do the splitting/processing and it uses only this NativeList, but I cannot schedule the list in parallel because at the time of scheduling the NativeList length is not known from the ITriggerEventsJob (since its not completed yet). What I do now is just use a big number for the scheduling and Return if the index is bigger than the NativeList length (once the Job starts the list will be filled and the length will be known). Is there a better way to schedule this so it can run in parallel? (This methode works as long as the value you schedule is bigger than the List length. I look at the list length of the previous frame to increase this value)

    Code (CSharp):
    1. public EntityCommandBuffer.ParallelWriter ecb;
    2.         [ReadOnly] public NativeList<TriggerObjectsComponent> tempTriggerBufferForJob2;
    3.  
    4.         public void Execute(int index)
    5.         {
    6.             if (index >= tempTriggerBufferForJob2.Length)
    7.             {              
    8.                 return;
    9.             }
    10.        
    11.             var A = tempTriggerBufferForJob2[index].A;
    12.             var B = tempTriggerBufferForJob2[index].B;
    13.  
    14. splitting/processing here...

    Code (CSharp):
    1.  
    2.  
    3. var A =   triggerEvent.EntityA;
    4. var B =  triggerEvent.EntityB;
    5.  
    6. if (lht.HasComponent(A) || lht.HasComponent(B) || rht.HasComponent(A) || rht.HasComponent(B))
    7.             {
    8.                 var newInteraction = ecb.CreateEntity(index);
    9.  
    10.                 var handTrigger = lht.HasComponent(A) || rht.HasComponent(A) ? A : B;
    11.                 var objectGrabbed = lht.HasComponent(A) || rht.HasComponent(A) ? B : A;
    12.                 var isLefthand = lht.HasComponent(handTrigger);
    13.  
    14.                 if (shopItems.HasComponent(objectGrabbed))
    15.                 {
    16.                     ecb.AddComponent(index, newInteraction, new HandShopInteractionTriggerPairComponent() { HandTrigger = handTrigger, ObjectGrabbed = objectGrabbed, isLeftHand = isLefthand });
    17.                 }
    18.                 else
    19.                 {
    20.                     ecb.AddComponent(index, newInteraction, new HandInteractionTriggerPairComponent() { HandTrigger = handTrigger, ObjectGrabbed = objectGrabbed, isLeftHand = isLefthand });
    21.                 }
    22.             }
    23.             else if (enemyWithinRangePair.HasComponent(A) || enemyWithinRangePair.HasComponent(B))
    24.             {
    25.              
    26.                 var withinRangeTrigger = enemyWithinRangePair.HasComponent(A) ? A : B;
    27.                 var enemy = enemyWithinRangePair.HasComponent(A) ? B : A;
    28.  
    29.                 var unitWithBrain = enemyWithinRangePair[withinRangeTrigger].OwnerOfTrigger;
    30.  
    31.                 if (otherBrainLinks.HasComponent(unitWithBrain))
    32.                 {
    33.                     var brain = otherBrainLinks[unitWithBrain].brainEntity;
    34.                     var preferedAction = otherPreferredFireActionComponent[unitWithBrain];                  
    35.                     ecb.AppendToBuffer(index, brain, new BrainActionInputData() { myName = preferedAction.preferredFireAction, target = enemy });
    36.                 }
    37.             }  
    38.             else if (hitEffects.HasComponent(A) || hitEffects.HasComponent(B))
    39.             {
    40.                 if (hitEffects.HasComponent(A) && hitEffects.HasComponent(B))
    41.                 {
    42.                     Debug.Log("Beide Objecten hebben hit effects, PhysicsTriggerSystem");
    43.                 }
    44.                 else
    45.                 {
    46.                     var hitEffectsTrigger = hitEffects.HasComponent(A) ? A : B;
    47.                     var hitObject = hitEffects.HasComponent(A) ? B : A;
    48.  
    49.                     var bufferLength = hitEffects[hitEffectsTrigger].Length;
    50.  
    51.                     for (int i = 0; i < bufferLength; i++)
    52.                     {
    53.                         var instanceHitEffect = ecb.Instantiate(index, hitEffects[hitEffectsTrigger][i].hitEffectPrefab);
    54.                        
    55.                         ecb.SetComponent(index, instanceHitEffect, new Translation() { Value = translation[hitEffectsTrigger].Value });
    56.                         ecb.SetComponent(index, instanceHitEffect, new TargetHitComponent() { Target = hitObject });
    57.                         ecb.SetComponent(index, instanceHitEffect, new OwnerComponent() { Owner = hitEffectsTrigger });
    58.  
    59.                         if (otherRootOwners.HasComponent(hitEffectsTrigger) && otherRootOwners.HasComponent(hitEffects[hitEffectsTrigger][i].hitEffectPrefab))
    60.                         {
    61.                          
    62.                             ecb.SetComponent(index, instanceHitEffect, new RootOwnerComponent() { RootOwner = otherRootOwners[hitEffectsTrigger].RootOwner });
    63.                         }
    64.                    
    65.                         ecb.SetComponent(index, instanceHitEffect, statProperties[hitEffectsTrigger]);
    66.                     }
    67.  
    68.                     if (!otherDestroyTag.HasComponent(hitEffectsTrigger))
    69.                     {                      
    70.                         ecb.AddComponent(index, hitEffectsTrigger, new DestroyComponentTag());
    71.                     }
    72.                 }
    73.             }
    74.             else if (towerTag.HasComponent(A) && towerTag.HasComponent(B))
    75.             {
    76.                 if (towercollisionbufferup.HasComponent(A) && towercollisionbufferup.HasComponent(B))
    77.                 {
    78.                     var minimmalHeightDifferenceForTowerInteraction = 0.5f;
    79.                     if (math.abs(translation[A].Value.y - translation[B].Value.y) > minimmalHeightDifferenceForTowerInteraction)
    80.                     {
    81.                         if (translation[A].Value.y < translation[B].Value.y)
    82.                         {
    83.                             ecb.AppendToBuffer(index, A, new ConnectedTowerBufferUpElement() { connectedTowerUp = B });
    84.                             ecb.AppendToBuffer(index, B, new ConnectedTowerBufferDownElement() { connectedTowerDown = A });
    85.                         }
    86.                         else
    87.                         {
    88.                             ecb.AppendToBuffer(index, B, new ConnectedTowerBufferUpElement() { connectedTowerUp = A });
    89.                             ecb.AppendToBuffer(index, A, new ConnectedTowerBufferDownElement() { connectedTowerDown = B });
    90.                         }
    91.                     }
    92.                 }
    93.             }
    94.             else if (resourceField.HasComponent(A) || resourceField.HasComponent(B))
    95.             {
    96.                 var resource = resourceField.HasComponent(A) ? A : B;
    97.                 var turret = resourceField.HasComponent(A) ? B : A;
    98.  
    99.                 var newInteraction = ecb.CreateEntity(index);
    100.  
    101.                 ecb.AddComponent(index, newInteraction, new PoolMinerTriggerPairComponent() { ResourcePool = resource, OwnerOfTrigger = turret, Trigger = turret });
    102.  
    103.             }
     
  8. Tudor

    Tudor

    Joined:
    Sep 27, 2012
    Posts:
    150
    Hey, sorry to bother, but I've been looking everywhere for usage example on DOTS 1.0 for how to run an
    `ITriggerEventsJob`

    These no longer exist:
    1. [UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
    2. [UpdateAfter(typeof(StepPhysicsWorld))]
    3. [UpdateBefore(typeof(ExportPhysicsWorld))]
    Is this the correct usage instead? Is this when physics updates should run?
    `[UpdateAfter(typeof(Unity.Physics.Systems.PhysicsSimulationGroup))]`

    Then, how do you actually schedule a `ITriggerEventsJob` in ECS 1.0? Here:

    Code (CSharp):
    1.        public void OnUpdate(ref SystemState state)
    2.         {
    3.             var ecbSingleton = SystemAPI.GetSingleton<EndSimulationEntityCommandBufferSystem.Singleton>();
    4.             var ecb = ecbSingleton.CreateCommandBuffer(state.WorldUnmanaged);
    5.             var jhandle = new PositionalWarpingJob
    6.             {
    7.                 ecb = ecb,
    8.             };
    9.             // Dependency = triggerJob.Schedule(stepPhysicsWorld.Simulation, Dependency);
    10.             // endECBSystem.AddJobHandleForProducer(Dependency);
    11.             state.Dependency = jhandle.Schedule(Unity.Physics.SimulationSingleton ??, state.Dependency);
    12.  
    13.         }
    I have no idea where to search for this stuff (https://docs.unity3d.com/Packages/com.unity.entities@1.0/manual/index.html) and even if I found something, it doesn't have usage examples. So please help :)

    ------------------------------

    [UPDATE]
    Last resort idea / the new way to search the "unity docs":

    Step 1: download the `EntityComponentSystemSamples-master` github project
    Step 2: open notepad++, Find In Files: `ITriggerEventsJob`, extension: *.cs, path: `EntityComponentSystemSamples-master\PhysicsSamples\Assets`
    Step 3:
    Code (CSharp):
    1. state.Dependency = new TriggerGravityFactorJob
    2.         {
    3.             TriggerGravityFactorGroup = m_Handles.TriggerGravityFactorGroup,
    4.             PhysicsGravityFactorGroup = m_Handles.PhysicsGravityFactorGroup,
    5.             PhysicsVelocityGroup = m_Handles.PhysicsVelocityGroup,
    6.         }.Schedule(SystemAPI.GetSingleton<SimulationSingleton>(), state.Dependency);
    That project is set up with the weirdest folder structure and scenes, and I completely missed `ITriggerEventsJob`. Also it doesn't come up when just searching within unity or in the folders. You need notepad++ ;)

    PS:
    Code (CSharp):
    1. [UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
    2. [UpdateAfter(typeof(PhysicsSystemGroup))]
     
    Last edited: Mar 26, 2023
  9. daniel-holz

    daniel-holz

    Unity Technologies

    Joined:
    Sep 17, 2021
    Posts:
    213
    @Tudor: Please have a look at the PhysicsSamples project. Under demos, there is a specific section about events:

    https://github.com/Unity-Technologies/EntityComponentSystemSamples/tree/master/PhysicsSamples/Assets/6. Events

    In these demos, an
    ITriggerEventsJob
    is triggered here as an example.
    Note that
    CollectTriggerEventsJob
    derives from
    ITriggerEventsJob
    .


    Please also take a look at the trigger events section in the Unity Physics documentation:
    https://docs.unity3d.com/Packages/c...manual/simulation-results.html#trigger-events
     
    Last edited: Aug 19, 2023
  10. chriscode

    chriscode

    Joined:
    Mar 2, 2015
    Posts:
    44
    Note stuff like the PhysicsShape has been effectively deprecated by moving it into part of the samples, rather than in the API. I believe the expectation is to add rigidbodies as "usual" as part of the authoring process.
     
    te_headfirst and layker90524 like this.
  11. chriscode

    chriscode

    Joined:
    Mar 2, 2015
    Posts:
    44
    It makes me a bit moany that all the links above are now broken.
     
    te_headfirst likes this.
  12. daniel-holz

    daniel-holz

    Unity Technologies

    Joined:
    Sep 17, 2021
    Posts:
    213
    I updated the links and description. The samples project was changed recently.
    Thanks for pointing out the broken links.
     
    pzy87 likes this.
  13. te_headfirst

    te_headfirst

    Joined:
    Aug 1, 2023
    Posts:
    8
    Great with the corrections but could you also provide an update as to the state of PhysicsBody and PhysicsShape are they deprecated or soon to be deprecated?. Should one then go with RigidBodys and Colliders and more importantly if so how would you go about handling collisions in burst compiled ECS code?

    (PS We would really like to avoid importing a "Sample" in our production code base ;-)
     
    Last edited: Oct 9, 2023
  14. te_headfirst

    te_headfirst

    Joined:
    Aug 1, 2023
    Posts:
    8
    Ping ping :)

    Question is really:
    How can I leverage collision filters in ECS on an entity that has only Ridigbody and Collider and not PhysicsBody and PhysicsShape? (I still really want to avoid importing Unity Physics Samples project code in our production code ;-).

    Can it be done in a similar way to how one currently have to handle e.g. InverseInertia and InverseMass by making a custom system to bake the values from the Rigidbody to the PhysicsMass component?

    I simply cannot find a way to set the BelongsTo and CollidesWith collision filters (are they also only part of the Samples code) and using classic Layers Overrides does not work at all.

    Any one?
     
  15. te_headfirst

    te_headfirst

    Joined:
    Aug 1, 2023
    Posts:
    8
    Based on a reply from tangell here in Unity forum I managed to get everything working.

    I ended up creating a CollisionFilterAuthoring component with two custom (flagged enum) properties and called them BelongsTo and CollidesWith which when put on a Prefab will set a TemporaryBakingType on the entity and then a CustomCollisionFilterSystem that picks this up and sets the collision filters on the PhysicsCollider (I set everything is set up to run only when baking systems run)

    Of course this bypasses the built-in layers functionality but if you wish to use that, the same setup can easily be adapted to tap into Include Layers, Exclude Layers and GetLayer() of the Prefab and set Collision Filters accordingly.

    Link to post: https://forum.unity.com/threads/ent...l-release-now-available.1494323/#post-9404102
     
    daniel-holz likes this.
  16. daniel-holz

    daniel-holz

    Unity Technologies

    Joined:
    Sep 17, 2021
    Posts:
    213
    Hi @te_headfirst! First off, sorry for the delay.

    I am glad you found a solution in your case.

    We are acutely aware of the fact that some important features in built-in physics are not yet baked into Unity Physics (e.g., the collider layer overrides), and analogously that some features in the custom authoring components are not available in the built-in physics component interfaces (e.g., the physics shape's "Force Unique" property). Rest assured that we are working on multiple fronts right now and this is one of them.

    In the meantime, many thanks for sharing your solution as it will help others that are in a similar solution until we have remedied this situation fully.
     
  17. daniel-holz

    daniel-holz

    Unity Technologies

    Joined:
    Sep 17, 2021
    Posts:
    213
    Another thing.
    In accordance to what I said above about us chipping away at this literally "half baked" situation (pardon the pun :)) continuously, I am happy to let you know that the mass overrides from the RigidBody authoring component are now correctly baked into the PhysicsMass ECS component since 1.1.
    I believe you are referring to the properties below, correct?

    upload_2023-10-13_9-58-54.png