Problems with Color.Lerp in a Coroutine

I’ve set up a Color.Lerp in a coroutine to trigger off a collision between sprites, instead of fading from one color to another it instead flicks from the base color to the end color instantly despite a timer set up inside the coroutine.

Any input would be appreciated!

void OnCollisionEnter2D(Collision2D Col) {
        if (Col.gameObject.name == "Invira-A" || Col.gameObject.name == "Invira-A(Clone)") {
            StartCoroutine(InfectionA());
            Destroy(Col.gameObject);
        }

        if (Col.gameObject.name == "Invira-B" || Col.gameObject.name == "Invira-B(Clone)") {
            StartCoroutine(InfectionB());
            Destroy(Col.gameObject);
        }
    }

    public IEnumerator InfectionA() {
        Debug.Log("Starting Infestation!");
        float ElapsedTime = 0.0f;
        float TotalTime = 6.0f;
        while (ElapsedTime < TotalTime) {
            ElapsedTime += Time.deltaTime;
            SR.color = Color.Lerp(Red, Green, (ElapsedTime / TotalTime));
        }
        Debug.Log("Ending Infestation!");
        yield return new WaitForEndOfFrame();
    }

Hey, this is actually a simple problem to solve =). As maccabe says, a coroutine will only yield execution when you tell it to.

The reason why the you end up going straight to the end colour within the same frame is because there’s no call to yield execution within the while loop.

There are a variety of things you can yield – WaitForSeconds(numberOfSeconds) is a common one (which does as you’d expect) and WaitForEndOfFrame will wait until the end of that frame. The thing is, neither of these are suitable in this case since you want to lerp every single frame.

Instead you can simply return null: yield return null. In this case nothing is returned and execution will simply pick up again on the next frame. This is a good way to iterate over a series of frames.

I’ve removed the WaitForEndOfFrame and added in yield return null in your code below. This should sort everything out =).

void OnCollisionEnter2D(Collision2D Col) {
         if (Col.gameObject.name == "Invira-A" || Col.gameObject.name == "Invira-A(Clone)") {
             StartCoroutine(InfectionA());
             Destroy(Col.gameObject);
         }
 
         if (Col.gameObject.name == "Invira-B" || Col.gameObject.name == "Invira-B(Clone)") {
             StartCoroutine(InfectionB());
             Destroy(Col.gameObject);
         }
     }
 
     public IEnumerator InfectionA() {
         Debug.Log("Starting Infestation!");
         float ElapsedTime = 0.0f;
         float TotalTime = 6.0f;
         while (ElapsedTime < TotalTime) {
             ElapsedTime += Time.deltaTime;
             SR.color = Color.Lerp(Red, Green, (ElapsedTime / TotalTime));
             yield return null;
         }
         Debug.Log("Ending Infestation!");
     }

Coroutines only wait when you tell them. Since there are no yields functions in the while loop

while (ElapsedTime < TotalTime) {
    ElapsedTime += Time.deltaTime;
    SR.color = Color.Lerp(Red, Green, (ElapsedTime / TotalTime));
}

will run through all the values of elapsed time in a single frame because the coroutine is not told to yield between each frame. Change it to

while (ElapsedTime < TotalTime) {
    ElapsedTime += Time.deltaTime;
    SR.color = Color.Lerp(Red, Green, (ElapsedTime / TotalTime));
    yield return new WaitForEndOfFrame();
}

rather than use a while loop change it to

yield return new WaitForSeconds(TotalTime);

I have used this lots of times to achieve the same effect.
Hope this helps