Stacking Coroutines

so i setup a way to take damage when under water but if i leave and enter and leave and enter ect. the damage stacks and instead of taking one tick of damage itll go to 2 then 3 the more i exit and enter the tag

public class LungCapacity : MonoBehaviour
{
    [SerializeField] float lungCapacity;
    [SerializeField] float timeBetweenTicks = 3f;
    [SerializeField] float underwaterHealthChange;
    [SerializeField] int numberOfTicks = 100;
    [SerializeField] GameObject player;

    float lungHealthPerTick;
    float ticksRemaining;
    bool isUnderWater = false;

    Health health;

    private void Awake()
    {
        health = GetComponent<Health>();
    }

    private void Update()
    {
        if (isUnderWater == true)
        {
            Debug.Log("You Are UnderWater");
            Debug.Log(lungHealthPerTick);
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("UnderWater") == true)
        {
            isUnderWater = true;
            if(isUnderWater == true)
            {
                StartCoroutine(HealthChangeOverTime(health));
            }
        }
    }

    private void OnTriggerExit(Collider other)
    {
        if (other.CompareTag("UnderWater") == true)
        isUnderWater = false;
        if (isUnderWater == false)
        {
            StopCoroutine(HealthChangeOverTime(health));
        }
    }

    public IEnumerator HealthChangeOverTime(Health health)
    {
        lungHealthPerTick = underwaterHealthChange / numberOfTicks;
        ticksRemaining = numberOfTicks;
        
       // while (ticksRemaining > 0 && !health.IsDead())
        while (isUnderWater == true)
        {
            if (isUnderWater)
            {
                health.TakeDamage(player, lungHealthPerTick);
            }
            else
            {
                health.Heal(lungHealthPerTick);

            }
            yield return new WaitForSeconds(timeBetweenTicks);
            ticksRemaining--;
        }
        yield break;
    }
}

any ideas how i can handle this?

strange interaction they dont stack if i start and stop the routine using the string name for them but thats not very cost effective is it ?

I don’t think you are stopping the coroutine.

I believe this is getting a new reference so you are not stopping the old one.

Rather have the coroutine cached and use that:

    private Coroutine underWaterRoutine;

    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("UnderWater"))
        {
            isUnderWater = true;
            underWaterRoutine = StartCoroutine(HealthChangeOverTime(health));
        }
    }

    private void OnTriggerExit(Collider other)
    {
        if (other.CompareTag("UnderWater"))
        {
            isUnderWater = false;
            StopCoroutine(underWaterRoutine);
        }
    }

I’d keep it simple. Have a single Update() and put your logic in there and wouldn’t worry about coroutines.

Along the lines of:

Update()
{
  If (IsUnderwater() == true)
    {
      ProcessUnderwaterDamage();
    }
}

ProcessUnderwaterDamage()
{
  if (lastTick > Time.time + timeBetweenTicks)
  {
    ProcessDamage();
    lastTick = Time.time;
  }
}

IsUnderwater()
{
  // return true if underwater, false if not.
}

It’ll probably need a couple more checks and things, but I’d keep it basic rather than doing all the fandangled stuff. Assuming it’s only going to be on less than a lot of gameObjects.

Effectiveness is relative. What are you comparing it to?

As you are only using the one Coroutine in this method, you could simply call StopAllCoroutines

if(isUnderWater==false)
{
     StopAllCoroutines();
}

Of course, you use other coroutines in a class, this would stop all of them which may not be desireable.

I would also simplify these trigger calls a bit…

    private void OnTriggerEnter(Collider other)
    {
        if (other.CompareTag("UnderWater") == true)
        {
                isUnderWater = true;
                StopAllCoroutines();
        }
    }

    private void OnTriggerExit(Collider other)
    {
        if (other.CompareTag("UnderWater") == true)
        {
            isUnderWater = false;
            StopCoroutine(HealthChangeOverTime(health));
        }
    }

Privacy & Terms