Dependency Management for Body Manipulation

Hello everyone,

I just spent almost an entire day trying to figure out why my system wouldn't apply forces to my bodies. Long story short I ran into systems ordering issue, where the name of the class will determine where it's scheduled. The real question i have in the end is "What is the proper/official way to formulate dependencies/UpdateBefore/UpdateAfter to interact with physics in ECS".

# The problem

I boiled it down to this trivial example:

using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;

namespace Systems.Simulation {
    [UpdateInGroup(typeof(SimulationSystemGroup))]
    public class WhatTheHeckSystem : SystemBase {
        protected override void OnUpdate() {
            Entities.ForEach((Entity entity, ref PhysicsVelocity physicsVelocity) => { physicsVelocity.Linear = new float3(0, 10, 0); }).ScheduleParallel();
        }
    }
}

Here's what happens when the class is called WhatTheHeckSystem. The force is applied as expected ( and the units take off like cute little rocket ships) because the system is ordered before BuildPhysicsWorld and any other physics world:
5946335--636722--upload_2020-6-5_22-29-35.png

Here's what happens when the class is called UnitWhatTheHeckSystem. The force is not applied since the system gets ordered after BuildPhysicsWorld but before ExportPhysicsWorld:
5946335--636719--upload_2020-6-5_22-27-49.png

It is understandable that because I give no constraints to system order the system ends up in an indeterminate spot. Fair. I learned my lesson here for sure :)

# The docs...

https://docs.unity3d.com/Packages/com.unity.physics@0.3/manual/interacting_with_bodies.html

The current physics example in the physics package docs are quite outdated, do not specify any system ordering/dependencies and rely on the fact that the system is arbitrarily ordered into the correct spot, given its name. Exhibit A:

# Attempts at fixing the problem

## Attempt 1: CombineDependencies
A friendly soul on discord suggested to add dependencies on the physics world to solve this issue.

using Unity.Entities;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Systems;

namespace Systems.Simulation {
   [UpdateInGroup(typeof(SimulationSystemGroup))]
   public class UnitWhatTheHeckSystem : SystemBase {
      private BuildPhysicsWorld buildPhysicsWorld;
      private StepPhysicsWorld stepPhysicsWorld;
      private ExportPhysicsWorld exportPhysicsWorld;

      protected override void OnCreate() {
         buildPhysicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
         stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
         exportPhysicsWorld = World.GetOrCreateSystem<ExportPhysicsWorld>();
      }

      protected override void OnUpdate() {
         Dependency = JobHandle.CombineDependencies(Dependency, buildPhysicsWorld.FinalJobHandle);
         Dependency = JobHandle.CombineDependencies(Dependency, stepPhysicsWorld.FinalJobHandle);
         Dependency = JobHandle.CombineDependencies(Dependency, exportPhysicsWorld.FinalJobHandle);
         Entities.ForEach((Entity entity, ref PhysicsVelocity physicsVelocity) => { physicsVelocity.Linear = new float3(0, 10, 0); }).ScheduleParallel();
      }
   }
}

Unfortunately adding those dependencies does not work (no force is applied), probably because the system is still ordered between BuildPhysicsWorld and ExportPhysicsWorld.

5946335--636731--upload_2020-6-5_22-41-47.png

## Attempt: [UpdateAfter(typeof(ExportPhysicsWorld))] /
[UpdateBefore(typeof(BuildPhysicsWorld))]

One solution that actually works is to force the execution with [UpdateAfter(typeof(ExportPhysicsWorld))] or [UpdateBefore(typeof(BuildPhysicsWorld))]:

using Unity.Entities;
using Unity.Mathematics;
using Unity.Physics;
using Unity.Physics.Systems;

namespace Systems.Simulation {
   [UpdateAfter(typeof(ExportPhysicsWorld))]
   public class UnitWhatTheHeckSystem : SystemBase {
      protected override void OnUpdate() {
         Entities.WithAll<Unit>().ForEach((Entity entity, ref PhysicsVelocity physicsVelocity) => { physicsVelocity.Linear = new float3(0, 10, 0); }).ScheduleParallel();
      }
   }
}

5946335--636734--upload_2020-6-5_22-50-33.png

## Attempt: [UpdateInGroup(typeof(LateSimulationSystemGroup))]

Similar to the previous solution this works fine as well.

# TL;DR
So again, the real question I have is:

What is the proper/official way to formulate dependencies/UpdateBefore/UpdateAfter to interact with physics in ECS?

Having a clear answer on this would be immensely helpful for anyone that does even the most trivial work with physics in ECS.

Thanks in advance for reading this far and/or an answer. :)

AFAIK if you want the physics to be applied on the same frame, you should use [UpdateBefore(typeof(BuildPhysicsWorld))] and if you don't mind to delay it one frame then you could use [UpdateAfter(typeof(ExportPhysicsWorld))]. This is pretty similar with how the TransformSystemGroup works (which makes me wonder why there isn't a PhysicsSystemGroup).

1 Like

Thanks for the relpy.

What I'm wondering is if [UpdateAfter(typeof(ExportPhysicsWorld))] by itself is really enough though, because if I understand UpdateAfter correctly, it'll only guarantee that the OnUpdate of my system runs after the OnUpdate of the ExportPhysicsWorld system, while the jobs of ExportPhysicsWorld could still be running and potentially mess with me writing to PhysicsVelocity, right?

In the end would the proper solution be to both [UpdateAfter(typeof(ExportPhysicsWorld))] as well as Dependency = JobHandle.CombineDependencies(Dependency, exportPhysicsWorld.FinalJobHandle);?

Or is [UpdateAfter(typeof(ExportPhysicsWorld))] enough because it behaves more like this:

Not sure if it does, but let's assume ExportPhysicsWorld DOES touches/writes PhysicsVelocity, then it would be enough to only do [UpdateAfter(typeof(ExportPhysicsWorld))], because ExportPhysicsWorld is the first to "reserve" PhysicsVelocity and the jobs in my system are requesting it after, causing my Job to actually run after the job(s) of ExportPhysicsWorld that writes to PhysicsVelocity.
Therefore preventing a race condition, without me having to explicitly depend on the exportPhysicsWorld.FinalJobHandle?

If you schedule a job that writes to PhysicsVelocity then Unity automatic dependency system will detect that and add the dependency for you automatically (this is also true for any other components and any other system). The same way, if you try to run something that writes to some component on main-thread after a job was scheduled for that same component then the safety check will come into play and warn you that you need to call Dependency.Complete() before writing to that component.

1 Like

Currently you can't really explicitly schedule your job before a physics system, unless it's EndFramePhysicsSystem. But it's coming in the next release!

What @brunocoimbra said is correct, if there is a data dependency that will be added automatically. But in both cases, you still need an UpdateBefore and UpdateAfter, to order your OnUpdate() calls correctly, because that will make sure the dependencies produced are correct.

2 Likes