Update component data in ICollisionEventsJob

Hi!

I am trying to update components data on colliding entities. In the end I want to be able to apply damage to units but I am not sure how to do this using Unity Physics.

Currently I am running the following code (which is not working as it does not update the Speed.Value - just for testing purposes)

using ECS.Components;
using Unity.Burst;
using Unity.Collections;
using Unity.Entities;
using Unity.Jobs;
using Unity.Physics;
using Unity.Physics.Systems;
using UnityEngine;


// READ https://discussions.unity.com/t/756266

namespace ECS.Systems
{
    [UpdateAfter(typeof(EndFramePhysicsSystem))]
    public class CollisionEventSystem : JobComponentSystem
    {
        private BuildPhysicsWorld _physicsWorld;

        private StepPhysicsWorld _stepPhysicsWorld;

        protected override void OnCreate()
        {
            this._physicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
            this._stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
        }

        protected override JobHandle OnUpdate(JobHandle inputDeps)
        {
            var job = new CollisionEventSystemJob
            {
                SpeedComponentData = GetComponentDataFromEntity<Speed>()
            };
           
            var jobHandle = job.Schedule(
                this._stepPhysicsWorld.Simulation,
                ref this._physicsWorld.PhysicsWorld,
                inputDeps
            );
            jobHandle.Complete();
            return jobHandle;
        }
    }

    [BurstCompile]
    struct CollisionEventSystemJob : ICollisionEventsJob
    {
        public ComponentDataFromEntity<Speed> SpeedComponentData;
        public void Execute(CollisionEvent collisionEvent)
        {
            var entityA = collisionEvent.Entities.EntityA;
            var entityB = collisionEvent.Entities.EntityB;

            if (SpeedComponentData.Exists(entityA) && SpeedComponentData.Exists(entityB))
            {
                Debug.Log("Setting Speed.");
                Speed speed = SpeedComponentData[entityA];
                speed.Value *= 2;
            }
        }
    }
}

However, I am not so sure if this makes sense, even if it worked. After all, I might have to destroy the projectile-entity and subtract the damage from the Health.Value of the hit entity. Not sure if this is the right place to take care of this.

Anyway, I can’t find a proper solution to this. Can I update values inside the CollisionEventSystemJob or do I have to collect data and finish the job elsewhere?

Sorry if this is a silly question, I am pretty new to Unity and, obviously, also to DOTS.

I would recommend something like a Damage component or DynamicBuffer. So basically if something can receive damage, you just accumulate or append to that value/buffer in your collision event job, and then have a separate, dependent job that applies the results of that Damage to a Health component. Basically, you have no guarantees about the order any of your contact events are processed on a given frame, so you don’t necessarily want to modify or destroy something in the contact event job itself, if the modified value might affect another contact event on that same frame.

2 Likes

Just for posterity (I was trying to do something similar, by writing to a component fetched with ComponentDataFromEntity):

  1. I think the write fails because “by default, you cannot write to components in the container in parallel Jobs” Struct ComponentDataFromEntity<T> | Package Manager UI website.
  2. To fix this, set the component back to the component data array to write: Update component data on collision

Can you show a sample with buffering? I mean, i am not sure how to return something from a job without applying modifications to components of some entity.