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.

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.
     
  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:
    84
    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.
     
    Egad_McDad, Emanx140, kilo32 and 9 others like this.
  4. JosepMariaPujol

    JosepMariaPujol

    Unity Technologies

    Joined:
    Jun 16, 2021
    Posts:
    84
    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:
    195
    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:
    148
    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:
    81
    @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/Demos/2. Setup/2d. Events

    In these demos, an
    ITriggerEventsJob
    is triggered here as an example.
    Note that
    StatefulEventCollectionJobs.CollectTriggerEvents
    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: Apr 22, 2023