Using OnCollisionStay as a coroutine.

The docs say that you can use OnCollisionStay as a coroutine if you use yield, but do not describe the semantics of it.

This seems like it would be useful to answer questions like “Have these two objects been colliding for X amount of time?” Without a coroutine you need instance variables to track this between calls (yuk).

  1. Is there a separate co-routine running for each object colliding with the monobehaviour? This seems like the most logical way.

  2. While you are yielding in OnCollisionStay, could it have been called again and started a new Coroutine?

  3. When you return from your yield call, is there any guarantee that the objects are still colliding?

It might just be that the answers are #1 yes, #2 yes, #3 no. Which doesn’t make it very useful for answering the question of “Have these two objects been colliding for X amount of time?”

I just tested this, and unless I made some big mistake, OnCollisionStay as a coroutine is just about useless. OnCollisionStay will be invoked repeatedly, even if the previous invocation isn’t finished. Furthermore, it will continue to progress even if the objects are no longer colliding.

Is there a separate co-routine running for each object colliding with the monobehaviour? yes

While you are yielding in OnCollisionStay, could it have been called again and started a new Coroutine? yes

When you return from your yield call, is there any guarantee that the objects are still colliding? no (The co-routine is always finished eventually, still colliding or not.)

(like you said)

I did some tests by printing out the hash of the collision and printing out “FixedUpdate”. It would seem that you don’t necessarily even have a guarantee that the co-routine will be finished the next update. After a while, though, it seemed to settle into having the form

...
B Pt1
A Pt2
Fixed Update
C Pt1
B Pt2
Fixed Update
D Pt1
C Pt2
...

But, like I said, it started out without that guarantee where some collisions took 3 fixed updates before their second half was executed.

EDIT: Works decently despite unpredictable starts

I went ahead and tried using it to log the floor for a “isGrounded” style check. I set the ground data in the start of the co-routine, then clear it in the second. Seems to work pretty well so far. Example:

private IEnumerator OnCollisionStay2D( Collision2D collision )
{
    var hash = collision.GetHashCode();
    foreach ( var contact in collision.contacts )
    {
        if ( contact.normal.y > FloorNormalMinY )
        {
            floorCollisions[hash] = new CollisionDetails { normal = contact.normal, velocity = contact.rigidbody?.velocity ?? Vector2.zero };
        }
    }

    yield return null;
    floorCollisions.Remove( hash );
}