• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
1
Question by Ulluses · Mar 21, 2015 at 02:47 PM · coroutinetimerspriterenderercolor.lerponcollision

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();
     }
Comment
Add comment · Show 2
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Ulluses · Mar 21, 2015 at 10:50 PM 0
Share

Hm I've tried both answers and neither have fixed the problem, I tried some further changes but to no avail. I'm sure the correct answer must be close given it's partial function but it just won't seem to lerp slowly at all.

avatar image Ulluses · Mar 22, 2015 at 03:33 AM 0
Share

Putting up the whole script with Johats changes and a few of my own from experimentation, here it is:- using UnityEngine; using System.Collections;

public class RedCellScript : $$anonymous$$onoBehaviour {

 public Color Red;
 public Color Green;
 public Color Yellow;
 public bool Infected;
 private float ColorSpeed;
 private SpriteRenderer SR;

 void Start () {
     SR = gameObject.GetComponent<SpriteRenderer>();
     ColorSpeed = 2f;
 }
 
 void Update () {
 
 }

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

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

 private IEnumerator InfectionA() {
     Debug.Log("Starting Infestation!");
     Infected = true;
     float ElapsedTime = 0.0f;
     float TotalTime = 6.0f;
     Color StartingColor = Red;
     Color EndingColor = Green;
     if (ElapsedTime < TotalTime) {
         ElapsedTime += Time.deltaTime;
         SR.color = Color.Lerp(StartingColor, EndingColor, (ElapsedTime / TotalTime));
         Debug.Log("Changed Color!");
         yield return null;
     }
     Debug.Log("Ending Infestation!");
 }

 public IEnumerator InfectionB() {
     yield return null;
 }

}

3 Replies

· Add your reply
  • Sort: 
avatar image
5
Best Answer

Answer by Johat · Mar 22, 2015 at 12:32 AM

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!");
      }
Comment
Add comment · Show 9 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Ulluses · Mar 22, 2015 at 02:42 AM 0
Share

It reaches the end of the coroutine but no color changes seem to occur at all now, not even a single frame switch to the ending color. The logic you laid out sounds like it should work so there must be something off with the color.lerp function... Perhaps something is off with the time?

avatar image Johat · Mar 22, 2015 at 03:28 AM 0
Share

Hi =).

Color.Lerp should work fine. You can confirm this (and confirm the coroutine is yielding) by using Debug.Log to print out the colour Color.Lerp returns each frame. You should find it changes.

$$anonymous$$y guess would be that something else is going on. What is SR.color and where is it defined? Does anything else change it? Are multiple objects calling and running your coroutines at the same time?

Could you post your full script so we can see if anything else is going on? =)

As a quick litmus test though, to confirm the above works properly try this:

  • Create an empty scene and put a cube in it.

  • Create a script called ColorLerp and attach it to the cube.

  • Press play and then space, you should see the colour correctly change. (Let me know if you don't!)

The code to put in ColorLerp:

 using UnityEngine;
 using System.Collections;
 
 public class ColorLerp : $$anonymous$$onoBehaviour
 {
     public Color StartColor = Color.green;
     public Color EndColor = Color.red;
     public float TransitionTime = 5f;
     
     private $$anonymous$$aterial _my$$anonymous$$aterial;
     // Just to make sure we don't try to lerp if we're already doing so
     private bool _transitioning = false;
     
     private void Awake()
     {
         _my$$anonymous$$aterial = GetComponent<Renderer>().material;
     }
     
     private void Update()
     {
         if(Input.Get$$anonymous$$eyDown($$anonymous$$eyCode.Space) && !_transitioning)
         {
             StartCoroutine(DoLerp());
         }
     }
     
     private IEnumerator DoLerp()
     {
         _transitioning = true;
         
         float timeElapsed = 0f;
         float totalTime = TransitionTime;
         
         Color startColor = StartColor;
         Color endColor = EndColor;
         
         while(timeElapsed < totalTime)
         {
             timeElapsed += Time.deltaTime;
             _my$$anonymous$$aterial.color = Color.Lerp(startColor, endColor, timeElapsed/totalTime);
             yield return null;
         }
         
         _transitioning = false;
     }
 }

I haven't had the chance to quickly test that in Unity, so a couple of typos may have slipped through, just so you know!

The above test should be able to confirm that Color.Lerp with coroutines works okay. So you know the problem must be something else.

As I say, put up the rest of your code so we can take a look =).

