Search Issue Tracker

By Design

Votes

0

Found in

2017.3.0f3

Issue ID

997783

Regression

No

OnCollisionEnter is being reported a couple frames too late for CharacterControllers penetrating other bodies

Physics

-

When Rigidbody bumps into a CharacterController, callbacks to OnCollisionEnter arrive a couple frames too late.

To reproduce:

1. Download attached project "Unity2017Repros.zip" and open in Unity
2. Open "LateCCOnCollisionEnter" scene
3. Enter Play mode
4. After few seconds exit Play mode and inspect Console:

- Starting at t = 0.6, the CharacterController (CC) is told to Move(0.1, 0, 0) each frame, making a bee-line for the cube.
- At t = 0.78, the CC bumps into the cube and only moves 0.03 in x rather than the full 0.1 as requested by the Move(). The capsule is now “stuck” at x = 1.12. This is all as expected, as 1.12 + 0.08 + 0.5 + 1.0 = 2.7. (1.12 is the CC’s center, 0.08 is the CC’s Skin Width, 0.5 is the CC’s radius, 1.0 is the distance from the cube’s center to the face the CC hit, and 2.7 is the cube’s x position.)
- Move() continues to be called on the CC every frame, but it doesn’t get anywhere for now.
- At t = 1.2, the cube starts to move slowly towards the CC, by (-0.03, 0, 0) each frame. For a few frames, this simply reduces the separation between the cube and the CC, “eating into” the CC’s Skin Width “buffer”.
- At t = 1.24, the cube moves to x = 2.61, and so now the cube and the CC are truly overlapping. We receive no OnCollisionEnter during that physics step, however. (The cube is listening for this callback.)
- At t = 1.26, the capsule (which always tries to move first), now that it is penetrating the cube, freely moves from 1.12 to 1.22. No hits reported by OnControllerColliderHit, so indeed the CC sees itself as penetrating the cube. The cube then moves an additional -0.03 in x. During the next physics step, we still do not see any calls to OnColliderEnter however.
- At t = 1.28, the capsule continues to move unimpeded from 1.22 to 1.32. The cube moves another -0.03 in x. Then, finally, during the next physics step the cube receives the OnColliderEnter callback that it has collided with the CC. The cube, during the callback, tries to “undo” its movement from the last frame by moving +0.03 in x. (And from this point on it no longer tries to move -0.03 in x each frame.) During the callback, we also see that the cube has already moved in the +x direction, presumably as a collision response to its overlap with the CC. It’s not enough to separate them though, and from then on the CC continues to take steps forward, and the cube continues to get “pushed along” by the CC (even though CC.Move() isn’t generally supposed to be able to push things).

Notes:
- It really seems there’s a delay of 2 frames in reporting OnCollisionEnter and not a specific distance threshold. If, for example, the cube only moves -0.003 in x each frame, it takes a lot longer for the cube to traverse the CC’s Skin Width, but once it does, the callback still occurs exactly 2 frames late.
- If you add a Capsule Collider (with the same dimensions as the CC) to the capsule gameObject, collisions are then reported correctly.

Reproduced on Unity 2017.1.3p1, 2017.2.1p3, 2017.3.0p4, 2018.1.0b5 and 2018.2.0a1

  1. Resolution Note:

    Internally, for each CharacterController PhysX creates a kinematic body and a shape (i.e. a collider). The created shape is not of the same size as the character controller, it's currently internally multiplied to be 0.8 of the original dimensions as set in the Inspector. In this particular project this means a difference between the 0.5 radius for the CapsuleCollider shape and the 0.4 internal radius for the CharacterController shape.

    On top of that, the CapsuleCollider attached together with the CharacterController would be treated as a static collider, unexpectedly. So the root transform will be connected to a kinematic body and a static one at the same time. It's generally advised to avoid having multiple systems controlling a single transform, but since it's static and never gets read back it turns out to be alright in the end.

    Now, when you call CharacterController.Move(), PhysX would use queries to check the end pose and then set this pose as a kinematic target of that kinematic body. However, as you probably know, the kinematic target doesn't actually get processed until the simulation happens (see MovePosition). This is where you see one frame lost. Another frame is then getting lost because there is a general one frame delay from the moment a contact was discovered to the the moment an appropriate callback was called. This behaviour is by design in PhysX.

    While it's not directly possible to reduce those two frames of delay, I wanted to add an idea that there might be a way for you to not rely on the collision reporting system here but discover the required contacts manually with the help of queries. One would do it in two steps: discover nearby colliders by using an overlap and then compute contacts with each object by a capsule sweep. These days you can thread that via batched queries (see CapsuleCast.ScheduleBatch) so the price shouldn't be all that large.

Comments (2)

  1. eddyb

    Oct 25, 2018 21:48

    Thanks for looking into this problem. I don't believe this is "by design" though. I understand what you are saying about the skin width vs the ACTUAL physics representation, but I'm afraid the problem I've described and demonstrated in that project is not explained by that difference. For instance, as I mentioned in the notes at the bottom of the report, the callback is always fired 2 frames late, regardless of the cube's movement speed – so the issue isn't one of geometry (ie. size of collider), but something else. Please reopen this case, as there is a real inconsistency/bug here that has yet to be explained. I'd be happy to answer any other questions you may have or dive into the details with you to try to figure this out.

  2. Tortuap

    Jul 28, 2018 19:18

    Holy fuck !

    Don't use OnCollisionEnter / Exit, those are just broken.
    Test the collision yourself using Physics class related functions like Overlap and Raycast.

    What a shame Unity guys ! Shame on you !

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.