Search Unity

Custom Interpolation for rigidbody2d.position modification instead of MovePosition?

Discussion in '2D' started by pastaluego, Sep 1, 2022.

  1. pastaluego

    pastaluego

    Joined:
    Mar 30, 2017
    Posts:
    196
    Since moving a rigidbody2d by directly modifying rigidbody2d.position causes it and its collider to move instantly and resets the interpolated position for that frame until the next simulation step, are there any resources for setting up a custom interpolation that allows for direct modification of rigidbody2d.position without interfering with interpolation?

    Because I need to move rigidbody2d.position multiple times during FixedUpdate in order to raycast against the newly updated collider positions to determine some customized physics behavior involving those raycast results. But this breaks rigidbody interpolation, so I need to come up with an alternative to still achieve visual smoothness.

    https://www.kinematicsoup.com/news/2016/8/9/rrypp5tkubynjwxhxjzd42s3o034o8

    This is the only resource I've found so far, but I was wondering if there were any others.
     
    Last edited: Sep 2, 2022
  2. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    Rigidbody2D movement interpolation is stopped only after it has begun so after the simulation step which sets it up. If you're doing this in the script FixedUpdate then the last interpolation cycle has finished and the new one hasn't begin yet. After the script FixedUpdate has completed, the internal FixedUpdate happens and physics runs the simulation step and starts the next interpolation.

    Making this change in the script Update does stop interpolation yes but that's because that's where interpolation happens and I could totally understand your question.

    I can only presume this is with a Kinematic Rigidbody2D? If so then if you returned the position to where it was then issue a Rigidbody2D.MovePosition then it'd move there in the next simulation step. If you don't do this then yes, setting a new position directly is an instant teleport from where it was with no interpolation support.

    I've considered the ability of turning off this implicit "interpolation reset" which is only there to stop devs from experiencing interpolation across large areas of the scene when they actually wanted to move there instantly. Lots of ways of doing that from adding an option per-Rigidbody2D to adding in a special SetPosition/SetRotation/SetPositionAndRotation with args to ask not to reset to having a global setting that states how far it can be "teleported" before interpolation is reset.

    As it stands, I'm not following why you'd need this.
     
  3. pastaluego

    pastaluego

    Joined:
    Mar 30, 2017
    Posts:
    196
    I do use kinematic bodies and use raycasts to determine collisions. Particular edge cases require me to limit movement to a fixed distance to allow for fixed length raycasts to handle those collision checks. So if something is moving faster than that fixed distance, the movement needs to be broken down into multiple steps shorter than that fixed distance and then run the collision raycasts after each step or it risks tunneling past the rays' origins. But this requires collider and position to update instantly in FixedUpdate so the subsequent steps' raycasts can work properly. It can't wait for the sync in the internal physics step, so MovePosition can't be used.

    I think essentially it would be like how I believe Unreal allows for physics substepping (never used it so I don't know for sure, but the concept I think is the same).
     
  4. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    I don't mean using this prior to each query. I mean using this for the final position move. This means your calculated move is interpolated to.

    That's quite different to what you described. To let you know though, 2D physics is considering simulation sub-stepping by adding a new SimulationMode2D. This would allow you to set a new sub-step (fixed internal) to split the frame-time up into.
     
  5. pastaluego

    pastaluego

    Joined:
    Mar 30, 2017
    Posts:
    196
    Oh I missed your idea of returning the rigidbody2D to the original position and then using MovePosition for the adjusted movement. That actually seems so simple and works, thanks!

    And that would be cool if Unity added that new SimulationMode2D option.
     
  6. pastaluego

    pastaluego

    Joined:
    Mar 30, 2017
    Posts:
    196
    @MelvMay
    Though actually while I have you, I was curious if you had any insight on this even though it's not technically related to 2D Physics.

    I made this thread because I'm currently not using rigidbodies but am looking to switch to it for the various benefits including the built-in interpolation. I'm currently using transform.position for movement and Physics2D.SyncTransforms() after each of my 'steps' in FixedUpdate so the colliders sync with the transform changes so the subsequent collision raycasts can properly detect the newly positioned colliders of each step. (And for visual interpolation I have a custom transform interpolation script that lerps transform positions in the Updates between fixedupdates)

    But one issue that prevents me from switching to using rigidbody2d.position is that I'm currently using a set of child transforms in order to use their positions as the start positions and end positions of
    Physics2D.Linecast(start, end)
    since child transforms automatically move/rotate/scale properly in relation to the parent object that is moved by transform.position += . The problem is if I switch to instead use rigidbody2d.position +=, am I right to believe that that is no longer possible unless I continue to still move the transforms alongside it? As illustrated here:

    upload_2022-9-2_19-14-1.png


    Given the number of positions I need to track for my linecasts' start and end positions, manually calculating each new start and end position for each linecast for each substep the object gets moved in FixedUpdate has in my experience been significantly worse performance-wise compared to being able to simply use the child transforms (as shown above) since they automatically internally update any position/rotation changes from the parent transform they inherit from.

    The closest I came to in terms of matching that performance was by using the transform's transformation matrix myself, but even that was worse than whatever happens internally to child transforms. But since using rigidbody2d.position doesn't also instantly move the transform, the transform's transformation matrix would be unusable anyway to my understanding.

    This is the only idea I have for that. Where after every rigidbody2d.position move I'd run a script like this's UpdateSensors() to update those sensor positions
    Code (CSharp):
    1. public class SensorTest : MonoBehaviour
    2. {
    3.     private Vector3 _center = new Vector3(0, 0);
    4.     public Vector3 Center { get; private set; }
    5.  
    6.     private Vector3 _bottomLeft = new Vector3(-1, -1);
    7.     public Vector3 BottomLeft { get; private set; }
    8.     private Vector3 _bottomRight = new Vector3(1, -1);
    9.     public Vector3 BottomRight { get; private set; }
    10.  
    11.  
    12.     private Vector3 _topLeft = new Vector3(-1, 1);
    13.     public Vector3 TopLeft { get; private set; }
    14.     private Vector3 _topRight = new Vector3(1, 1);
    15.     public Vector3 TopRight { get; private set; }
    16.  
    17.     public void UpdateSensors(Rigidbody2D rb)
    18.     {
    19.         Quaternion rotation = Quaternion.Euler(0, 0, rb.rotation);
    20.         Vector3 position = rb.position;
    21.  
    22.         Matrix4x4 matrix = Matrix4x4.TRS(position, rotation, Vector3.one); //not sure how I'd properly do scale with a rigidbody2d instead of a transform
    23.         Center = matrix.MultiplyPoint3x4(_center);
    24.         BottomLeft = matrix.MultiplyPoint3x4(_bottomLeft);
    25.         BottomRight = matrix.MultiplyPoint3x4(_bottomRight);
    26.         TopLeft = matrix.MultiplyPoint3x4(_topLeft);
    27.         TopRight = matrix.MultiplyPoint3x4(_topRight);
    28.     }
    29. }
    Sorry if that was hard to follow, but do you have any ideas for that?


    ****
    Actually now after doing tests, using rigidbody + updating the raycast sensor positions with the
    Matrix4x4.TRS
    using each new
    rigidbody.position
    and
    Quaternion.Euler(0, 0, rigidbody.rotation)
    now seems to be much faster unless I set up the test wrong.

    I was originally doing the test with 500 instantiations of either the rigidbody.position script or transform.position script, and had them all spawn on the same position while I moved around. And the rigidbody version hung from the profiler showing Physics2D.Simulate>Physics2D.FindNewContacts despite all my collisions in the collision matrix being disabled which I thought was odd. But after staggering them on spawn so they're not on top of each other, the rigidbody version is much faster. (Though I'm still not sure why them overlapping would call Physics2D.FindNewContacts if they're not supposed to be able to detect one another anyway with the each checkbox in the collision matrix being disabled)
     
    Last edited: Sep 3, 2022
  7. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    Cool. I do agree that this should be simpler though! Mainly this was a workaround for you; there's overhead in constantly moving Rigidbody2D around, mostly from the attached Collider2D having their broadphase updated. For only a handful of bodies though, it'd be irrelevant.

    I agree! We're on it.

    I'll grab a coffee and read/reply to your following post....
     
  8. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    Okay, there's a lot to your post so I'll likely end up telling you things you do and don't know but I'll write it as much for others reading as well as yourself. I will add that whilst you say it's not 2D physics, I can only find 2D physics related questions so maybe there's a deeper question I've missed. Sorry if that's the case.

    Be aware (if you're not already) that if you don't add a Rigidbody2D then the Collider2D is attached to the hidden static ground-body so its internal geometry is calculated relative to that. I say "attached" but a Unity Collider is a collection of Box2D fixtures (physics shapes). They can only be created against a body (Box2D b2Body). They are owned by it. In our case, without a Rigidbody2D we create such shapes via this "ground" body so if you "move" it, all that geometry has to be recalculated because its local space is the same as its world-space. So in short, don't ever do this. Ever. In the very least, add a Rigidbody2D and set its body-type to Static or Kinematic. That way, when you modify it, only the Rigidbody2D pose is updated because the Collider2D is attached to it in the body local-space. Also, you can get it to instantly update by directly setting the Rigidbody2D.position/rotation. This won't change the Transform of course, that happens when the simulation runs at the end when it writes body positions to the transform.

    Calling SyncTransform in FixedUpdate is pointless. As I said, the physics simulation calls this once at the start of the simulation step which will happen immediatley after all the script fixed-updates have called. We do this because we have to check if the dev has (incorrectly I might add) changed Transforms so we have to read Transforms into Rigidbody2D or even worse, recreate Static ground-body colliders.

    If you're calling SyncTransform or modifying Transforms you're working against physics. I'm not trying to "slap your wrist here" or be patronising, just stating it as a clear fact that the physics system wants to work the way it's designed to which is updating Rigidbody2D then writing thoses poses out to the Transform and never the other way around.

    So yes, as I stated above. The role of the Rigidbody2D is to write to the Transform during the simulation step as it's the authority on the pose. I'm not sure why this is an issue if you read the position/rotation from the Rigidbody2D always. Don't use the Transforms. This is particularly important when using interpolation because the Transform is always historic i.e. moving from the previous Rigidbody2D pose to the current one. Reading the Rigidbody2D pose is where the Transform is moving to.

    If the scale isn't changing (I hope it isn't because all the colliders are recreated anyway if you do that) then this approach would work. If the scale isn't changing then you can grab that once and reuse it.

    So it seems you're good to go here although (not trying to be critical) I'm still not quite following what seems to be a fairly convoluted way to write a character controller but then again, likely it's required because I've not got all the details.

    Let me start by stating that this is entirely in Box2D. I added a profiler entry to the method b2ContactManager::FindNewContacts. This might prove useful in looking at the code. What Box2D is doing here is looking at everything that is moved (it queues up any changes you make). It then queries the broadphase asking if there are any potential pairs (these are AABB overlaps). It then proceeds to process those potential pairs. It will early out of the related physics shapes (colliders) are not set to collide and move on. The thing is, to even know they shouldn't collider, it has to first know what might be potentially colliding. Finding these potential pairs though is super fast unless of course you've got crazy, crazy numbers of them. Unfortunately you cannot get that count or other information unless you're using the new 2022.2 2D Physics Profiler which includes counts of broadphase updates count (potentials) and pairs (real pair contacts). You will be avoiding the thing that is the most expensive which is the narrowphase shape/shape intersection tests.

    I'm not sure if it's really relevant to what you're doing but I wrote a basic Kinematic move/slide example controller that might have some useful info here (maybe not but the link is in the video):


    Sorry if I missed any important points such as the part where you seem to be (?) tracking/aligning rotation against the surface. I'm sure there's some subtle things I've completely missed. Feel free to highlight or ask them again.
     
  9. pastaluego

    pastaluego

    Joined:
    Mar 30, 2017
    Posts:
    196
    Thanks for the detailed response. And yeah I originally was thinking purely about transforms which in my head wasn't Physics2D related but ended up being tied directly to it. And the reason I felt I was forced to use transform.position and Physics2D.SyncTransforms() was ultimately because both convenience and a lack of understanding that made rigidbodies (wrongly) appear impossible to use.

    Specifically being

    1. Previously being unaware rigidbody.position instantly moved colliders without waiting for internal physics step like MovePosition does

    2. Previously believing
    rigidbody.position +=
    irreparably broke rb interpolation before realizing I could move it back to the original position and then use MovePosition and still get the interpolation.

    3. Depending entirely on the transform.position of child gameobjects for key functions since they update instantly alongside
    transform.position +=
    of the parent, but not with
    rigidbody.position +=
    .

    So an overall lack of knowledge made me believe I was trapped into using transform.position and Physics2D.SyncTransforms(). I can't know for sure if I can still use it this new way until I completely switch my system over to using rigidbody, but I think it should work.

    But to give a bit more reason why I needed the instant movement in FixedUpdate is because for my Sonic-like 2D controller, and this could be a lack of knowledge trapping me again, I didn't see how else I could achieve something like the picture below. With my current knowledge, my only options were to either subdivide the movement + raycast collisions myself in FixedUpdate, or achieve the same thing by setting the fixed timestep to an unrealistically tiny number to subdivide the hyper-fast movement into steps small enough to work. Which I assume isn't a good idea, so I chose the former.

    upload_2022-9-4_12-8-10.png

    This movement speed is probably 30x faster than any object will ever move, but there are other reasons why I felt I needed to do it this way. This one was just the simplest way to explain it. Though it's also nice that it works properly if I ever want to move this fast. But the non-linear nature of the movement made me believe traditional Physics2D and just using the internal physics step wouldn't work, but again that could be coming entirely from a lack of knowledge.

    But now that I have an idea of how it could still work with rigidbody.position, I will try to switch the system over.
     
    MelvMay likes this.
  10. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,491
    Glad some of my post was useful. To add though that we're aware that there's a number of aspects that are adding to dev friction when it comes to writing their own movement controllers or simply having a complex query set-up. There are a number of things scoped for 2023 to help with this which also includes explicit interpolation control. It will get easier.

    Thanks for the image which does clarify some things for me. I'm pleased there's a higher performance way forward for you. Let's use this thread if you encounter any stumbling blocks.

    I'll just add that one thing with the approach above where you're moving the same thing several times might be a problem because of how Box2D queues moves. It queues the fact that a shape(s) have moved via their proxy-id. It doesn't waste time checking if it's already queue but instead deals with that during the simulation step where it sorts then ignores pairs for narrow-phase (intersection testing). There's a potential optimization opportunity here that I've taken note of. I suspect you're seeing the FindNewContacts because of this but I cannot be sure.

    One work item that is scoped is the ability to specify an explicit body position in queries so (say) casting colliders would allow you to specify the body position without having to actually change it. That doesn't help you here because you're doing a bunch of line-cast queries but it's part of the controller solution. We do have "visual (component-based) queries" too which is a set-up where you batch queries (of all kinds) and state when you want the results. The visual side means you can add a query into a list on a component and edit it in the scene-view with options in the inspector. The query arguments for position/direction can be set to be relative to various things, including transform etc. This, in theory, would help you here. No landing date, just scoped for 2023 release cycle.
     
    pastaluego likes this.
  11. Rodi12

    Rodi12

    Joined:
    Sep 5, 2022
    Posts:
    1
    To determine collisions, I do employ kinematic bodies and raycasts. To allow for fixed length raycasts to handle certain collision tests, I have to restrict movement in some edge circumstances to a fixed distance. The movement must be divided into steps that are individually less than the fixed distance if something is travelling faster than that; otherwise, it runs the risk of tunneling past the sources of the collision raycasts. But for the next steps' raycasts to function properly, collider and position must update quickly in FixedUpdate. MovePosition cannot be utilized because it cannot wait for the internal physics step to sync.

    It would effectively work the same way, in my opinion, that Unreal supports physics.
     
  12. pastaluego

    pastaluego

    Joined:
    Mar 30, 2017
    Posts:
    196
    @MelvMay

    I took a break from this but came back to it and realized I'm still having an issue with updating my 'sensor' positions when using rigidbody.position to move my 2D Controller instead of transform.position, and I can't figure out a performant way to handle it.

    It's technically a hyperoptimization that I don't really need to do, I just am so curious how child transform positions and rotations get updated so cheaply.

    upload_2023-3-5_2-46-29.png
    When I was using transform.position += to move my controller, I was using 10 empty child gameobjects (seen above) attached to my controller simply for the fact that their transform positions and rotations update automatically and seemingly for no cost whenever I moved the the controller transform with transform.position += or transform.rotation =. (I don't know how they get updated under the hood).

    I used these empty child transform positions for the start and end positions of my collision sensors (linecasts) since the positions got reliably updated instantly and automatically with any transform movement I needed to do in FixedUpdate (like adjusting the transform position whenever a collision linecast hit something, which would update the positions for subsequent linecast positions that same frame).

    However, if I were to instead use rigidbody2D.position += to move my controller, I lose access to this because the transform doesn't move until the internal physics update, so I can't use child transforms for the 'sensor' positions anymore.

    I thought I could get away with doing this
    Code (CSharp):
    1. public class SensorTest : MonoBehaviour
    2. {
    3.     private Vector3 _bottomLeft = new Vector3(-1, -1);
    4.     public Vector3 BottomLeft { get; private set; }
    5.     private Vector3 _bottomRight = new Vector3(1, -1);
    6.     public Vector3 BottomRight { get; private set; }
    7.     private Vector3 _topLeft = new Vector3(-1, 1);
    8.     public Vector3 TopLeft { get; private set; }
    9.     private Vector3 _topRight = new Vector3(1, 1);
    10.     public Vector3 TopRight { get; private set; }
    11.     //etc
    12.     public void UpdateSensors(Rigidbody2D rb)
    13.     {
    14.         Quaternion rotation = Quaternion.Euler(0, 0, rb.rotation);
    15.         Vector3 position = rb.position;
    16.         Matrix4x4 matrix = Matrix4x4.TRS(position, rotation, Vector3.one);
    17.  
    18.         BottomLeft = matrix.MultiplyPoint3x4(_bottomLeft);
    19.         BottomRight = matrix.MultiplyPoint3x4(_bottomRight);
    20.         TopLeft = matrix.MultiplyPoint3x4(_topLeft);
    21.         TopRight = matrix.MultiplyPoint3x4(_topRight);
    22.         //etc
    23.     }
    24. }
    But I realized I would need to call UpdateSensors() every time I move rigidbody2D.position += in order to perfectly mirror the way transform.position += updates its child transform positions/rotations under the hood.

    I tested it and it seems to be much worse performance-wise than the empty transforms.

    So my question is, is it possible to replicate the fast way child transform positions and rotations seem to get updated when their parent moves except with hardcoded Vector3s instead that get transformed by the rigidbody position and rotation?
     
    Last edited: Mar 6, 2023