How to setup/initialize EntityCommandBuffer for ITriggerEventJob?

after 2 hrs of frustrating confusion, a severe headache, and an urge to throw my laptop to the wall, I’ve completely given up on trying to learn how to make EntityCommandBuffer work for my trigger job.

can anyone pls tell me in an easy way how to set up the entityCommandBuffer for ITriggerEventJob?

I’m trying to make a jobSystem that destroys entities that collides with a trigger collider that I made.

also if you wanna help dont just give me a wall of text with but structure it with just easy to read points and a straight forward code example. Thank you

2 Likes

Add this to your trigger volume and don’t forget to check “Is Trigger”

[GenerateAuthoringComponent]
public struct TriggerVolume : IComponentData
{

}

And this is basically a copy and paste from Unity’s ECS Physics samples which are aviable at GitHub - Unity-Technologies/EntityComponentSystemSamples
and the code itself
[

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Physics;
using Unity.Physics.Systems;

[UpdateAfter(typeof(EndFramePhysicsSystem))]
public class DeleteOnEnterTriggerSystem : JobComponentSystem
{
    EndSimulationEntityCommandBufferSystem m_EntityCommandBufferSystem;

    BuildPhysicsWorld m_BuildPhysicsWorldSystem;
    StepPhysicsWorld m_StepPhysicsWorldSystem;

   [BurstCompile]
    struct ProcessAllTriggerEventsJob : ITriggerEventsJob
    {
        [ReadOnly] public ComponentDataFromEntity<PhysicsVelocity> PhysicsVelocityGroup;
        [ReadOnly] public ComponentDataFromEntity<TriggerVolume> VolumeGroup;
        public EntityCommandBuffer commandBuffer;
        public void Execute(TriggerEvent triggerEvent)
        {
            Entity entityA = triggerEvent.Entities.EntityA;
            Entity entityB = triggerEvent.Entities.EntityB;

            bool isBodyATrigger = VolumeGroup.Exists(entityA);
            bool isBodyBTrigger = VolumeGroup.Exists(entityB);

            // Ignoring Triggers overlapping other Triggers
            if (isBodyATrigger && isBodyBTrigger)
                return;

            bool isBodyADynamic = PhysicsVelocityGroup.Exists(entityA);
            bool isBodyBDynamic = PhysicsVelocityGroup.Exists(entityB);

            // Ignoring overlapping static bodies
            if ((isBodyATrigger && !isBodyBDynamic) ||
                (isBodyBTrigger && !isBodyADynamic))
                return;

            if(isBodyATrigger && isBodyBDynamic)
            {
                commandBuffer.DestroyEntity(entityB);
            }

            if (isBodyBTrigger && isBodyADynamic)
            {
                commandBuffer.DestroyEntity(entityA);
            }
        }
    }

    protected override void OnCreate()
    {
        base.OnCreate();
        m_EntityCommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
        m_BuildPhysicsWorldSystem = World.GetOrCreateSystem<BuildPhysicsWorld>();
        m_StepPhysicsWorldSystem = World.GetOrCreateSystem<StepPhysicsWorld>();

    }
    protected override JobHandle OnUpdate(JobHandle inputDependencies)
    {
        var job = new ProcessAllTriggerEventsJob
        {
            PhysicsVelocityGroup = GetComponentDataFromEntity<PhysicsVelocity>(true),
            VolumeGroup = GetComponentDataFromEntity<TriggerVolume>(true),
            commandBuffer = m_EntityCommandBufferSystem.CreateCommandBuffer()
        }.Schedule(m_StepPhysicsWorldSystem.Simulation, ref m_BuildPhysicsWorldSystem.PhysicsWorld, inputDependencies);

        m_EntityCommandBufferSystem.AddJobHandleForProducer(job);
        return job;
    }
}

](GitHub - Unity-Technologies/EntityComponentSystemSamples)

1 Like

The folowing system will add a component to the coliding entities. An other system is then in charge of destroying the entire entity heirarchy (the entities that have the component and recursivly all it’s childs). you could just set diretcly the enities to be destroyed in this job if you don’t care about the childs

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Physics;
using Unity.Physics.Systems;

[UpdateAfter(typeof(EndFramePhysicsSystem))]
public class CollisionTrigerHierarchyDestructionSystem : JobComponentSystem
{


    BuildPhysicsWorld buildPhysicsWorldSystem;
    StepPhysicsWorld stepPhysicsWorld;
    EndSimulationEntityCommandBufferSystem m_EntityCommandBufferSystem;

    protected override void OnCreate()
    {
        buildPhysicsWorldSystem = World.GetOrCreateSystem<BuildPhysicsWorld>();
        stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
        m_EntityCommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
    }

