Why is OnCollisionExit2D late?

I am having a problem with the “OnCollisionExit2D” callback. It seems to be occurring one physics update too late.

According to the documentation, it simply calls when a collider is no longer touching another. According to the documentation on execution order, it occurs after the physics update.

So if I move a Rigidbody2D in FixedUpdate, it would seem that this is the sequence of events that should happen:

  1. Set Rigidbody2D velocity in
    FixedUpdate
  2. Internal physics
    update (rigidbody moves)
  3. OnCollisionExit2D is called

However, the ACTUAL sequence of events is something like this:

  1. Set Rigidbody2D velocity in
    FixedUpdate
  2. Internal physics
    update (rigidbody moves)
  3. OnCollisionStay2D is
    called
  4. FixedUpdate
  5. OnCollisionExit2D is called

Here is some test code I wrote to confirm this problem:

    int timesteps = 0;

    Rigidbody2D _rigidbody;

    // Use this for initialization
    void Start()
    {
        _rigidbody = gameObject.GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        timesteps++;

        Vector3 velocity = new Vector3();

        if (Input.GetKeyDown(KeyCode.Space))
        {
            Debug.LogFormat("Moved {0}", timesteps);
            velocity.y += 10000;
        }

        velocity.y -= 1;

        _rigidbody.velocity = velocity;
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        Debug.LogFormat("Enter {0}", timesteps);
    }

    private void OnCollisionStay2D(Collision2D collision)
    {
        Debug.LogFormat("Stay {0} {1}", timesteps, transform.position);
    }

    private void OnCollisionExit2D(Collision2D collision)
    {
        Debug.LogFormat("Exit {0}", timesteps);
    }

Here is the logging for this code:
89074-unity.png

Why is this happening? Shouldn’t the physics engine be aware that it stopped colliding? It seems like it is calling the OnCollisionExit2D on the physics update AFTER it stops colliding, rather than the same one. Is this always the case? Is there some way I can change this behavior? My game requires that the characters always know if they are on the ground after the physics update. Can/ should I use raycasting instead?

I have created a ticket for that issue : FogBugz

Also I found those possible workarounds :

  • method OverlapCollider gives a correct result
  • method Distance gives a correct result
  • method Raycast gives you RaycastHit2D which contains the same info than Collision2D

Also note that when teleporting the rigidbody using rigidbody.position, the issue does not happen. While issue is produced when moving the rigidbody using rigidbody.AddForce, rigidbody.velocity or rigidbody.MovePosition.