The ICollisionEventsJob calls Execute on 8 threads in parallel for a single collision, causing my projectiles to deal damage 8x the amount they should. Except on the first collision, it works properly there. Even though I remove the damage component after the first collision, it doesn’t care.
ICollisionEventsJob also does not allow me to get the JobIndex, so I can’t use a concurrent ECB to enforce it to only happen once.
using System;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Physics;
using Unity.Physics.Systems;
[UpdateAfter(typeof(ExcludeCollisionPairJobSystem))]
[UpdateAfter(typeof(EndFramePhysicsSystem))]
// [UpdateAfter(typeof(StepPhysicsWorld))]
[UpdateBefore(typeof(DestroyOnCollisionJobSystem))]
[UpdateBefore(typeof(DealDamageJobSystem))]
public class DealsDamageOnCollisionJobSystem : JobComponentSystem
{
private bool systemRunning = true;
private EndSimulationEntityCommandBufferSystem endSimECBSystem;
private BuildPhysicsWorld buildPhysicsWorld;
private StepPhysicsWorld stepPhysicsWorld;
// [BurstCompile]
private struct DealsDamageOnCollision : ICollisionEventsJob
{
private EntityCommandBuffer ecb;
[ReadOnly] private ComponentDataFromEntity<Tag_DealsDamageOnCollision> dealsDamageOnCollisionData;
[ReadOnly] private ComponentDataFromEntity<HealthComponent> healthData;
[ReadOnly] private ComponentDataFromEntity<DealsDamageComponent> damageData;
private BufferFromEntity<DamageBufferData> damageBuffer;
#region Constructor
public DealsDamageOnCollision(EntityCommandBuffer ecb,
[ReadOnly] ComponentDataFromEntity<Tag_DealsDamageOnCollision> destroyCollisionData,
[ReadOnly] ComponentDataFromEntity<HealthComponent> healthData,
[ReadOnly] ComponentDataFromEntity<DealsDamageComponent> damageData,
BufferFromEntity<DamageBufferData> damageBuffer)
{
this.ecb = ecb;
this.dealsDamageOnCollisionData = destroyCollisionData;
this.healthData = healthData;
this.damageData = damageData;
this.damageBuffer = damageBuffer;
}
#endregion
public void Execute(CollisionEvent collisionEvent)
{
// UnityEngine.Debug.Log($"{collisionEvent.Entities.EntityA}, {collisionEvent.Entities.EntityB}");
CheckAndDealDamage(collisionEvent.Entities.EntityA, collisionEvent.Entities.EntityB);
CheckAndDealDamage(collisionEvent.Entities.EntityB, collisionEvent.Entities.EntityA);
}
private void CheckAndDealDamage(Entity entity1, Entity entity2)
{
if(dealsDamageOnCollisionData.Exists(entity1) && damageData.Exists(entity1))
{
if(healthData.Exists(entity2))
{
DynamicBuffer<DamageBufferData> buffer = damageBuffer.Exists(entity2) ?
damageBuffer[entity2] :
ecb.AddBuffer<DamageBufferData>(entity2);
buffer.Add(new DamageBufferData{
damage = damageData[entity1].damage,
dealtBy = damageData[entity1].dealtBy
});
ecb.RemoveComponent<DealsDamageComponent>(entity1);
}
}
}
}
protected override void OnCreate()
{
endSimECBSystem = World.GetOrCreateSystem<EndSimulationEntityCommandBufferSystem>();
buildPhysicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
if(!systemRunning)
return default;
// If physics are off, get out of here
if(stepPhysicsWorld.Simulation.Type == SimulationType.NoPhysics)
return default;
// inputDeps.Complete();
JobHandle jobHandle = new DealsDamageOnCollision(
endSimECBSystem.CreateCommandBuffer(),
GetComponentDataFromEntity<Tag_DealsDamageOnCollision>(true),
GetComponentDataFromEntity<HealthComponent>(true),
GetComponentDataFromEntity<DealsDamageComponent>(true),
GetBufferFromEntity<DamageBufferData>(false)
).Schedule(stepPhysicsWorld.Simulation, ref buildPhysicsWorld.PhysicsWorld, inputDeps);
endSimECBSystem.AddJobHandleForProducer(jobHandle);
return jobHandle;
}
}
Edit: The problem still exists (and I’ve tried many different approaches at this point) My work around was to have my job that deals the damage from the damage buffer to check if any of the damage buffer elements were duplicate.