Changing the Color of a Material with an Enum

Hello Comm-Unity,
I am working on a single traffic light script that changes the value of an enum after a delay, and changes the color of the material attached to the script’s gameObject. This sounds confusing, so here is the code:

using UnityEngine;
using System.Collections;

public class TrafficLight : MonoBehaviour
{
    public enum color
    {
        Green,
        Yellow,
        Red
    }

    public color Col = color.Green;
    public float GreenDuration = 5.0f;
    public float YellowDuration = 2.0f;
    public float RedDuration = 5.0f;

    void Update()
    {
        StartCoroutine (ChangeColor ());
    }

    IEnumerator ChangeColor()
    {
        if(Col == color.Green)
        {
                        //Starts Delay
            yield return new WaitForSeconds(GreenDuration);
            //Changes Enum Value
                        Col = color.Yellow;
            //Changes Material Color
                        this.GetComponent<Renderer>().material.color = Color.yellow;
        }
        else if(Col == color.Yellow)
        {
            yield return new WaitForSeconds(YellowDuration);
            Col = color.Red;
            this.GetComponent<Renderer>().material.color = Color.red;
        }
        else if(Col == color.Red)
        {
            yield return new WaitForSeconds(RedDuration);
            Col = color.Green;
            this.GetComponent<Renderer>().material.color = Color.green;
        }
    }
}

However, when I run the code, when it goes from red to green, it’s fine. When it goes from green to yellow, it’s also fine. Then it, as everything always does, messes up in the last possible sequence. Basically, what happens is that it jumps between yellow and red for the duration of the RedDuration float, and then it goes back to green and it’s fine. Any help is much appreciated on this puzzling question.
Thank you,
Tiger27

As far as I know, Unity doesn’t check if a routing is running or not before starting a new instance of it (and i don’t see why it should). By starting your routing on every Update, your controls probably are messed up by the number of routines. Try to move your StartCoroutine on the Awake/Start function, or use a simple bool to keep memory of the current coroutine status, something like:

bool isRunning = false;
void Update{
    if(!isRunning){
        isRunning = true;
        StartCoroutine (ChangeColor ());
    }
}
1 Like

@FreeFly90 Thank you so much! That is exactly what I needed. In case any future people try to do this and have trouble:
Make sure to put a statement at the end of the coroutine resetting the bool to false. Again, thank you so much, this has been puzzling me for quite a bit.

  • Tiger27
1 Like

You’re welcome :slight_smile:

I assume this is a coroutine that should run for the entirety of the lifetime of the object? If so, just start the coroutine in the Start() method and let it loop endlessly:

IEnumerator ChangeColor()
{
    while(true)
    {
        if(Col == color.Green)
        {
                        //Starts Delay
            yield return new WaitForSeconds(GreenDuration);
            //Changes Enum Value
                        Col = color.Yellow;
            //Changes Material Color
                        this.GetComponent<Renderer>().material.color = Color.yellow;
        }
        else if(Col == color.Yellow)
        {
            yield return new WaitForSeconds(YellowDuration);
            Col = color.Red;
            this.GetComponent<Renderer>().material.color = Color.red;
        }
        else if(Col == color.Red)
        {
            yield return new WaitForSeconds(RedDuration);
            Col = color.Green;
            this.GetComponent<Renderer>().material.color = Color.green;
        }
    }
}

If you need more control you can always change the while statement to one with a variable you can set outside the coroutine.

1 Like

You don’t set a Coroutine in an Update, at least not to check, if it is still running or to stop it before restarting it.

The other thing is, that you set an if-statement to check the color and you change the color, before setting the color to the Material-color. So the if statement stops, before its fully through.

And try not to use a while loop if possible. It might freeze the program.

You don’t necro 6±year old posts, please…

People that read those, don’t care about time, only about the answers. Thats why they visit.

While there’s some truth to it, the question is already answered by more than one person and has several viable and correct solutions. This question is 6+ years old. The OP already said that this problem is solved, so there’s no need to reply after such a long time.

Necroposting without any gain is against the code of conduct . Point “1i”.

Apart from that what you said and recommeded is partially confusing and partially wrong / misleading.

Not sure what’s the intention of this sentence. While it’s true that you should avoid starting a new coroutine all the time, using a “running” boolean is common practise and does work.

This point is just plainly wrong. Changing the value of “Col” inside one of the if statements does not suddenly make the code stop. Code generally runs sequencially. When you enter an if statement you’re in and it keeps running the concent no matter what happens to the condition afterwards. Coroutines specifically can be suspended at a yield statement and return at exactly this point at a later time. Though that doesn’t change how the if statement work.

This suggestion / warning just has no grounds. Actually doing what @Timelog suggested is the better approach in comparison to constantly starting a new coroutine once the old one has finished. Coroutines allocate memory when they are started / created. A coroutine that just keeps running is in most cases better for performance. Since this is about a traffic light simulation, you generally have an endless loop.