avatar image Johat · Mar 22, 2015 at 03:40 AM 0
Share

One thought, just re-looking. Coroutines are run on $$anonymous$$onoBehaviours, so will stop (be destroyed) if that $$anonymous$$onoBehaviour is destroyed. Do you see "Ending Infestation!" show up in the console? If not, I think this might be the problem.

In OnCollision2D, you destroy a GameObject. Is that the same GameObject that the coroutine is running on? (i.e. this script?)

If so, then that's the problem. With your original version, the colour lerped to the end before the call to destroy, resulting in something that instantly jumped to the end colour. In the new version, the coroutine only gets to run one frame before it is destroyed along with the $$anonymous$$onoBehaviour, resulting in code that would work but doesn't get a chance and so the starting colour stays.

Try making sure either the destruction happens after the coroutine is finished, or run the coroutine on an object that doesn't get destroyed (whichever object is changing colour would make the most sense). Let me know if you need help setting that up (I don't know what the rest of your code looks like).

I think that might be your problem anywho.

avatar image Ulluses · Mar 22, 2015 at 03:45 AM 0
Share

The red cell, that has this script, is the one that changes color and the virus that bumps it is destroyed and kicks off the infection process. I've put up the script with your advice added in, there is still no color change although all the debug.logs get printed just fine so the whole coroutine is gone through at least.

avatar image Johat · Mar 22, 2015 at 03:53 AM 1
Share

Hmm. That's odd. =S

Couldn't see anything else weird in your script.

Can you try replacing: Color.Lerp(Red, Green, (ElapsedTime/TotalTime))

with: Color.Lerp(Red, Green, 1f) just to confirm the SpriteRender goes green okay (should be instant again).

Let me know what happens.

Show more comments
avatar image
1

Answer by maccabbe · Mar 21, 2015 at 02:48 PM

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();
 }
Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Ulluses · Mar 21, 2015 at 10:53 PM 0
Share

Didn't seem to work, I tried a few extra things similar and none of them changed the outcome.

avatar image Bunny83 · Mar 22, 2015 at 04:14 AM 0
Share

Don't use WaitForEndOfFrame. This will wait until you reach the end of the current frame. Once you're there and you call it again it will be returned immediately as you are already at the end of the current frame. If you want to wait a single frame, use "yield return null;".

avatar image maccabbe · Mar 22, 2015 at 04:39 AM 0
Share

Thanks for the explanation Bunny83. I wasn't familiar with WaitForEndOfFrame() and couldn't figure out how it was different from yield return null (which I usually use) so I left it in there.

avatar image
0

Answer by GamesDeveloper12 · Mar 21, 2015 at 02:49 PM

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

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Bunny83 · Mar 22, 2015 at 04:18 AM 0
Share

That makes not much sense as he has code in that while loop that should be executed each frame.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

The best place to ask and answer questions about development with Unity.

To help users navigate the site we have posted a site navigation guide.

If you are a new user to Unity Answers, check out our FAQ for more information.

Make sure to check out our Knowledge Base for commonly asked Unity questions.

If you are a moderator, see our Moderator Guidelines page.

We are making improvements to UA, see the list of changes.



Follow this Question

Answers Answers and Comments

4 People are following this question.

avatar image avatar image avatar image avatar image

Related Questions

Destroying gameobject after animation 1 Answer

Coroutine timer running crazy fast 2 Answers

Target multiple units with individual timers upon entering trigger 1 Answer

Play animation 5 sec, stop for 5 seconds, repeat 0 Answers

Coroutines pauses when not focused on the game page. 1 Answer

  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges