Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Dependency Management for Body Manipulation

Discussion in 'Physics for ECS' started by nicolasgramlich, Jun 6, 2020.

  1. nicolasgramlich

    nicolasgramlich

    Joined:
    Sep 21, 2017
    Posts:
    231
    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:

    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Mathematics;
    3. using Unity.Physics;
    4.  
    5. namespace Systems.Simulation {
    6.     [UpdateInGroup(typeof(SimulationSystemGroup))]
    7.     public class WhatTheHeckSystem : SystemBase {
    8.         protected override void OnUpdate() {
    9.             Entities.ForEach((Entity entity, ref PhysicsVelocity physicsVelocity) => { physicsVelocity.Linear = new float3(0, 10, 0); }).ScheduleParallel();
    10.         }
    11.     }
    12. }
    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:
    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
    :
    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.
    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Jobs;
    3. using Unity.Mathematics;
    4. using Unity.Physics;
    5. using Unity.Physics.Systems;
    6.  
    7. namespace Systems.Simulation {
    8.    [UpdateInGroup(typeof(SimulationSystemGroup))]
    9.    public class UnitWhatTheHeckSystem : SystemBase {
    10.       private BuildPhysicsWorld buildPhysicsWorld;
    11.       private StepPhysicsWorld stepPhysicsWorld;
    12.       private ExportPhysicsWorld exportPhysicsWorld;
    13.  
    14.       protected override void OnCreate() {
    15.          buildPhysicsWorld = World.GetOrCreateSystem<BuildPhysicsWorld>();
    16.          stepPhysicsWorld = World.GetOrCreateSystem<StepPhysicsWorld>();
    17.          exportPhysicsWorld = World.GetOrCreateSystem<ExportPhysicsWorld>();
    18.       }
    19.  
    20.       protected override void OnUpdate() {
    21.          Dependency = JobHandle.CombineDependencies(Dependency, buildPhysicsWorld.FinalJobHandle);
    22.          Dependency = JobHandle.CombineDependencies(Dependency, stepPhysicsWorld.FinalJobHandle);
    23.          Dependency = JobHandle.CombineDependencies(Dependency, exportPhysicsWorld.FinalJobHandle);
    24.          Entities.ForEach((Entity entity, ref PhysicsVelocity physicsVelocity) => { physicsVelocity.Linear = new float3(0, 10, 0); }).ScheduleParallel();
    25.       }
    26.    }
    27. }
    28.  
    Unfortunately adding those dependencies does not work (no force is applied), probably because the system is still ordered between
    BuildPhysicsWorld
    and
    ExportPhysicsWorld
    .

    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))]
    :

    Code (CSharp):
    1. using Unity.Entities;
    2. using Unity.Mathematics;
    3. using Unity.Physics;
    4. using Unity.Physics.Systems;
    5.  
    6. namespace Systems.Simulation {
    7.    [UpdateAfter(typeof(ExportPhysicsWorld))]
    8.    public class UnitWhatTheHeckSystem : SystemBase {
    9.       protected override void OnUpdate() {
    10.          Entities.WithAll<Unit>().ForEach((Entity entity, ref PhysicsVelocity physicsVelocity) => { physicsVelocity.Linear = new float3(0, 10, 0); }).ScheduleParallel();
    11.       }
    12.    }
    13. }
    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. :)
     
  2. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    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).
     
    nicolasgramlich likes this.
  3. nicolasgramlich

    nicolasgramlich

    Joined:
    Sep 21, 2017
    Posts:
    231
    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
    ?
     
  4. brunocoimbra

    brunocoimbra

    Joined:
    Sep 2, 2015
    Posts:
    679
    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.
     
    nicolasgramlich likes this.
  5. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    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.
     
    nicolasgramlich and brunocoimbra like this.