- Home /
OnCollisionEnter fires, but OnCollisionExit does not
I have code that looks like this:
void OnCollisionEnter2D(Collision2D collider)
{
//"Blast" the object backwards on collision
rigidbody2d.velocity = new Vector3(-3.5f, 4f, 0f);
collider2d.isTrigger = true;
}
void OnTriggerExit2D(Collider2D collider)
{
Debug.Log("OnTriggerExit2D Here!");
}
void OnCollisionExit2D(Collision2D collider)
{
Debug.Log("OnCollisionExit2D Here!");
}
So the gist is that on collision, I set the velocity of the object to blast backwards on collision, and turn the collider into a trigger.
Technically when this happens depending how fast the code to switch isTrigger = true; is run, either OnTriggerExit2D will run, or OnCollisionExit2D will run, right? One of which must run, right!?
However what I'm seeing is that neither of them will run and I do not get either of the Debug.Log() calls.
This was working perfectly fine on Unity <4.5.1. At some point (I believe it's 4.5.2 that broke it, but not too sure which version)
Any idea what might have changed and why this is happening?
Having a bit of trouble understanding what you're trying to do here, and I'll admit I've never tried to change a collider into a trigger at runtime before so I don't know what pitfalls there are. But what sort of collider/trigger does the other object involved in the collision have on it? And what if you change: collider2d.isTrigger = true;
to collider.isTrigger = true;
?
Well the collider is a collider2d, so using collider.isTrigger is not going to work.
The other collider that this object collides on is a BoxCollider2D with isTrigger = false
Answer by Baste · Oct 05, 2014 at 03:20 PM
First of all, your code isn't compiling*, but I guess you wrote it from memory instead of copying it directly, so I'll just assume everything's in order.
I checked what was happening, and you're completely correct, neither of the exit messages are fired. OnCollisionExit does fire if you don't turn the collider into a trigger. If you make a coroutine that delays adding the force until the next frame, you get an OnTriggerEnter, then an OnTriggerExit:
void OnCollisionEnter2D(Collision2D collider)
{
Debug.Log("On collision enter 2D");
//"Blast" the object backwards on collision
//rigidbody2D.velocity = new Vector2(-3.5f, 4f);
collider2D.isTrigger = true;
StartCoroutine(Bounce());
}
IEnumerator Bounce()
{
yield return null;
rigidbody2D.velocity = new Vector3(-3.5f, 4f, 0f);
}
void OnTriggerExit2D(Collider2D collider)
{
Debug.Log("OnTriggerExit2D Here!");
}
The really strange thing here is that if you're using 3D components, all four messages are fired without the need for a coroutine to delay anything. You get a Collision enter, then a trigger enter, then a collision exit, then a trigger exit. It seems like that in the 3D world, if an object is turned from a non-trigger to a trigger while colliding with something, OnTriggerEnter is immediately fired, and after that OnCollisionExit fires. Finally the Trigger exit fires when the object moves away from the other collider again. Meaning that where you get four distinct messages in 3D, you get a single one in 2D. This is really strange, and probably a bug.
As I said, everything works if you delay setting the bounce for a frame, the trigger exit is fired, just after an enter. A better suggestion is perhaps just to do the thing you want to do on the exit straight away, or if it needs to wait for a frame or two, just put it in a coroutine. Hope that helps, I'll probably make a bug report about this.
*Both OnCollisionEnter and OnCollisionExit needs to have a Collision2D parameter (not Collider2D), and you have written collider2D and rigidbody2D with a small d. Finally, the velocity of a Rigidbody2D needs to be a Vector2, not a Vector3. That last part doesn't prevent anything from working, but you don't need that extra 0.
Yeah, sorry it was really only supposed to be pseudocode, but I ended up writing it in C#, but yeah my code is written correctly, the stuff i wrote here was just for example purposes. I've edited my code now to use Collision2D ins$$anonymous$$d of Collider2D
That's right, if I use Invoke with a delay, ins$$anonymous$$d of setting the velocity straight away it works Ok. However it's not perfect as in my tests (with a 0.05f delay) this only works about 90% of the time, and the percentage is much lower on slower devices (older Android phones) (Any more delay and it will make things look bad.
I'll try it using a Coroutine soon, it may work better than using a timed Invoke?
I've been unsure if I'm doing something wrong or if this could be Unity bug, it all points to this being an actual bug. I've thought about making a bug report, but haven't gotten around to it, so I'm glad you're doing that!
Thanks
I've made the bug report now, yeah.
I would use a coroutine ins$$anonymous$$d. Timed invokes are a bit tricky - you're never sure when the thing fires. If you use a coroutine and add yield return null, that means that the next line of the coroutine fires on the next frame - which means that you're guaranteed that all settings such as changing the trigger status has been taken into effect.
Is there a bug tracker number or issue number on issuetracker? I'd like to follow along the progress.
As far as I understand, you make bug reports through Unity, and the ones that gets accepted (are reproducible, basically) gets put up on the tracker.