Search Unity

Question Delay-free "rigidbody parenting" with kinematic bodies?

Discussion in 'Physics for ECS' started by PhilSA, Jun 24, 2022.

  1. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I think the subject was brought up a few times in the past, and I'm wondering if someone else has already solved this. Imagine you want a kinematic rigidbody (like some shield that rotates around a character) to be parented to another rigidbody (like a character). How would you do it?

    __________________________

    Problem

    Rigidbodies must be root objects, so we can't use regular transform parenting. Instead, I assume we could write our own "parenting" system that makes a rigidbody copy the world pos/rot of a target "empty" socket transform entity which is a child of the character. Remember; we need to use the interpolated pose of the target entity to follow, so we must rely on its LocalToWorld. But it's like there is an unavoidable 1-frame delay here. We can choose between 4 different things to be outdated:
    • If we move our shield before the TransformSystemGroup, we'll be moving it to an outdated position (the LocalToWorld of the child entity to follow won't be computed)
    • If we move our shield after the TransformSystemGroup, our shield LocalToWorlds won't be up to date (so it'll be rendered in the past)
    • If we move the shield before the physics update, we'll be using the outdated rigidbody pose (before the bpdy moved with velocity)
    • If we move the shield after the physics update, our shield's colliders in the CollisionWorld will be stuck one frame in the past, so the raycasting won't be accurate

    __________________________

    Current solution (pretty bad)

    We could decide to update the TransformSystemGroup and the BuildPhysicsWorld systems twice per frame in order to solve the issue. It would go like this:
    1. Character rigidbody moves during fixedupdate
    2. Transforms update gives us the interpolated world pose of the child entity to follow
    3. Our custom rigidbody parenting system places the rotating shield body where it needs to be
    4. Do another BuildPhysicsWorld update, so our updated shield colliders can be raycasted against accurately
    5. Do another Transforms update so any child entities of the rotating shield will be rendered at the correct pose (we need this because we moved the shield after the transforms update)
    This sounds like a very expensive solution, because of steps 4 and 5. I'm not really sure if we could manually update the pose of our rigidbody in the PhysicsWorld in order to solve step 4... because the BVH would have to be recalculated too I believe?

    And I think we'll still have the issue of collision/trigger events being 1 frame late on the shield, even after all this. We'd need a way to call a "ProcessPhysicsEvents" update independently from the StepPhysicsWorld update?

    An alternative would be to "predict" the final pose of a certain transform based on the info we have prior to the physics update. But this won't work if collision-solving makes our prediction invalid, or if the thing we should be following is an animation bone (unless animation updates before the FixedStepGroup?). I also have a specific case (character controller) where the character movement update necessarily has to be after the physics update in order to properly deal with movement against moving bodies, so this prediction approach wouldn't be suitable either way

    __________________________

    What would make things easier/better

    I assume the reason why Unity.Physics avoids support for the transform hierarchy must be because of performance reasons? (it's a whole bunch of checks & calculations we can just skip) So I'm trying to think of alternatives that don't involve adding support for that. I also want to avoid solutions based on fixed joints, because that won't work in the case of hitboxes attached to animation bones (the bones themselves won't be rigidbodies)

    An "Instant Transforms API" would allow us to avoid step 5. I'd imagine this in the form of a "TransformsWorld" struct that contains all kinds of ComponentDataFromEntity<> and a bunch of functions related to transforms. This TransformsWorld would allow us to immediately calculate the LocalToWorlds of all entities in the hierarchy under a certain entity. Calculating LocalToWorlds instantly like this would be less efficient than what the TransformSystemGroup does, but if we have very few transforms we need to recalculate in that way, I think it probably still beats having to update the TransformSystemGroup twice. I'm not completely sure though; maybe with the change filtering, calling multiple TransformSystemGroup updates isn't so bad

    An efficient way to incrementally build PhysicsWorld (update rigidbody poses & BHV in PhysicsWorld, but only for a given list of rigidbody entities) would fix step 4. After moving the shield rigidbody, we call an incremental physicsWorld build only for our shield entities. Our shields could now accurately be raycasted. It would also be great to incrementally call for a collision events update at this point, but without the physics step.

    All in all, the final ideal solution for me would look like this:
    1. Physics update moves our character rigidbody
    2. TransformsUpdate calculates interpolated LocalToWorlds of all child entities of the character
    3. We move our shield to its target entity's LocalToWorld
    4. We either call another TransformSystemGroup update, or manually call for an update of all the LocalToWorlds in the Shield hierarchy with the "Instant Transforms API", for up-to-date rendering of shield renderers.
    5. We call an incremental BuildPhysicsWorld and an incremental ProcessPhysicsEvents

    What are your thoughts on this?
     
    Last edited: Jun 27, 2022
    Krooq, ashwinFEC and thelebaron like this.
  2. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I've been thinking about this a little more and I'm starting to think some of the things I've talked about wouldn't really be required afterall

    Projectile raycasting will typically happen in the FixedStepGroup, which means we only really should update the shield in the CollisionWorld on fixedUpdates. If we move the shield after the physics update, the pose of the shield colliders in the CollisionWorld (the pose it had before being moved) will correspond closely to what's rendered on screen in the first variable update after a fixed update (the interpolated pose that starts at the "last fixedUpdate" pose), and it will gradually become more incorrect as the variable updates approach the next fixedUpdate. The opposite solution (doing an incremental BuildPhysicsWorld after we move the shield) would just give us the same problem but in reverse (inaccurate on early variable updates, and more accurate on late variable updates). We'd be dealing with the same amount of inaccuracy either way, so I don't think incremental BuildPhysicsWorlds would be needed afterall. Not for this case at least

    As for the "Instant Transforms API", I think it would be worth profiling the cost of multiple TransformSystemGroup updates per frame before I arrive at that conclusion. But I still have a feeling that manual transform hierarchy updates for a few dozen transforms would be better than the scheduling & change filtering of all the jobs in the TransformSystemGroup, especially in an open-world type of game with tons of transforms

    I realize this thread is just me thinking out loud so far, but sometimes it helps figuring things out!
     
    Last edited: Jun 27, 2022
  3. WildMaN

    WildMaN

    Joined:
    Jan 24, 2013
    Posts:
    128
    Don't you think that dropping the requirement to follow interpolated LTW would help? It seems you're ok with the inevitable discrepancy between shield's fixedstep collider position and it's interpolated render mesh. So just do interpolation manually as a last step maybe?
     
  4. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    I think it wouldn't change much in the case where we'd be following a child entity. In either case we'll only know the worldspace transform of that child after the TransformGroup updates. If that child entity moves relatively to the root, we can't simply compute its world pose as a fixed local transformation of the root transform. So even if the shield wants to handle its own interpolation, it still needs an up-to-date world transform to move to on fixedupdates

    The delay in CollisionWorld update isn't so bad, but a delay in transforms (renderer) update will visually result in an elastic/laggy object when your followed object is moving.

    Imagine having a regular medieval shield rigidbody attached to the forearm bone of an animated character. When the character moves, there would be a weird visual lag between the arm and the shield
     
    Last edited: Jun 28, 2022
  5. WildMaN

    WildMaN

    Joined:
    Jan 24, 2013
    Posts:
    128
    Sure, what I proposed is to let Unity do it's FixedStep as usual, have both entities consistent positioning in physics world and manually adjust rendering interpolation in render world. As opposed to dealing with interpolation in physics world.

    Personally, I found Unity's interpolation imperfect, so for a competitive medieval slasher I'd think of a custom render interpolator.
     
  6. PhilSA

    PhilSA

    Joined:
    Jul 11, 2013
    Posts:
    1,926
    The difficulty I'm seeing is: how do you know what your shield's "target pose" for interpolation is, during the fixed update?

    Things would happen like this:
    • FixedStepSimulationGroup
      • Physics update (moves the character rigidbody)
      • At this point your shield needs to know what its "target pose" is for interpolation (the pose it'll try to interpolate to over the next fixed timestep). However, since we don't yet have access to the post-movement worldspace pos/rot of child transforms of the character at this point, we can only have an outdated "target pose".
        • If our target pose is the child's LocalToWorld, we'd have an interpolation that tries to catch up to another interpolation, rather than two interpolations that are in sync
    • SimulationGroup
      • TransformSystemGroup / Handle your custom interp

    If you can't have access to the fully up-to-date world pose of your character's arm bone (a child entity of the character) before the TransformSystemGroup updates, then you will necessarily have a shield that appears to lag behind the character's arm. Moreover; animation updates will most likely happen at a variable rate instead of at a fixed rate, which means that an interpolated rigidbody trying to follow an animated bone will most likely have some kind of offset compared to the true current bone pose
     
    Last edited: Jun 28, 2022