0
\$\begingroup\$

The code below has the player flashing when touching a hazard but continues flashing indefinitely. If I stand on the spike for a few seconds, the flashing speeds up faster than I want.

public class Player : MonoBehaviour {

    public int maxHealth = 10;
    public int currentHealth;
    public HealthBar healthBar;

    private float currentDamageInterval;
   [SerializeField] private float damageInterval = 2f; //in seconds

   [SerializeField] private Transform groundCheck;
   [SerializeField] private LayerMask hazardLayer;

    private SpriteRenderer sr;
    private Color[] colors = { Color.red, Color.white };

    void Start() {
        sr = gameObject.GetComponent<SpriteRenderer>();
        currentHealth = maxHealth;
        healthBar.SetMaxHealth(maxHealth);
    }

    void Update() {
        //subtract 1 every real life second
        if (currentDamageInterval > 0) {
            //sr.color = Color.white;
            currentDamageInterval -= Time.deltaTime;
        }
        if(currentDamageInterval <= 0 ) {
            if (Physics2D.OverlapCircle(groundCheck.position, 0.5f, hazardLayer)) {
                TakeDamage(1);
            }
        }
    }

    public bool CanTakeDamage() {
        return !(currentDamageInterval > 0f);
    }

    public void TakeDamage(int damage) {
        //if the current number in the currentDamageInterval is <= 0 or in other words if
        //2 seconds delay has passed, subtract 1 from the healthbar
        if (currentDamageInterval <= 0) {
            currentHealth -= damage;
            healthBar.SetHealth(currentHealth);
            StartCoroutine(DamageFlashing(1f, .3f));
            //set currentDamageInterval to start a new cooldown/delay period (2 seconds)
            currentDamageInterval = damageInterval;
        }
        sr.color = Color.white;
    }

    IEnumerator DamageFlashing(float time, float intervalTime) {
        float elapsedTime = 0f;
        int index = 0;

        while (elapsedTime < time) {
            sr.color = colors[index % 2];

            elapsedTime += Time.deltaTime;
            index++;
            yield return new WaitForSeconds(intervalTime);
        }
    }
}

How can I have the flashing speed/intervals stay stable and stop flashing after 1 second (parameter I set in the DamageFlashing Coroutine)?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

You're starting multiple coroutines that are overlapping each other. Every 2 seconds you start a new copy of the coroutine while the other is still running, resulting in the increasing frequency of flashing.

"Shouldn't the old coroutine have finished by the time the new one starts?"

Not when you're advancing the timer like this:

elapsedTime += Time.deltaTime;
yield return new WaitForSeconds(intervalTime);

Here every intervalTime = 0.3f seconds, you advance your internal timer by only the time since the last frame — not the total time since the last coroutine iteration. If your game is running at 60 fps, then you're counting 16 ms for every 33 ms that pass. If your framerate is higher, your time dilation is even worse.

You can fix this like so, using the same interval to advance your timer that you use to pause the coroutine, and ensuring there's only one flashing coroutine running at a time.

    Coroutine _damageFlash;

    public void TakeDamage(int damage) {
        // You have a function for this,
        // and you already check this in Update!
        //if (currentDamageInterval <= 0) {

        currentHealth -= damage;
        healthBar.SetHealth(currentHealth);

        // Guarantee at most one coroutine runs at a time.
        if (_damageFlash != null)
            StopCoroutine (_damageFlash);

        _ damageFlash = StartCoroutine(DamageFlashing(1f, .3f));

            
        currentDamageInterval = damageInterval;
    }

    IEnumerator DamageFlashing(float duration, float interval) {

        int index = 0;
        var wait = new WaitForSeconds(interval);

        for (float elapsed = 0f; elapsed < duration; elapsed += interval) {
            sr.color = colors[index % 2];
            index++;
            yield return wait;
        }

        _damageFlash = null;
    }
\$\endgroup\$
1
  • \$\begingroup\$ This worked like a charm and makes sense. I really appreciate all of your help. You've helped me several times before! I'm going to be studying this in depth to really make it stick to my brain xD \$\endgroup\$ Mar 10, 2023 at 2:27

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .