OnTriggerEnter called one frame after collision occurs

I’m confused by some collision behavior with some objects of mine. I have one stationary object, a sphere, which has an OnTriggerEnter script on it. I also have one moving object, an icosahedron, which in this example is moving up towards the sphere. The sphere has a trigger collider, and the icosahedron has a non-trigger collider.

What I’m finding is that OnTriggerEnter isn’t called on the first frame that the objects overlap. Instead, it’s called one frame later.

Here are the two objects, paused on the first frame where they visually appear to overlap:

And here are just the colliders, showing them clearly overlapping:

However, OnTriggerEnter isn’t called that frame. Instead, it’s called on the following frame, where the objects aren’t actually overlapping anymore:

I have the moving object’s collision detection set to Continuous Dynamic for the most accuracy. I realize it’s possible for a very fast moving object to bypass the collision detection if it’s moving fast enough, but what puzzles me here is that OnTriggerEnter isn’t called when the two objects initially overlap. It’s only on the next frame that the collision occurs.

From the docs on OnTriggerEnter, it says, “The GameObject with Collider.isTrigger set to true has OnTriggerEnter called when the other GameObject touches or passes through it.”

I also tried calling Physics.OverlapSphere in FixedUpdate, expecting more accurate results, but it’s the same thing. OverlapSphere doesn’t find any results on the first frame where the colliders overlap. Instead, it only finds results on the subsequent frame.

In this case, the icosahedron is clearly touching the sphere, so I’m not sure why OnTriggerEnter isn’t being called. Is there some Unity physics/collision convention I’m missing?

1 Like

Looking a bit more into this, I wondered how the behavior compared to OnCollisionEnter. I started a new project to make sure this wasn’t something weird in my main project. What I found is that OnCollisionEnter, if anything, is called a bit earlier than I might have expected, but that it’s definitely called some number of frames before OnTriggerEnter.

I set up a simple scene with two spheres, one trigger and one not, at the bottom. Two other spheres with rigidbodies fall into to them. The left sphere is a non-trigger collider, while the right sphere is a trigger collider. The left sphere has an OnCollisionEnter script that changes its color to red on collision, and similarly the right sphere has an OnTriggerEnter script that changes it to red on collision. The dynamic spheres are dropped from the same height. I then stepped through the frames to observe the timing of the events:

4790396--457520--Collision.gif

On the left, the OnCollisionEnter fires a frame before OnTriggerEnter. It fires exactly at the point that I would have expected the OnTriggerEnter to fire. But you can see on the right that the two sphere overlap for one frame before OnTriggerEnter is called and the right sphere changes to red.

I’m not sure if this has always been the behavior, but I believe I’m left unable to test for the presence of colliders accurately. Again, maybe I’m missing something obvious, maybe this is just the way physics works in Unity, or maybe it’s a bug.

4790396–457526–Physics-7171640-scene.unitypackage (3.61 KB)

3 Likes

I’d file a bug report with the repro, so at least the issue could be looked at.

2 Likes

I’ll do that. I went back to 2017.4 LTS, to see if it was happening there, and it seems to be the same behavior. I don’t know if that implies it’s “always” been this way, or if 2017.4 LTS has picked up physics changes that were considered bugs, causing its behavior to be aligned with 2019.1.

Also, a correction to my previous post: It seems that using Physics.OverlapSphere does behave as expected, and that’s how I’ve worked around this in my game. Where previous I was using OnTriggerEnter to detect overlap, I’m not using Physics.OverlapSphere, and it’s giving me the expected results. So that’s good, at least. Here’s an example of the difference between OnTriggerEnter and Physics.OverlapSphere. On the left I’m using OnTriggerEnter to detect the falling sphere, and on the right I’m using Physics.OverlapSphere:

4792433--457916--TriggerVsOverlap.gif

1 Like

Submitted this as bug #1172583. Interesting it really doesn’t seem to be an issue with how far into the trigger the falling sphere gets. If I turn gravity down to -0.1 instead of -9.81, the sphere falls very slowly, but it still only called OnTriggerEnter the second frame that the colliders overlap. So it seems like OnTriggerEnter’s logic only fires when two colliders overlap for two consecutive frames. I tried an experiment where I let the falling sphere intersect the lower sphere. Then I dragged the upper sphere up so they weren’t overlapping anymore. OnTriggerEnter was not called in that case. So, it’s not that OnTriggerEnter is called a frame after the collision occurs; It’s that OnTriggerEnter seems to need two consecutive frames of overlap before considering the collider triggered.

2 Likes

I reported this, and was told it’s just the way PhysX works, and has worked for 6+ years. I was pointed to this old issue tracking bug:

In any case, this appears to be the behavior of PhysX, and not something Unity can/will fix. My workaround, again, was to use Physics.OverlapSphere instead of OnTriggerEnter when I needed more accurate results.

2 Likes

I can’t speak for 3D but note that Box2D has similar behaviour.

This comes from when new contacts are generated. In the case of Box2D when you step the simulation, the first thing it does is create new contacts and updates/destroys existing ones as can be seen here. It then then solves both the contact and joint constraints which integrates positions/velocities (forces, gravity etc) and adjusts positions based upon the solver impulse solution. The step then finishes and Unity reports the current contacts and writes-back the body poses to the Unity Transform system.

The above means that the contacts prior to solving are what you get for 2D or in other words, the contacts that were solved. This applies to discrete collision detection. For continuous there are several steps that can adjust the existing contact so the contact gets updated and is more relevant to the current position.

This has been discussed on the Box2D forums and here a few times. The end result is a compromise where the simulation step is preceeded by ensuring all contacts are up to date before solving. You could do this after solving to ensure the contacts reflect the current post-solve poses but that can lead to other problems because you’re not necessarily solving the correct state. You could also (additionally) update all contacts at the end of the simulation step but that’ll potentially have a large performance impact.

3 Likes

That’s an interesting write up. I probably don’t strongly understand it, but I think I get then point. To oversimplify, the physics step needs to move stuff around, and it needs to determine the state of the world (in order to fire events like OnTriggerEnter). At the beginning of the step, it’s kind of like you’re checking where everything ended up after all the movement happened by the end of the previous step. Maybe. :slight_smile:

Thanks for taking the time to explain the behavior.

Yes, in simple terms it’s a case of calculating the current set of contacts based upon where everything is (these will be reported when the simulation step finishes) then solve those contacts (remove overlaps etc) and finally update the body positions. These positions are not necessarily where the contacts were made but the callbacks report the contacts nevertheless. These new positions may have different contacts but those will be done when the simulation runs again.

Well I tried to keep it short and reduce the waffle. :slight_smile:

2 Likes

I know this is an old thread but just wanted to mention that Physics.OverlapSphere also seems delayed by a frame for me, when it comes to collision detection. The actual solution for me turned out to use Collider+Rigidbody set as Kinematic (is true).

In my use case it’s not just detection of collision/trigger that is a problem but mainly objects going through each other at high velocity. Let’s say I have a sphere and a cube. The sphere is moving at very high speed to hit the stationary cube. Here are combinations and results of what I saw:

Sphere

  • sphere has a rigidbody and is not kinematic (is moved by force)
  • sphere is moving very fast and hits the cube

Cube

  • cube has box collider and no rigidbody → result is that sphere is blocked by cube (GOOD)
  • cube has box collider and rigidbody set to kinematic (= true) → result is that sphere is blocked by cube (GOOD)
  • cube has box collider and rigidbody not set to kinematic (=false) → result is that sphere moves through the cube (BAD)
  • cube has box collider and rigidbody set to kinematic (=true) and is set as a trigger → result is that trigger is not fired when sphere goes through the cube (BAD)

Is this expected behaviour? Anyone else seen this?

1 Like