    [BurstCompile]
    struct CollisionEventSystemJob : ITriggerEventsJob
    {
        public EntityCommandBuffer EntityCommandBuffer;
        public void Execute(TriggerEvent triggerEvent)
        {
            EntityCommandBuffer.AddComponent(triggerEvent.Entities.EntityA, new DestroyHierarchy());
            EntityCommandBuffer.AddComponent(triggerEvent.Entities.EntityB, new DestroyHierarchy());
        }

      
    }

 

    protected override JobHandle OnUpdate(JobHandle inputDependencies)
    {
        var job = new CollisionEventSystemJob() {
           EntityCommandBuffer =  m_EntityCommandBufferSystem.CreateCommandBuffer()
        }.Schedule(stepPhysicsWorld.Simulation, ref buildPhysicsWorldSystem.PhysicsWorld,
             inputDependencies);

        m_EntityCommandBufferSystem.AddJobHandleForProducer(job);
        return job;
    }


}

Keep in mind that destroying entities may cause error if they are still referenced by other entities.

Bonus, the DestroyHierarchy system :

using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Transforms;

[UpdateAfter(typeof(CollisionTrigerHierarchyDestructionSystem))]
public class DestroyHierarchySystem : JobComponentSystem
{
    private EntityQuery m_ChildRootGroup;
    private EntityQuery m_RootsGroup;
    private EntityQueryMask m_LocalToWorldWriteGroupMask;
    private EndSimulationEntityCommandBufferSystem m_EntityCommandBufferSystem;

    protected override void OnCreate()
    {
        m_EntityCommandBufferSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
        m_ChildRootGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new ComponentType[]
            {
                    ComponentType.ReadOnly<DestroyHierarchy>(),
                    ComponentType.ReadOnly<Child>()
            }
        });

        m_RootsGroup = GetEntityQuery(new EntityQueryDesc
        {
            All = new ComponentType[]
            {  
                    ComponentType.ReadOnly<DestroyHierarchy>()
            }
        });

    }

    [BurstCompile]
    struct DestroyChilds : IJobChunk
    {
        [ReadOnly] public ArchetypeChunkBufferType<Child> ChildType;
        [ReadOnly] public BufferFromEntity<Child> ChildFromEntity;
        public EntityCommandBuffer.Concurrent EntityCommandBuffer;

        public void Execute(ArchetypeChunk chunk, int index, int entityOffset)
        {
            var chunkChildren = chunk.GetBufferAccessor(ChildType);
            for (int i = 0; i < chunk.Count; i++)
            {
                var children = chunkChildren[i];
                for (int j = 0; j < children.Length; j++)
                {
                    RecursiveChildsDestroy(children[j].Value,index);
                }
            }
        }


        void RecursiveChildsDestroy(Entity entity, int index)
        {
            EntityCommandBuffer.DestroyEntity(index,entity);
            if (ChildFromEntity.Exists(entity))
            {
                var children = ChildFromEntity[entity];
                for (int i = 0; i < children.Length; i++)
                {
                    RecursiveChildsDestroy(children[i].Value, index);
                }
            }
        }
    }

    [BurstCompile]
    struct DestroyHierarchyRootJob : IJobForEachWithEntity<DestroyHierarchy>
    {
        public EntityCommandBuffer.Concurrent EntityCommandBuffer;

        public void Execute(Entity entity, int index, [ReadOnly] ref DestroyHierarchy c0)
        {
            EntityCommandBuffer.DestroyEntity(index,entity);
        }
    }

    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        var childType = GetArchetypeChunkBufferType<Child>(true);
        var childFromEntity = GetBufferFromEntity<Child>(true);

        var destroyChilds = new DestroyChilds
        {
            ChildType = childType,
            ChildFromEntity = childFromEntity,
            EntityCommandBuffer = m_EntityCommandBufferSystem.CreateCommandBuffer().ToConcurrent()
        };
        var destroyChildsJobHandle = destroyChilds.Schedule(m_ChildRootGroup, inputDeps);

        m_EntityCommandBufferSystem.AddJobHandleForProducer(destroyChildsJobHandle);

        var destroyHierarchyRootJob = new DestroyHierarchyRootJob()
        {
            EntityCommandBuffer = m_EntityCommandBufferSystem.CreateCommandBuffer().ToConcurrent()
        };
        var destroyHierarchyRootJobHandle = destroyHierarchyRootJob.Schedule(m_RootsGroup, destroyChildsJobHandle);

        m_EntityCommandBufferSystem.AddJobHandleForProducer(destroyHierarchyRootJobHandle);

        return destroyHierarchyRootJobHandle;
    }
}
1 Like

Thank you
you said that destroying an entity whilst it still being referenced may cause an error. How do I deal with that?(like how to un-reference an entity in both monoBehaviour and componentSystem) cause besides causing an error it also makes the game and editor crash.

I have not dealt with tha issue yet.
My thougth would be to make a system fore each IComponent data that references an entity and when the entity does not exist anymore, set it or destroy it has you need/wish.

seems like such a basic issue yet I literally only see one thread on it and there wasnt even a reply on that thread.

1 Like