Knockback feature: OnCollisionEnter + OnCollisionStay

Hello!
I try to implement damage and knockback event with cooldown. It happens when character touches enemy or stands beside it.

    bool invulnerable = false;
    public float horizontalKnockbackForce;
    public float horizontalKnockbackForce;
    float knockbackDuration = 2f;

    public IEnumerator KnockbackAndHurt(Transform obj)
        {
            invulnerable = true;
            Hurt(hp);    // function for health point substraction
    
            float timer = 0;
    
            while (knockbackDuration > timer)
            {
                timer += Time.deltaTime;
                rb.AddForce(Vector2.up * verticalKnockbackForce);
                if (transform.position.x < obj.position.x)
                {
                    rb.AddForce(Vector2.right * horizontalKnockbackForce);
                }
                else
                {
                    rb.AddForce(Vector2.left * horizontalKnockbackForce);
                }
            }
            yield return 0;
            invulnerable = false;
        }

It works fine with OnCollisionEnter2D:

private void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.collider.gameObject.CompareTag("Enemy"))
        {    
            if (invulnerable == false)
            {
                StartCoroutine(KnockbackAndHurt(collision.collider.transform));
            }
        }
    }

But OnCollisionStay2D is invoking coroutine too fast or several times simultaneously, so character is getting much damage and flying away too far:

private void OnCollisionStay2D(Collision2D collision)
    {
        if (collision.collider.gameObject.CompareTag("Enemy"))
        {
            if (invulnerable == false)
            {
                StartCoroutine(KnockbackAndHurt(collision.collider.transform));
            }
        }
    }

How can I make it work and combine it with OnCollisionEnter2D?

Hi!

In your KnockbackAndHurt coroutine, you never do a yield return during your while loop, so instead of the coroutine lasting 2 seconds (incrementing your timer and adding force, waiting a frame, then repeating), it will last for one frame (incrementing timer and adding force and incrementing timer and adding force and inc… then waiting).

Include the fact that OnCollisionStay will trigger every frame the colliders are touching, whereas OnCollisionEnter will only trigger once (when they collide),
and this is why so many coroutines get run when you use OnCollisionStay.

The fix should be as simple as moving the yield return in your KnockbackAndHurt coroutine one line above into the while loop. You may also need to recalibrate your verticalKnockbackForce variable or the way you add knockback in general to compensate for any differences.

Hope this helps! :smiley: