Search Unity

Manually updating a CollisionWorld after the physics systems update

Discussion in 'Physics for ECS' started by PhilSA, Feb 15, 2021.

  1. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    Here's my use case:
    • I have a character controller that must update after the physics systems (build/step/export)
    • My character systems query the CollisionWorld for obstacles and move the character's kinematic rigidbody
    • I would like to update the CollisionWorld after my character movement so that I can raycast the characters at their up-to date positions later in that frame. Imagine having a game with hitscan-type weapons and fast-moving characters, and the hit detection has to be perfect
    It looks like I could simply manually write to "CollisionWorld.Bodies.WorldFromBody" after my character movement is done, but I'd just like to validate if that would be the correct approach.

    I'm worried that there must be some kind of BVH update step that I'd be missing in this case. I see a "CollisionWorld.UpdateDynamicTree()" that looks promising, but because it uses PhysicsWorld instead of CollisionWorld, I don't think I can use it in my case. My character needs to be netcode-prediction-compatible, and that means only relying on CollisionWorld instead of PhysicsWorld. (netcode's PhysicsHistorySystem only keeps a history of CollisionWorlds; not PhysicsWorlds)

    I think in an ideal world, there would be a possibility to mark some rigidbodies as "dirty" and then call a CollisionWorld.RebuildIncremental() that rebuilds the collision world, but only for bodies that have been set to dirty.
     
    Last edited: Feb 15, 2021
    Occuros likes this.
  2. milos85miki

    milos85miki

    Joined:
    Nov 29, 2019
    Posts:
    197
    Hi @PhilSA, your assumption about BVH update is correct and it's not so simple (nor cheap) to update it.
    CollisionWorld.UpdateDynamicTree() requires PhysicsWorld for 2 reasons:
    1. Needs velocities of all dynamic bodies (and gravity vector), in order to expand body AABBs in BVH
    2. Updates RigidBody.WorldFromBody with precise position based on the center of mass:
    rb.WorldFromBody = math.mul(motionDatas[i].WorldFromMotion, math.inverse(motionDatas[i].BodyFromMotion));


    Are you planning to rebuild the BVH after each character get updated?
    I'd recommend trying to avoid that if possible, since it'll hurt perf. We don't support a partial rebuild at the moment, but even if/when we do you should be aware that in some cases it could be costly or result in suboptimal tree.

    It'd be best if you could get away with just setting sync collision world flag in Physics Step. That would give you an up-to-date BVH when the last job in StepPhysicsWorld is done.

    If characters are not too fast, doing their collision detection via shape cast should be ok (after simulation). They might get into penetration with each other, but will get de-penetrated in next frame. That's how our CC sample works (chars have 0 velocity for physics sim, as we're driving them "manually").

    Hoping this helps or at least clarifies some things, please let me know if there's any more info I could provide.
     
    MNNoxMortem likes this.
  3. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I should've mentioned: my characters also have 0 physicsVelocity, so they aren't moved by the StepPhysicsWorld. They are moved manually after

    The reason why it's necessary for me to update my character positions after the physics step and the sync collision world can be tricky to explain, but it has to do with cases where a character moves against another moving kinematic body. I need to query the up-to-date moving body during my character movement' CastColliders, because otherwise there will be a visible gap between my character and the moving body

    But if a "rebuildIncremental" feature does become possible, I would call it only once after all characters have moved. It would probably also be an opt-in feature due to its cost, but I think it would be useful to some projects that need complete accuracy

    I wonder how viable the following workaround would be? :
    • wait for all my characters to finish their move
    • move all character entities to a separate "CharactersWorld" world
    • Run BuildPhysicsWorld in that world
    • move all characters back to the regular world
    • if I really need up-to-date raycasting, I can raycast characters in that CharactersWorld
     
    Last edited: Feb 18, 2021
    ScriptsEngineer likes this.
  4. milos85miki

    milos85miki

    Joined:
    Nov 29, 2019
    Posts:
    197
    Are you driving those kinematic bodies manually as well (or maybe changing their positions after the character interacts with them)?
    Anyway, I believe that you really need to rebuild the BVH, but not sure how to do it without PhysicsWorld. CollisionWorld just doesn't have all the required data (velocities, for example). Maybe you could do some work before BuildPhysicsWorld and then do the rest after StepPhysicsWorld (with sync collision world ON)?

    If you absolutely need to query after each character moves, maybe it's worth making Broadphase struct public and having an additional Broadphase.Tree with all important objects are driven outside of simulation. You'd have finer control over that tree, it wouldn't interfere with the simulation and you could combine query results from the local tree and the PhysicsWorld to figure out how to drive the characters.
     
  5. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    So here's a summary of what happens:
    1. Physics system update (build/step/export/end)
    2. My character update (uses CastCollider & CalculateDistance to solve all collisions. The end result is moving the translation & rotation of the character entity to the solved position)
    3. TransformGroup update
    4. Render that
    that sounds like it would be a pretty great solution for my use case if it can become possible.

    Another semi-related use case that was brought up in the early days of DOTS physics was this: How could I instantiate 500 prefabs at random points in a circle, but make sure none of them overlap? In order to make this work in DOTS physics, it looks like we'd have no choice but to either instantiate them over 500 frames, or rebuild the physics world 500 times in a single frame (once between each iteration)... I don't necessarily know what exactly I would suggest in order to solve this, but I'm throwing this out there in case you guys can figure something out. It's a use case that isn't that uncommon
     
    Last edited: Feb 20, 2021
  6. milos85miki

    milos85miki

    Joined:
    Nov 29, 2019
    Posts:
    197
    Aham, so it seems that your main concern is potential character overlap after they're all moved to solved positions (which were determined after CastCollider & CalculateDistance, but not looking at updated positions of other characters)? If yes, could you solve character-character depenetration in collision events?

    I'm thinking about moving the characters before BuildPhysicsWorld (using CastCollider & CalculateDistance), letting BuildPhysicsWorld build BVH based on those and then doing character depenetration based on the character-character collision events (can use CastCollider & CalculateDistance to see where the other bodies are or maybe remember results of previous casts and just move the characters back, down those paths). If you check "sync collision world", you would get latest positions of all bodies in the BVH at the end of the step (when StepPhysicsWorld's last job is done).

    You could also try to make local changes to physics package and have a separate Broadphase with characters only. The "regular" PhysicsWorld would still have all bodies, while the separate Broadphase would only contain characters. You can copy their RigidBody, MotionData and MotionVelocity data into new arrays and use them in Broadphase's "immediate" API (like
    public void Build(NativeArray<RigidBody> staticBodies, NativeArray<RigidBody> dynamicBodies, NativeArray<MotionVelocity> motionVelocities, float collisionTolerance, float timeStep, float3 gravity, bool buildStaticTree = true)
    ). Creating a separate PhysicsWorld is also an option, but just using Broadphase directly is probably easier and faster.

    Regarding body placement, I think it's a separate problem usually not so closely tied to a physics engine. Have you seen other engines providing such utils?
    For custom-built solutions, I'd recommend approximating bodies with their AABBs. It reduces the complexity but it's till quite hard.
     
  7. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I tested this code in monobehaviour physics. When I press a key, it instantiates a physics cube prefab, and does a raycast that should hit the cube where it spawned. In this test, the raycast properly detects the cube that was just instantiated on that same frame, and there is no need to wait for the next physics step:
    Code (CSharp):
    1.     void Update()
    2.     {
    3.         if(Input.GetKeyDown(KeyCode.Y))
    4.         {
    5.             Instantiate(prefab, transform.position, transform.rotation);
    6.  
    7.             if(Physics.Raycast(transform.position + (Vector3.forward * 10f), -Vector3.forward, out RaycastHit hit, 30f, -1, QueryTriggerInteraction.Collide))
    8.             {
    9.                 Debug.Log("Hit " + hit.transform.gameObject.name);
    10.             }
    11.         }
    12.     }
    I can also move the rigidbody right after instantiating it, and the raycast will properly detect the hit at the moved position. It's almost like the physics world is being constantly rebuilt at every change that happens, or maybe everytime a query is made (?)

    __________________________

    The main problem with updating before BuildPhysicsWorld is that my characters already need an up-to-date collisionWorld to do CastColliders against. When my characters move against other kinematic bodies that have a velocity, they must cast against the final pose of those moving bodies for that frame. Otherwise there is a visible gap between my characters and the moving bodies, because they're raycasting against an outdated collision world

    The solution of adding my own broadphase sounds interesting, but I can't do it for this project because it is intended to be an asset store product that everyone can use. The solution of creating a separate PhysicsWorld is tempting, though. I think I'll look into that!
     
    Last edited: Feb 22, 2021
  8. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Just to add to the point that separate PhysicsWorld is probably fine for you. Overhead of the PhysicsWorld is really small, it's just a we buffers that will be used only to build the tree. We could try to make this a bit more self contained so that CollisionWorld covers all this...
     
    PhilSA likes this.
  9. desertGhost_

    desertGhost_

    Joined:
    Apr 12, 2018
    Posts:
    260
    Hi @PhilSA

    What solution did you ended up going with?

    Thanks.
     
    GameDeveloper1111 likes this.
  10. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I kinda ended up not having to tackle this issue (yet), so I'm not 100% sure how I would do this.

    However, I think I was wrong about my assumption that I needed this to be all doable in CollisionWorld instead of PhysicsWorld. After implementing an actual online game prototype, I realized that there was actually no reason why I couldn't use PhysicsWorld. The BuildPhysicsWorld update needs to be part of the prediction loop either way, so we're not limited to only using PhysicsWorldHistory (which gives you only CollisionWorld). I could also implement my own PhysicsWorldHistory system that remembers PhysicsWorlds instead of CollisionWorlds, if I really needed that

    So my conclusion is that I would simply make a PhysicsWorld containing only the characters, and use CollisionWorld.UpdateDynamicTree() after the character update.

    I'm curious to see the DOTS demo team try to implement an actual networked game using DOTS NetCode + Physics though. Not because I think it wouldn't work, but because I have a lot of uncertainties about how to best implement this, and an example would be great
     
    Last edited: Apr 7, 2021
    GameDeveloper1111 likes this.
  11. DreamersINC

    DreamersINC

    Joined:
    Mar 4, 2015
    Posts:
    131
    @PhilSA Did you ever find a solution here? Any chance you can share some code ?