Some outdated examples show the use of a “CollisionEvents” from StepPhysicsWorld, but this is no longer accessible. Supposedly an ICollisionEventsJob can be used, but it doesn’t seem to do anything.
The physics simulation in my project works, the objects bounce off of each other, and my CollisionSystem runs, but my CollisionJob never gets any collision events.
[UpdateAfter(typeof(StepPhysicsWorld))]
[UpdateBefore(typeof(EndFramePhysicsSystem))]
public class CollisionSystem : JobComponentSystem
{
struct CollisionJob : ICollisionEventsJob
{
[ReadOnly] public PhysicsWorld physicsWorld;
public void Execute(CollisionEvent ev) // this is never called
{
Entity a = physicsWorld.Bodies[ev.BodyIndices.BodyAIndex].Entity;
Entity b = physicsWorld.Bodies[ev.BodyIndices.BodyBIndex].Entity;
Debug.Log($"collision event: {ev}. Entities: {a}, {b}");
}
}
BuildPhysicsWorld buildPhysicsWorldSystem;
StepPhysicsWorld stepPhysicsWorld;
EndFramePhysicsSystem endFramePhysicsSystem;
protected override void OnCreate()
{
buildPhysicsWorldSystem = World.GetOrCreateSystem<BuildPhysicsWorld>();
stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
Debug.Log($"collision system running: {Time.time}"); // this runs correctly
inputDeps = JobHandle.CombineDependencies(inputDeps, buildPhysicsWorldSystem.FinalJobHandle);
inputDeps = JobHandle.CombineDependencies(inputDeps, stepPhysicsWorld.FinalJobHandle);
var physicsWorld = buildPhysicsWorldSystem.PhysicsWorld;
var collisionJob = new CollisionJob
{
physicsWorld = physicsWorld
};
JobHandle collisionHandle = collisionJob.Schedule(stepPhysicsWorld.Simulation, ref physicsWorld, inputDeps);
return collisionHandle;
} // OnUpdate
} // System
I want some way to get pairs of entities from collision events every frame. How can this be done?
I have no PhysicsShape objects. Checking the docs, it looks like PhysicsShape is a MonoBehaviour, which I presume is for hybrid ECS. This is pure ECS and the objects have no relation to GameObjects.
Is there another way to access that setting for pure ECS?
If you are creating the colliders from code well at least that script should contain where Raises Collision Events gets turned into an ecs component property
I think I figured it out. When creating the Collider, there’s a field for a Unity.Physics.Material which has a field for a Unity.Physics.Material.MaterialFlags, which is an enum. One of the options is EnableCollisionEvents. I’m not sure what the other two are.
Creating the Collider like this leads to CollisionEvents actually being generated.
var material = new Unity.Physics.Material
{
CustomTags = Unity.Physics.Material.Default.CustomTags,
Flags = Unity.Physics.Material.MaterialFlags.EnableCollisionEvents |
Unity.Physics.Material.MaterialFlags.EnableMassFactors |
Unity.Physics.Material.MaterialFlags.EnableSurfaceVelocity,
Friction = Unity.Physics.Material.Default.Friction,
FrictionCombinePolicy = Unity.Physics.Material.Default.FrictionCombinePolicy,
Restitution = Unity.Physics.Material.Default.Restitution,
RestitutionCombinePolicy = Unity.Physics.Material.Default.RestitutionCombinePolicy,
};
var boxCollider = Unity.Physics.BoxCollider.Create(
new BoxGeometry{
Orientation=quaternion.identity,
Size=new float3(0.25f,0.25f,0.25f)},
CollisionFilter.Default,
material
);
Then: var colliderComponent = new PhysicsCollider { Value = boxCollider }; EntityManager.AddComponentData<PhysicsCollider>(entity, colliderComponent);
and so on.
I haven’t tested whether it sees all of the CollisionEvents it should, but at least it sees some, and that’s a start.
I have troubles with the code from MixGrey’s first post.
My scene contains one plane with a PhysicsShape inside a subscene and one sphere above it with a PhysicsShape, PhysicsBody and ConvertToEntity component.
The only script in the scene is the CollisionSystem from the first post.
The issue I have is, that I always get the following error directly after starting the scene and I don’t know why or how I can get rid of it.
InvalidOperationException: The previously scheduled job CollisionSystem:CollisionJob reads from the NativeArray CollisionJob.UserJobData.physicsWorld.DynamicsWorld.m_MotionDatas. You are trying to schedule a new job Solver:ApplyGravityAndCopyInputVelocitiesJob, which writes to the same NativeArray (via ApplyGravityAndCopyInputVelocitiesJob.MotionDatas). To guarantee safety, you must include CollisionSystem:CollisionJob as a dependency of the newly scheduled job.
InvalidOperationException: The previously scheduled job CollisionSystem:CollisionJob reads from the NativeArray CollisionJob.UserJobData.physicsWorld.DynamicsWorld.m_MotionDatas. You are trying to schedule a new job Jobs:CreateMotions, which writes to the same NativeArray (via CreateMotions.Data.MotionDatas). To guarantee safety, you must include CollisionSystem:CollisionJob as a dependency of the newly scheduled job.
I’ve also tried to change the code a bit like in UnityPhysicsSamples - 2d. Events - CollisionEventImpulseBehaviour.cs
This example don’t pass the PhysicsWorld to the job, instead get the Entities out of the CollissionEvent struct.
In this case I get a similar error.
InvalidOperationException: The previously scheduled job CollisionSystem:CollisionJob reads from the NativeArray CollisionJob.InputVelocities. You are trying to schedule a new job Solver:ApplyGravityAndCopyInputVelocitiesJob, which writes to the same NativeArray (via ApplyGravityAndCopyInputVelocitiesJob.InputVelocities). To guarantee safety, you must include CollisionSystem:CollisionJob as a dependency of the newly scheduled job.
Does anyone have an idea what I’m missing to get this code working?
You can add a synch point after yout job with collisionHandle.Complete().
Depending on the logic of your game, maybe you could change to an ITriggerEventsJob which don’t need a synchpoint.
If your system is updated after StepPhysicsWorld and before EndFramePhysicsSystem, you can also add collisionHandle to EndFramePhysicsSystem.HandlesToWaitFor. HandlesToWaitFor is effectively Complete’d on the next time through the BuildPhysicsWorld system update.
I also have this problem and it is happening not everywhere. I created Main scene and it was working okay with it but when I added new scene for Play Mode testing this errors appear no matter how I combine handles.
Here is my code right now:
[UpdateAfter(typeof(EndFramePhysicsSystem))]
public class TickDamagePlayerOnCollisionSystem : JobComponentSystem
{
private BuildPhysicsWorld _buildPhysicsWorldSystem;
private StepPhysicsWorld _stepPhysicsWorldSystem;
private EntityQuery _playerGroup;
protected override void OnCreate()
{
_buildPhysicsWorldSystem = World.GetOrCreateSystem<BuildPhysicsWorld>();
_stepPhysicsWorldSystem = World.GetOrCreateSystem<StepPhysicsWorld>();
_playerGroup = GetEntityQuery(new EntityQueryDesc
{
All = new ComponentType[]
{
typeof(PlayerTag),
typeof(Health)
},
});
}
[BurstCompile]
public struct TickDamagePlayerOnCollisionJob : ICollisionEventsJob
{
[ReadOnly] [DeallocateOnJobCompletion] public NativeArray<Entity> players;
public ComponentDataFromEntity<Health> healths;
public ComponentDataFromEntity<TickDamage> TickDamages;
public void Execute(CollisionEvent collisionEvent)
{
var entityA = collisionEvent.Entities.EntityA;
var entityB = collisionEvent.Entities.EntityB;
if (!TickDamages.Exists(entityA) || !players.Contains(entityB))
{
var temp = entityA;
entityA = entityB;
entityB = temp;
}
if (!TickDamages.Exists(entityA) || !players.Contains(entityB))
{
return;
}
var tick = TickDamages[entityA];
if (!(tick.CurrentTime > tick.Time)) return;
var health = healths[entityB];
health.Value -= tick.Value;
healths[entityB] = health;
tick.CurrentTime = 0;
TickDamages[entityA] = tick;
}
}
protected override JobHandle OnUpdate(JobHandle inputDeps)
{
var jobHandle = new TickDamagePlayerOnCollisionJob
{
players = _playerGroup.ToEntityArray(Allocator.TempJob),
healths = GetComponentDataFromEntity<Health>(),
TickDamages = GetComponentDataFromEntity<TickDamage>()
}.Schedule(_stepPhysicsWorldSystem.Simulation, ref _buildPhysicsWorldSystem.PhysicsWorld, inputDeps);
return jobHandle;
}
}
use the ITriggerEventsJob if you don’t need the collider to interract with physics
shcedule your ICollisionEvtnesJob after the StepPhysicsSystem and before the EndFramePhysicsSytem and either
a) add a synch point with Complete()
b) inject your jobhandle to EndFramePhysicsSystem with EndFramePhysicsSystem.HandlesToWaitFor
Thank you!
So as I understood you must process collider events between StepPhysicsWorld and EndFramePhysicsSystem. I am not sure what is the reason for it but I think it is somehow related to how collider event system is working.
I got that idea about processing ColliderEvents after EndFramePhysicsSystem from UnityPhysicsSample.
I added attribute to update system after StepPhysicsWorld and injected handle to EndFrame to make sure that it will complete before physics processing complete.