Search Unity

Sample code doesn't run in my project.

Discussion in 'Physics for ECS' started by Krajca, Feb 21, 2021.

  1. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    I'm currently building a breakout game as a DOTS exercise. I needed collision detection so I tried to use code from "2d2. Events - Contact" sample as a base. A plain copy of CollisionEventImpulseSystem with a change of components for the desired ones got me a few errors.

    InvalidOperationException: The previously scheduled job BallBlockCollisionEventSystem:BallBlockCollisionJob reads from the Unity.Collections.NativeArray`1[Unity.Physics.Velocity] BallBlockCollisionJob.EventReader.m_InputVelocities. You are trying to schedule a new job Solver:parallelApplyGravityAndCopyInputVelocitiesJob, which writes to the same Unity.Collections.NativeArray`1[Unity.Physics.Velocity] (via ParallelApplyGravityAndCopyInputVelocitiesJob.InputVelocities). To guarantee safety, you must include BallBlockCollisionEventSystem:BallBlockCollisionJob as a dependency of the newly scheduled job.
    Unity.Jobs.LowLevel.Unsafe.JobsUtility.ScheduleParallelFor (Unity.Jobs.LowLevel.Unsafe.JobsUtility+JobScheduleParameters& parameters, System.Int32 arrayLength, System.Int32 innerloopBatchCount) (at <31d5d65b32ec483292e13e8ae4100b93>:0)
    Unity.Jobs.IJobParallelForExtensions.Schedule[T] (T jobData, System.Int32 arrayLength, System.Int32 innerloopBatchCount, Unity.Jobs.JobHandle dependsOn) (at <31d5d65b32ec483292e13e8ae4100b93>:0)
    Unity.Physics.Solver.ScheduleApplyGravityAndCopyInputVelocitiesJob (Unity.Collections.NativeArray`1[T] motionVelocities, Unity.Collections.NativeArray`1[T] inputVelocities, Unity.Mathematics.float3 gravityAcceleration, Unity.Jobs.JobHandle inputDeps, System.Boolean multiThreaded) (at Library/PackageCache/com.unity.physics@0.6.0-preview.3/Unity.Physics/Dynamics/Solver/Solver.cs:159)
    Unity.Physics.Simulation.ScheduleStepJobs (Unity.Physics.SimulationStepInput input, Unity.Physics.SimulationCallbacks callbacksIn, Unity.Jobs.JobHandle inputDeps, System.Boolean multiThreaded) (at Library/PackageCache/com.unity.physics@0.6.0-preview.3/Unity.Physics/Dynamics/Simulation/Simulation.cs:349)
    Unity.Physics.Systems.StepPhysicsWorld.OnUpdate () (at Library/PackageCache/com.unity.physics@0.6.0-preview.3/Unity.Physics/ECS/Base/Systems/StepPhysicsWorld.cs:94)
    Unity.Entities.SystemBase.Update () (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/SystemBase.cs:412)
    Unity.Entities.ComponentSystemGroup.UpdateAllSystems () (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:472)
    UnityEngine.Debug:LogException(Exception)
    Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/Stubs/Unity/Debug.cs:19)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:477)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:423)
    Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystem.cs:114)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:472)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:417)
    Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystem.cs:114)
    Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ScriptBehaviourUpdateOrder.cs:333)
    UnityEditor.EditorApplication:Step()
    Unity.Entities.Editor.LiveLinkToolbar:DrawPlaybar(CommandExecuteContext) (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities.Editor/LiveLink/LiveLinkToolbar.cs:82)
    UnityEngine.GUIUtility:processEvent(Int32, IntPtr, Boolean&)

    InvalidOperationException: The previously scheduled job Broadphase:prepareStaticBodyDataJob writes to the Unity.Collections.NativeArray`1[Unity.Physics.CollisionFilter] PrepareStaticBodyDataJob.FiltersOut. You are trying to schedule a new job DisplayCollisionEventsSystem:DisplayCollisionEventsJob, which reads from the same Unity.Collections.NativeArray`1[Unity.Physics.CollisionFilter] (via DisplayCollisionEventsJob.UserJobData.World.CollisionWorld.Broadphase.m_StaticTree.BodyFilters). To guarantee safety, you must include Broadphase:prepareStaticBodyDataJob as a dependency of the newly scheduled job.
    Unity.Jobs.LowLevel.Unsafe.JobsUtility.Schedule (Unity.Jobs.LowLevel.Unsafe.JobsUtility+JobScheduleParameters& parameters) (at <31d5d65b32ec483292e13e8ae4100b93>:0)
    Unity.Physics.ICollisionEventJobExtensions.ScheduleUnityPhysicsCollisionEventsJob[T] (T jobData, Unity.Physics.ISimulation simulation, Unity.Physics.PhysicsWorld& world, Unity.Jobs.JobHandle inputDeps) (at Library/PackageCache/com.unity.physics@0.6.0-preview.3/Unity.Physics/Dynamics/Simulation/ICollisionEventsJob.cs:78)
    Unity.Physics.ICollisionEventJobExtensions.Schedule[T] (T jobData, Unity.Physics.ISimulation simulation, Unity.Physics.PhysicsWorld& world, Unity.Jobs.JobHandle inputDeps) (at Library/PackageCache/com.unity.physics@0.6.0-preview.3/Unity.Physics/Dynamics/Simulation/ICollisionEventsJob.cs:38)
    Unity.Physics.Authoring.DisplayCollisionEventsSystem.OnUpdate () (at Library/PackageCache/com.unity.physics@0.6.0-preview.3/Unity.Physics.Hybrid/Utilities/DebugDisplay/DisplayCollisionEventsSystem.cs:45)
    Unity.Entities.SystemBase.Update () (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/SystemBase.cs:412)
    Unity.Entities.ComponentSystemGroup.UpdateAllSystems () (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:472)
    UnityEngine.Debug:LogException(Exception)
    Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/Stubs/Unity/Debug.cs:19)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:477)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:423)
    Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystem.cs:114)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:472)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystemGroup.cs:417)
    Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ComponentSystem.cs:114)
    Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities/ScriptBehaviourUpdateOrder.cs:333)
    UnityEditor.EditorApplication:Step()
    Unity.Entities.Editor.LiveLinkToolbar:DrawPlaybar(CommandExecuteContext) (at Library/PackageCache/com.unity.entities@0.17.0-preview.41/Unity.Entities.Editor/LiveLink/LiveLinkToolbar.cs:82)
    UnityEngine.GUIUtility:processEvent(Int32, IntPtr, Boolean&)

    Here is my code:
    Code (CSharp):
    1. [UpdateInGroup(typeof(FixedStepSimulationSystemGroup))]
    2.     [UpdateAfter(typeof(EndFramePhysicsSystem))]
    3.     public class BallBlockCollisionEventSystem : SystemBase
    4.     {
    5.         private BuildPhysicsWorld buildPhysicsWorldSystem;
    6.         private StepPhysicsWorld stepPhysicsWorldSystem;
    7.         private EntityQuery blockEntityQuery;
    8.  
    9.         protected override void OnCreate()
    10.         {
    11.             buildPhysicsWorldSystem = World.GetOrCreateSystem<BuildPhysicsWorld>();
    12.             stepPhysicsWorldSystem = World.GetOrCreateSystem<StepPhysicsWorld>();
    13.             blockEntityQuery = GetEntityQuery(new EntityQueryDesc
    14.             {
    15.                 All = new ComponentType[]
    16.                 {
    17.                     typeof(Block)
    18.                 }
    19.             });
    20.         }
    21.  
    22.         protected override void OnUpdate()
    23.         {
    24.             if (blockEntityQuery.CalculateEntityCount() == 0)
    25.             {
    26.                 return;
    27.             }
    28.  
    29.             Dependency = new BallBlockCollisionJob
    30.             {
    31.                 BlocksGroup = GetComponentDataFromEntity<Block>(),
    32.                 BallGroup = GetComponentDataFromEntity<Ball>(),
    33.             }.Schedule(stepPhysicsWorldSystem.Simulation,
    34.                 ref buildPhysicsWorldSystem.PhysicsWorld, Dependency);
    35.         }
    36.  
    37.         [BurstCompile]
    38.         private struct BallBlockCollisionJob : ICollisionEventsJob
    39.         {
    40.             public ComponentDataFromEntity<Block> BlocksGroup;
    41.             public ComponentDataFromEntity<Ball> BallGroup;
    42.  
    43.             public void Execute(CollisionEvent collisionEvent)
    44.             {
    45.                 var entityA = collisionEvent.EntityA;
    46.                 var entityB = collisionEvent.EntityB;
    47.  
    48.                 var isABall = BallGroup.HasComponent(entityA);
    49.                 var isBBall = BallGroup.HasComponent(entityB);
    50.  
    51.                 var isABlock = BlocksGroup.HasComponent(entityA);
    52.                 var isBBlock = BlocksGroup.HasComponent(entityB);
    53.              
    54.  
    55.                 if (isABall && isBBlock)
    56.                 {
    57.                     var ball = BallGroup[entityA];
    58.                     var block = BlocksGroup[entityB];
    59.  
    60.                     ball.PointAmount += block.PointsForDestruction;
    61.                     block.MarkForDestruction = true;
    62.                 }
    63.  
    64.                 if (isABlock && isBBall)
    65.                 {
    66.                     var ball = BallGroup[entityB];
    67.                     var block = BlocksGroup[entityA];
    68.  
    69.                     ball.PointAmount += block.PointsForDestruction;
    70.                     block.MarkForDestruction = true;
    71.                 }
    72.             }
    73.         }
    74.     }
    InvalidOperationException: The previously scheduled job BallBlockCollisionEventSystem:BallBlockCollisionJob reads from the Unity.Collections.NativeArray`1[Unity.Physics.Velocity] BallBlockCollisionJob.EventReader.m_InputVelocities. You are trying to schedule a new job Solver:parallelApplyGravityAndCopyInputVelocitiesJob, which writes to the same Unity.Collections.NativeArray`1[Unity.Physics.Velocity] (via ParallelApplyGravityAndCopyInputVelocitiesJob.InputVelocities). To guarantee safety, you must include BallBlockCollisionEventSystem:BallBlockCollisionJob as a dependency of the newly scheduled job.
    There is a clear error message, but "BallBlockCollisionEventSystem:BallBlockCollisionJob" doesn't read from any "Unity.Collections.NativeArray`1[Unity.Physics.Velocity]" so I'm lost in what exactly is wrong here. Can anyone explain what's going on, as sample code works just fine?
     
  2. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    Probably adding
    stepPhysicsWorldSystem.AddInputDependency(Dependency);
    at the end of your OnUpdate should fix the error. What is happening is that the StepPhysicsWorldSystem is trying to schedule a job that modifies the PhysicsWorld but it doesn't know about the CollisionJob that you scheduled in your system.
     
  3. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    It worked. Thanks!
    The question is why I need it and the sample code doesn't?
     
    Last edited: Feb 21, 2021
  4. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    Not sure if you missed something or you are looking at an outdated code, but they actually do: https://github.com/Unity-Technologi...ripts/DynamicBufferCollisionEventAuthoring.cs


    Code (CSharp):
    1.     // lines 164 and 165
    2.     [UpdateAfter(typeof(StepPhysicsWorld))]
    3.     [UpdateBefore(typeof(EndFramePhysicsSystem))]
    4.  
    5.             // line 290
    6.             Dependency = JobHandle.CombineDependencies(m_StepPhysicsWorld.FinalSimulationJobHandle, Dependency);
    7.  
    8.             // line 344
    9.             m_EndFramePhysicsSystem.AddInputDependency(Dependency);
     
  5. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
  6. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    Seems like an error on the samples. If you are not getting any error when running it then it is because the scheduled job is just happening to complete before the StepPhysicsWorldSystem tries to Schedule anything, so just a coincidence.
     
    Krajca likes this.
  7. Krajca

    Krajca

    Joined:
    May 6, 2014
    Posts:
    347
    That explains a lot. Thank you for your help!
     
  8. milos85miki

    milos85miki

    Joined:
    Nov 29, 2019
    Posts:
    197
    Yes, it was a bug, thank you! We changed the system to run between ExportPhysicsWorld and EndFramePhysicsSystem.
    BTW, the safest thing to do if your system updates after EndFramePhysicsSystem (and needs PhysicsWorld) is to call
    m_BuildPhysicsWorld.AddInputDependencyToComplete(Dependency);
    . The next release of UnityPhysics will make these things much simpler and better explained in the documentation.
     
    jconsole and Krajca like this.
  9. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    I'm really stuck on this... I copied CollisionEventImpulseAuthoring.cs into my project and ran it with no errors (it never got called of course because none of my entities have a CollisionEventImpulse component on them).

    Then I replaced CollisionEventImpulse with HealthData because I have some entities that have health data... where HealthData looks like:

    Code (CSharp):
    1. using Unity.Entities;
    2. public struct HealthData : IComponentData { public float health; }
    Seems simple enough... but I keep getting this error:

    InvalidOperationException: The ComponentDataFromEntity<HealthData> CollisionEventImpulseJob.UserJobData.ColliderEventImpulseGroup must be marked [ReadOnly] in the job CollisionEventImpulseSystem:CollisionEventImpulseJob, because the container itself is marked read only.
    Unity.Jobs.LowLevel.Unsafe.JobsUtility.Schedule (Unity.Jobs.LowLevel.Unsafe.JobsUtility+JobScheduleParameters& parameters) (at <be28439f7b014fc38435d198c0640ff7>:0)
    Unity.Physics.ICollisionEventJobExtensions.ScheduleUnityPhysicsCollisionEventsJob[T] (T jobData, Unity.Physics.ISimulation simulation, Unity.Physics.PhysicsWorld& world, Unity.Jobs.JobHandle inputDeps) (at Library/PackageCache/com.unity.physics@0.6.0-preview.3/Unity.Physics/Dynamics/Simulation/ICollisionEventsJob.cs:78)
    Unity.Physics.ICollisionEventJobExtensions.Schedule[T] (T jobData, Unity.Physics.ISimulation simulation, Unity.Physics.PhysicsWorld& world, Unity.Jobs.JobHandle inputDeps) (at Library/PackageCache/com.unity.physics@0.6.0-preview.3/Unity.Physics/Dynamics/Simulation/ICollisionEventsJob.cs:38)
    CollisionEventImpulseSystem.OnUpdate () (at Assets/Scripts/Systems/CollisionEventImpulseSystem.cs:95)
    Unity.Entities.SystemBase.Update () (at Library/PackageCache/com.unity.entities@0.17.0-preview.42/Unity.Entities/SystemBase.cs:412)
    Unity.Entities.ComponentSystemGroup.UpdateAllSystems () (at Library/PackageCache/com.unity.entities@0.17.0-preview.42/Unity.Entities/ComponentSystemGroup.cs:472)
    UnityEngine.Debug:LogException(Exception)
    Unity.Debug:LogException(Exception) (at Library/PackageCache/com.unity.entities@0.17.0-preview.42/Unity.Entities/Stubs/Unity/Debug.cs:19)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.17.0-preview.42/Unity.Entities/ComponentSystemGroup.cs:477)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.42/Unity.Entities/ComponentSystemGroup.cs:423)
    Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.17.0-preview.42/Unity.Entities/ComponentSystem.cs:114)
    Unity.Entities.ComponentSystemGroup:UpdateAllSystems() (at Library/PackageCache/com.unity.entities@0.17.0-preview.42/Unity.Entities/ComponentSystemGroup.cs:472)
    Unity.Entities.ComponentSystemGroup:OnUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.42/Unity.Entities/ComponentSystemGroup.cs:417)
    Unity.Entities.ComponentSystem:Update() (at Library/PackageCache/com.unity.entities@0.17.0-preview.42/Unity.Entities/ComponentSystem.cs:114)
    Unity.Entities.DummyDelegateWrapper:TriggerUpdate() (at Library/PackageCache/com.unity.entities@0.17.0-preview.42/Unity.Entities/ScriptBehaviourUpdateOrder.cs:333)

    I have been trying to fix this error all afternoon! I tried doing things like changing typeof(HealthData) to ComponentType.ReadOnly(typeof(HealthData)) etc, but nothing works. It makes no sense... why does it work when the component's name is CollisionEventImpulse but fail when the component's name is HealthData???? There are only 3 differences that I can think of:

    1. One is named ColllisionEventImpulse and the other is named HealthData.
    2. CollisionEventImpulse contains a float3 but HealthData contains a float.
    3. HealthData is on some of my entities and some of my other systems read HealthData. There is one system that writes to it if there is damage (but that's not happening right now because there is no applied damage).
    My guess is that it has something to do with #3. I don't get it though... I have not read anything in the docs about not being allowed to read a component from two different jobs, and even if there is such a rule, that "must be marked [ReadOnly] in the job because the container itself is marked read only" error isn't exactly very helpful on that front.

    This is sooooooooo frustrating!!!
     
  10. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    I'm still just as stuck as before.... but here are some new things I learned:

    .......First thing.........
    I copied CollisionEventImpulseAuthoring.cs from EntityComponentSystemSamples-master\UnityPhysicsSamples\Assets\Demos\2. Setup\2d. Events\Scripts and pasted it into my project without modifying anything. Then I added a cube to my project, named it Repulsor Cube, attached PhysicsShape, PhysicsBody, ConvertToEntity, and CollisionEventImpulseAuthoring. I changed all the settings to match the Repulsor Cube in the Unity Physics Samples. I still gives the error: "InvalidOperationException: The ComponentDataFromEntity<CollisionEventImpulse> CollisionEventImpulseJob.UserJobData.ColliderEventImpulseGroup must be marked [ReadOnly] in the job CollisionEventImpulseSystem:CollisionEventImpulseJob, because the container itself is marked read only."

    .....Second thing....
    I changed all the package versions in the UnityPhysics 2d Events sample to match mine. Especially physics, entities, jobs, and burst. In my project, Burst is 1.6.0-pre2 because I'm using A* Pathfinding Project and when I imported it, the Burst version was automatically incremented even though Hybrid Renderer calls for a much older version. The Unity Physics demo still works fine.

    So..... The exact same code works in one project and is broken in a different one. This makes me think it must be some sort of project setting or package version that is causing the error, but after 12 hours of comparing the two projects and trying to figure out what the difference is, I can't determine the root of the problem. Perhaps I will start a new project all over again and import all my assets into it. That fixed a different problem once.
     
    brunocoimbra likes this.
  11. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    @brunocoimbra Great news! @eizenhorn solved it! I had imported an asset that had a [ReadOnly] attribute that was intended for marking fields in the editor/inspector as read only, and it was conflicting with [Unity.Collections.ReadOnly].
     
    brunocoimbra likes this.
  12. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    @brunocoimbra From an architectural and efficiency standpoint, is it recommended to have multiple collision detection systems, or should we try to combine all of them into one? For example, should I have one ICollisionEventsJob that scans for projectiles and applies damage, another one that plays a sound when nav agents bump into each other or walls, and another that does something else when different things collide.... or is it better to combine all of the collision events into a single job? Or does it even matter?
     
  13. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    It is hard to give you an answer that fits all cases as each use-case have their own external requirements, the following is how I think around it:

    - Each CollisionEventJob can use up to 1 thread at a time and branching can downgrade Burst performance, so I would avoid doing so many different things inside a single job.
    - I tend to use an event-like approach when dealing with Collision and Trigger events to avoid using multiple differents ComponentDataFromEntity inside a single Collision/Trigger job, this way I can simply add/remove/enable/disable specific components that are used for another system to actually process the Collision/Trigger.

    An example from an approach I've had in one of my test projects was:

    - Create a CollisionEnter/Stay/Exit logic where each one has their own buffer and are populated through CollisionJobs (the samples have a nice start for that)
    - Have different systems that use one (or more) of those buffers combined with other components to do the logic and early-out if the buffer is empty, like
    Entities.ForEach((in DamageForce damageForce, in DynamicBuffer<CollisionEnter> collisionEnterBuffer) { ... })
    (can't recall the Entities.ForEach syntax for buffers, I am more used to jobs structs, but you got the idea)
     
    lclemens likes this.
  14. lclemens

    lclemens

    Joined:
    Feb 15, 2020
    Posts:
    761
    Awesome! Thanks, I really like the idea of using events. I use tertle's event system so I might use that, but adding and removing CollisionEnter/Exit components seems reasonable also.
     
  15. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    I actually never add/remove CollisionEnter/Exit buffers, I keep those even if they are empty, the Jobs just early out if they are empty. But in my case enter/exit could happen very frequently (bullet hell-like game), so adjust to what makes more sense to your use case.