1

So I have an OnTriggerEnter function that removes health every second usig a Coroutine. I want to stop it with a OnTriggerExit function and StopCoroutine. While this works with only one gameObject, it breaks with multiple colliders since it always cancels "routine". What would be the best way to stop just the Coroutine for the object that left the Collider? I feel like there is an easy solution for it but I just don't see it.

OnTriggerEnter/Exit

    private float delay= 1f;
    void OnTriggerEnter(Collider other)
    {
        DealDamage(other, delay);
    }
    private void OnTriggerExit(Collider other)
    {
        StopDamage();
    }

Coroutine

private Coroutine routine;
private void DealDamage(Collider col, float damageRate)
{
    var health = col.GetComponent<IChangeHealth>();
    if (health == null) return;
    routine = StartCoroutine((DamageEverySecond(health, damageRate)));
}
IEnumerator DamageEverySecond(IChangeHealth health, float rate)
{
    while (true)
    {
       health.RemoveHealth(Damage);       
               yield return new WaitForSeconds(rate); 
    }
}
protected void StopDamage()
{
    StopCoroutine(routine);
}
Filip cz
  • 19
  • 2
  • You could store the Coroutine in the 'Other' objects that enter/exit trigger. Or you could hold the Coroutines in a dictionary, lookup by Other gameobject. – Kale_Surfer_Dude Sep 14 '20 at 00:09

1 Answers1

2

As metnioned use a Dictionary<Collider, Coroutine> to store and retrieve your routines for the given collider object:

private float _delay= 1f;
private readonly Dictionary<Collider, Coroutine> _routines = new Dictionary<Collider, Coroutine>();

private void OnTriggerEnter(Collider other)
{
    DealDamage(other, _delay);
}

private void OnTriggerExit(Collider other)
{
    // pass on the collider reference
    StopDamage(other);
}

private void DealDamage(Collider col, float damageRate)
{
    // In general don't use null check for types derived from UnityUngine.Object but rather the bool operator
    if (!col.TryGetComponent(out IChangeHealth health)) return;

    // Check if a routine already exists for this collider
    if(_routines.TryGetValue(col, out var routine) && routine != null)
    {
        // Avoid concurrent routines for the same object
        StopCoroutine(routine);
    }

    // start a routine for this collider and store the reference
    routines[col] = StartCoroutine((DamageEverySecond(health, damageRate)));
}

protected void StopDamage(Collider other)
{
    // Check if a routine already exists for this collider
    if(_routines.TryGetValue(other, out var routine) && routine != null)
    {
        // if yes stop it
        StopCoroutine(routine);
    }
}

private IEnumerator DamageEverySecond(IChangeHealth health, float rate)
{
    while (true)
    {
       health.RemoveHealth(Damage); 
  
       yield return new WaitForSeconds(rate); 
    }
}

See also

derHugo
  • 83,094
  • 9
  • 75
  • 115