Wzrd
Wzrd

Reputation: 258

Unity | Coroutine Speeding Up After Each Executions

My coroutine to fade the player out after they die,

// Fade out ragdoll
IEnumerator RagdollFade()
{
    yield return new WaitForSeconds(3f);

    while (startingColour.a > 0.0f)
    {
        headSR.color = new Color(headSR.color.r, headSR.color.g, headSR.color.b, headSR.color.a - (Time.deltaTime / 1.5f));
        bodySR.color = new Color(bodySR.color.r, bodySR.color.g, bodySR.color.b, bodySR.color.a - (Time.deltaTime / 1.5f));
        leftArmSR.color = new Color(leftArmSR.color.r, leftArmSR.color.g, leftArmSR.color.b, leftArmSR.color.a - (Time.deltaTime / 1.5f));
        rightArmSR.color = new Color(rightArmSR.color.r, rightArmSR.color.g, rightArmSR.color.b, rightArmSR.color.a - (Time.deltaTime / 1.5f));
        leftLegSR.color = new Color(leftLegSR.color.r, leftLegSR.color.g, leftLegSR.color.b, leftLegSR.color.a - (Time.deltaTime / 1.5f));
        rightLegSR.color = new Color(rightLegSR.color.r, rightLegSR.color.g, rightLegSR.color.b, rightLegSR.color.a - (Time.deltaTime / 1.5f));

        yield return null;
    }
}

speeds up after each execution.

For example, this first time the coroutine is called everything works fine and after 3 seconds the player is faded out. However, the next time it is called 3 seconds don't pass before the fade, the next time even less time, etc.

startingColour is set in the Start() function.

Upvotes: 0

Views: 406

Answers (3)

Wzrd
Wzrd

Reputation: 258

Ok, I've solved the problem, and it's completely my bad.

I realized that ANOTHER coroutine I had was interfering with the current one, so that's why StopCoroutine() and adding a fade check wasn't working.

Sorry guys for not including that in my question post, you would probably have been able to help me out more effectively.

So for anyone who encounters strange routine behavior in the future, make sure two coroutines are not interfering with each other.

Upvotes: 1

Kha
Kha

Reputation: 46

Try call StopCoroutine() before starting a new Coroutine. Possibly you have a few coroutines working at the same time.

Upvotes: 2

derHugo
derHugo

Reputation: 90724

It seems that your startingColour.a value is always bigger then 0 so the while loop never finishes and your coroutine just runs forever. Hard to tell without seeing the rest of your code.

So if you start it a second time you now have both routines running parallel => now each frame you decrease the alphas by the double amount .. then triple .. etc. and it also doesn't wait the 3 seconds before the first called routines are already running the while loop so they continue to decrease the alphas.


You could use StopAllCoroutines or StopCoroutine in order to interrupt any still running routines when starting a new one. But that's actually more a kind of dirty workaround.


I would rather takle the actual issue and make sure your while loop returns which is currently unlikely to happen since you seem to not change startColor.a anywhere.

Or add a flag not allowing parallel routines at all like e.g.

private bool isFading;

IEnumerator RagdollFade()
{
    if(isFading) yield brake;

    // prevents other routines
    isFading = true;

    ...

    // reset the flag once routine is finished
    isFading = false;
}

Then I would also rather suggest to have one single float value you use for fading using Color.Lerp like

private bool isFading;

// you can also use a fixed duration and not pass it as parameter
// but this way you are even more flexible
IEnumerator RagdollFade(float duration)
{
    if(isFading) yield brake;

    // prevents other routines
    isFading = true;

    yield return new WaitForSeconds(3f);

    // it is more performant to gather all required information beforehand

    headStartColor = headSR.color;
    bodyStartColor = bodySR.color;
    leftArmStartColor = leftArmSR.color;
    rightArmStartColor = rightArmSR.color;
    leftLegStartColor = leftLegSR.color;
    rightLegStartColor = rightLegSR.color;

    headTargetColor = new Color(headStartColor.r, headStartColor.g, headStartColor.b, 0f);
    bodyTargetColor = new Color(bodyStartColor.r, bodyStartColor.g, bodyStartColor.b, 0f);
    leftArmTargetColor = new Color(leftArmStartColor.r, leftArmStartColor.g, leftArmStartColor.b, 0f);
    rightArmTargetColor = new Color(rightArmStartColor.r, rightArmStartColor.g, rightArmStartColor.b, 0f);
    leftLegTargetColor = new Color(leftLegStartColor.r, leftLegStartColor.g, leftLegStartColor.b, 0f);
    rightLegTargetColor = new Color(rightLegStartColor.r, rightLegStartColor.g, rightLegStartColor.b, 0f);

    var passedTime = 0f;
    while (passedTime < duration)
    {
        // get the interpolation factor from 0 to 1
        var factor = passedTime / duration;
        // for adding additional ease-in and ease-out
        // factor = Mathf.SmoothStep(0, 1, factor);

        headSR.color = Color.Lerp(headStartColor, headTargetColor, factor);
        bodySR.color = Color.Lerp(bodyStartColor, bodyTargetColor, factor);
        leftArmSR.color = Color.Lerp(leftArmStartColor, leftArmTargetColor, factor);
        rightArmSR.color = Color.Lerp(rightArmStartColor, rightArmTargetColor, factor);
        leftLegSR.color = Color.Lerp(leftLegStartColor, leftLegTargetColor, factor);
        rightLegSR.color = Color.Lerp(rightLegStartColor, rightLegTargetColor, factor);


        // avoid overshooting
        passedTime += Mathf.Min(Time.deltatime, duration - passedTime);
        yield return null;
    }

    // reset the flag once routine is finished
    isFading = false;
}

This is more flexible and you can add ease-in and ease-out using whatever simple math you like.

Upvotes: 4

Related Questions