Search Issue Tracker

By Design

Votes

1

Found in

2018.4

2019.4

2020.1

2020.1.14f1

2020.2

2021.1

Issue ID

1298358

Regression

No

Rigidbody2D.IsTouching returns true for one frame when GameObject is no longer touching anything

Physics2D

-

How to reproduce:
1. Open the attached project's Scene labeled "Main"
2. In the Hierarchy, select the "Rigidbody" GameObject
3. In the Inspector, on the "RB Test" Component, under the "Toggle bounds history" header, enable "Draw is Touching"
4. Enter the Play Mode
5. Press the Space key
6. Observe the "Rigidbody" bounds drawn in yellow

Expected result: Rigidbody2D.IsTouching returns false when GameObject is no longer touching anything
Actual result: Rigidbody2D.IsTouching returns true for one frame when GameObject is no longer touching anything

Reproducible with: 2018.4.30f1, 2019.4.16f1, 2020.1.17f1, 2020.2.0f1, 2021.1.0a10

  1. Resolution Note:

    Unity 2D Physics uses Box2D. Box2D calculates contacts (finds new ones and updates/deletes old ones) for the solver to use. The solver performs position/velocity integration and reaction to contacts. The final position therefore isn't at the contact point(s), those are used to direct the solver. This contacts->solver->write-back means contacts are where you started, not where you ended-up. To deal with that, you'd need to recalculate contacts again after the solver runs, something which by default, Box2D doesn't do. Note there is an exception here for Continuous collision mode when contacts are updated due to prematurely contacting something during the step but that isn't applicable here.

    "IsTouching" is the same as "GetContacts" in that it simply looks at active contacts for that body/collider(s). These are Box2D contacts and are only calculated once per simulation step before anything else happens.

    In the example scene, you are in contact with the ground. You apply a force then after fixed-update the simulation runs. Box2D calculates/updates contacts at that ground position then integrates the force into velocity and subsequently position so you get the upwards movement. The solver then does it work and the body is at the new upwards position. You then render the position at the ground because that's where it was when you asked prior to the simulation step. Note that this point, the original bounds are different than what the actual body/transform are because it's moved upwards so the bounds are prior to moving.

    Next fixed-update you calculate the bounds again which are above the platform and query "IsTouching" but as stated in the last paragraph, the body has moved but contacts won't be calculated until the simulation runs which is after the FixedUpdate has run so it says it is touching. Getting Contacts will still return the same results; it's not a descrepancy between IsTouching/GetContacts etc.

    Queries are immediately in that they perform actions without caching results there and then so based upon the current body position etc. Istouching/GetContacts don't calculate contacts, those are only produced once during the simulation.

    In short, these are the steps taking Box2D into account:

    Unity FixedUpdate Callback
    Box2D Simulation runs:
    > New/Update/Delete Contacts
    > Integration Motions
    > Solve
    > Write-Back Rigidbody2D poses to Transforms

    The Box2D world simulation step can be seen here: https://github.com/erincatto/box2d/blob/master/src/dynamics/b2_world.cpp#L935

    In short, Unity isn't delaying anything. It's all about when contacts are updated.

Add comment

Log in to post comment

All about bugs

View bugs we have successfully reproduced, and vote for the bugs you want to see fixed most urgently.