Search Unity

Detecting Collisions

Discussion in 'Physics for ECS' started by EternalAmbiguity, Nov 7, 2020.

  1. EternalAmbiguity

    EternalAmbiguity

    Joined:
    Dec 27, 2014
    Posts:
    3,144
    I've created a new project in 2020.1.10f1. I've imported the standard ECS packages: Entities (0.16.0-preview.21), Jobs (0.7.0-preview.17), and Unity Physics (0.5.1-preview.2). I've created the following scripts.

    The following goes on an empty gameobject in the scene:
    Code (CSharp):
    1. using System.Collections.Generic;
    2. using Unity.Entities;
    3. using Unity.Rendering;
    4. using Unity.Transforms;
    5. using Unity.Physics;
    6. using Unity.Physics.Systems;
    7. using Unity.Physics.Authoring;
    8. using UnityEngine;
    9. using Unity.Mathematics;
    10.  
    11. public class TestScript : MonoBehaviour
    12. {
    13.     BlobAssetStore blobAssetStore;
    14.     Entity entityPrefab;
    15.     public GameObject entityGO;
    16.     EntityManager em;
    17.  
    18.     private void Awake()
    19.     {
    20.         blobAssetStore = new BlobAssetStore();
    21.     }
    22.     // Start is called before the first frame update
    23.     void Start()
    24.     {
    25.         em = World.DefaultGameObjectInjectionWorld.EntityManager;
    26.         var settings = GameObjectConversionSettings.FromWorld(World.DefaultGameObjectInjectionWorld, blobAssetStore);
    27.         settings.ConversionFlags = GameObjectConversionUtility.ConversionFlags.AssignName;
    28.         entityPrefab = GameObjectConversionUtility.ConvertGameObjectHierarchy(entityGO, settings);
    29.         em.AddComponentData(entityPrefab, new Translation { Value = new float3(0, 0, 0) });
    30.         em.AddComponentData(entityPrefab, new Rotation { Value = quaternion.identity });
    31.     }
    32.  
    33.     void CreateEntity()
    34.     {
    35.         var instance = em.Instantiate(entityPrefab);
    36.         var te = new TestEntity();
    37.         em.SetComponentData(instance, new Translation { Value = new float3(0,0,0) });
    38.         em.SetComponentData(instance, new Rotation { Value = quaternion.identity });
    39.         em.SetComponentData(instance, te);
    40.     }
    41.  
    42.     // Update is called once per frame
    43.     void Update()
    44.     {
    45.         if (Input.GetKeyUp(KeyCode.Space))
    46.         {
    47.             CreateEntity();
    48.         }
    49.     }
    50. }
    The following goes on a simple 3D Cube prefab:
    Code (CSharp):
    1. using Unity.Entities;
    2.  
    3. [GenerateAuthoringComponent]
    4. public struct TestEntity : IComponentData
    5. {
    6.     public int testVar;
    7. }
    8.  
    The prefab also has a ConvertToEntity component, as well as a PhysicsShape component with a "Raise Trigger Events" CollisionResponse (I've also tried "Collide Raise Collision Events"). The prefab is also assigned as the "entityGO" in the previous script.

    The following script as well:
    Code (CSharp):
    1. using Unity.Burst;
    2. using Unity.Entities;
    3. using Unity.Physics;
    4. using Unity.Physics.Systems;
    5. using UnityEngine;
    6. using Unity.Collections;
    7. using Unity.Jobs;
    8.  
    9. [UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
    10. [UpdateAfter(typeof(StepPhysicsWorld))]
    11. [UpdateBefore(typeof(EndFramePhysicsSystem))]
    12. public class TestEntitySystem : SystemBase
    13. {
    14.     //EntityCommandBufferSystem m_Barrier;
    15.     BuildPhysicsWorld physicsWorldSystem;
    16.     StepPhysicsWorld m_StepPhysicsWorldSystem;
    17.     EndFramePhysicsSystem EndFramePhysicsSystem;
    18.  
    19.     protected override void OnCreate()
    20.     {
    21.         //m_Barrier = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    22.         physicsWorldSystem = Unity.Entities.World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<BuildPhysicsWorld>();
    23.         m_StepPhysicsWorldSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<StepPhysicsWorld>();
    24.         EndFramePhysicsSystem = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem<EndFramePhysicsSystem>();
    25.     }
    26.  
    27.     [BurstCompile]
    28.     struct CollisionEventJob : ICollisionEventsJob
    29.     {
    30.         public void Execute(CollisionEvent collisionEvent)
    31.         {
    32.             Debug.Log("Collision");
    33.         }
    34.     }
    35.  
    36.  
    37.     protected override void OnUpdate()
    38.     {
    39.         //Debug.Log("active");
    40.         Dependency = new CollisionEventJob().Schedule(m_StepPhysicsWorldSystem.Simulation, ref physicsWorldSystem.PhysicsWorld, Dependency);
    41.         EndFramePhysicsSystem.AddInputDependency(Dependency);
    42.         //m_Barrier.AddJobHandleForProducer(Dependency);
    43.         //Dependency.Complete();
    44.     }
    45. }
    46.  
    I start the scene in the editor. In The Entity Debugger I can see the first entity created. I then press Spacebar multiple times, and see new entities created, but nothing appears in my console. These entities ought to be created at the origin, and they ought to all collide with one another, and I ought to see a comment in the console when that happens. I don't see any comments in the console.

    What am I doing wrong?
     
  2. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Did you add a body to the prefab, or just collider? Entities with no physics body will end up being static, and static-static pairs are filtered out. That's why you don't get events.
     
    lndcobra and EternalAmbiguity like this.
  3. EternalAmbiguity

    EternalAmbiguity

    Joined:
    Dec 27, 2014
    Posts:
    3,144
    That works. Thanks a ton. A couple questions regarding actual usage:

    I have a system that just moves entities on a trajectory. When I add a Physics Body I get an error related to that - "changing position/rotation/velocity/collider ECS data on dynamic entities during physics step". Is this due to the specifics of my implementation? Is it possible to move an entity that has a physics body in a normal Entities.ForEach loop?

    Is it possible to detect collisions with regular GameObjects? If so, how?
     
  4. lndcobra

    lndcobra

    Joined:
    Jan 28, 2020
    Posts:
    21
    Trust me to read this, half a day after struggling with this for 2-3 hours! :(
     
  5. EternalAmbiguity

    EternalAmbiguity

    Joined:
    Dec 27, 2014
    Posts:
    3,144
    I was able to "resolve" my first issue by simply letting the physics system handle movement.

    The second issue is still unresolved. I intend to have a player-controlled object that can interact with entities, but the combination of not-being-able-to-manually-move-entities-with-physics with the different physics systems for entities and gameobjects means I'm not sure how to detect collisions.

    Edit: scratch that. Looks like you can have input on an entity that just doesn't have a Physics Body. I was able to convert my player object to an entity and detect collisions.
     
    Last edited: Nov 10, 2020
  6. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    So all good? :) Let me know if you have any more questions.
     
    EternalAmbiguity likes this.
  7. EternalAmbiguity

    EternalAmbiguity

    Joined:
    Dec 27, 2014
    Posts:
    3,144
    That's everything as it regards physics, though I am curious about a couple other DOTS things that I'm not sure belong in this sub-forum: 1. scheduling entities to be created (with associated modifications to translation, rotation, etc.) within an Entities.Foreach, and 2. Calling regular C#/Unity code upon a collision/trigger event firing.

    Again I don't think either is related to this sub-forum, and I haven't looked too far into either, so when I do if I don't find an answer I'll probably make a post in the larger DOTS forum.

    Thanks for the help.
     
    petarmHavok likes this.
  8. lndcobra

    lndcobra

    Joined:
    Jan 28, 2020
    Posts:
    21
    RE: calling managed memory code I personally pass in a CommandBuffer into my job (usually a BeginSimulation one), and write additional component to one of the entities or set a singleton.

    Code (CSharp):
    1.         var ecb = _ecbSystem.CreateCommandBuffer().AsParallelWriter();
    2.        
    3.         Dependency = new TriggerJob
    4.         {
    5.             Ecb = ecb,
    6.             // These remaining are just to access components on the entities
    7.             PlayerEntities = GetComponentDataFromEntity<PlayerTag>(),
    8.             CoinColliderComponent = GetComponentDataFromEntity<CoinColliderComponent>(),
    9.             CoinComponent = GetComponentDataFromEntity<CoinComponent>(),
    10.             LinkedEntityGroup = GetBufferFromEntity<LinkedEntityGroup>(),
    11.             ParticleSystemBufferElement = GetBufferFromEntity<ParticleSystemBufferElement>(),
    12.  
    13.         }.Schedule(_stepPhysicsWorld.Simulation, ref _physicsWorld.PhysicsWorld, Dependency);
    14.        
    15.         _ecbSystem.AddJobHandleForProducer(Dependency);

    I then have another system(s) to read these components and do the work on main thread:

    Example system which listens for the PlayParticles system and run without burst on main thread.

    Code (CSharp):
    1.  
    2. public class PlayParticleSystem : SystemBase
    3. {
    4.     private EndSimulationEntityCommandBufferSystem _endSimEcbSystem;
    5.  
    6.     protected override void OnCreate()
    7.     {
    8.         _endSimEcbSystem = World.GetExistingSystem<EndSimulationEntityCommandBufferSystem>();
    9.     }
    10.    
    11.     protected override void OnUpdate()
    12.     {
    13.         var ecb = _endSimEcbSystem.CreateCommandBuffer();
    14.  
    15.         Entities
    16.             .WithName("PlayParticles")
    17.             .WithoutBurst()
    18.             .WithAll<PlayParticles>()
    19.             .ForEach((ref Entity entity, in ParticleSystem particle) =>
    20.         {
    21.             particle.Play();
    22.             ecb.RemoveComponent<PlayParticles>(entity);
    23.         }).Run();
    24.        
    25.         _endSimEcbSystem.AddJobHandleForProducer(Dependency);
    26.     }
    27. }

    RE: Scheudling entities to be created, again I usually use the BeginSim as I don't want entities to be created and/or half processed, rather have them spawn beginning of next frame and get affected by all systems. (Was seeing entities spawn in their original position being moved first using EndSim).

    I am relatively new as well so no idea what is the best practice! Defo would be interested to hear how other people are doing it!
     
    EternalAmbiguity likes this.