How to make a smooth constant RGB color transition?

Hello!

I’m making a little practice project. There are blocks on the screen and I want them to constantly change colors.

I have written this (which is a mush of this: Smooth continuous color change over the game? - Unity Engine - Unity Discussions and this: https://codepen.io/Codepixl/pen/ogWWaK):
but his code stops after a single transition.

void Update()
    {
        colorChange();
    }

    private void colorChange()
    {
        if (timeLeft <= Time.deltaTime)
        {
            if (r >= 0f && b == 0f)
            {
                r--;
                g++;
            }
            if (g >= 0f && r == 0f)
            {
                g--;
                b++;
            }
            if (b >= 0f && g == 0f)
            {
                r++;
                b--;
            }

            // transition complete
            // assign the target color
            GetComponent<SpriteRenderer>().color = targetColor;

            // start a new transition
            targetColor = new Color(r, g, b, 1f);
            timeLeft = 1.0f;
        }
        else
        {
            // transition in progress
            // calculate interpolated color
            GetComponent<SpriteRenderer>().color = Color.Lerp(GetComponent<SpriteRenderer>().color, targetColor, Time.deltaTime / timeLeft);

            // update the timer
            timeLeft -= Time.deltaTime;
        }


    }

Any ideas how to rewrite this code so it works? I’m new to coding real-time code.

Thanks for reading this, and have a nice day!

First, never test floating point for equality. This might work as long as g was really set to zero, but it is not a good habit to get into.

Second, this indicates to me you are thinking 0 to 255:

But you are using this constructor:

new Color(r, g, b, 1f);

which accepts numbers from 0.0f to 1.0f (such as the alpha term above, which is optional and defaults to 1.0f anyway)

Are you looking for new Color32() perhaps?

Also, you can use Color.Lerp() to smoothly change between any two arbitrary colors.

Smoothing movement between any two particular values:

https://discussions.unity.com/t/812925/5

You have currentQuantity and desiredQuantity.

  • only set desiredQuantity
  • the code always moves currentQuantity towards desiredQuantity
  • read currentQuantity for the smoothed value

Works for floats, Vectors, Colors, Quaternions, anything continuous or lerp-able.

The code: https://gist.github.com/kurtdekker/fb3c33ec6911a1d9bfcb23e9f62adac4

2 Likes

Thanks! I tweaked the code a bit to make sure no float shenanigans happen and now it works.

void Update()
    {
        colorChange();
        Debug.Log((int)r);
        Debug.Log(g);
        Debug.Log(b);
    }

    private void colorChange()
    {
        if (timeLeft <= Time.deltaTime)
        {
            if ((int)Math.Round(r) >= 0 && (int)Math.Round(b) == 0)
            {
                r -=0.1f;
                g += 0.1f;
            }
            if ((int)Math.Round(g) >= 0 && (int)Math.Round(r) == 0)
            {
                g -= 0.1f;
                b += 0.1f;
            }
            if ((int)Math.Round(b) >= 0 && (int)Math.Round(g) == 0)
            {
                r += 0.1f;
                b -= 0.1f;
            }

            // transition complete
            // assign the target color
            GetComponent<SpriteRenderer>().color = targetColor;

            // start a new transition
            targetColor = new Color(r, g, b, 1f);
            timeLeft = 1.0f;
        }
        else
        {
            // transition in progress
            // calculate interpolated color
            GetComponent<SpriteRenderer>().color = Color.Lerp(GetComponent<SpriteRenderer>().color, targetColor, Time.deltaTime / timeLeft);

            // update the timer
            timeLeft -= Time.deltaTime;
        }


    }

You’re still overcomplicating the problem. I’m not sure why you’re doing the whole Math.Round rigamaorale in the first place but that part of the if statement is going to evalute to true anytime that color is less than half of total brightness, which doesn’t seem like something you want. And changing the color value by 0.1 each time it changes is just not a particularly smooth transition.

I think you would benefit from the Gradient class. You can make a public Gradient member:

public Gradient colorGradient;

…and edit it in the Inspector to look like a rainbow in the pattern you want to animate through, starting and ending with the same color. And then you can just do something like this to animate using that gradient:

public Gradient colorGradient;
public float gradientDuration = 10f; //seconds
private float currentTime = 0f;

void Update() {
   currentTime += Time.deltaTime;
   if (currentTime > gradientDuration) currentTime = 0f;
   GetComponent<SpriteRenderer>().color = colorGradient.Evaluate(currentTime / gradientDuration);
}
1 Like

My code also preserves color difference between blocks. Also it work smoothly because of the Lerp.

Edit: How to inspect a gradient? It’s in thje code? By serializing it?

Edit: Yeah Serializing seems to work. How could I preserve the color difference with this?

Here is my full code currently:

public class CookieBlock : MonoBehaviour
{
    Color originalColor;
 
    float r = 1f;
    float b = 0f;
    float g = 0f;
    float timeLeft;
    Color targetColor;
 

    public Gradient colorGradient;
    public float gradientDuration = 10f; //seconds
    private float currentTime = 0f;
    void Start()
    {
        originalColor = gameObject.GetComponent<SpriteRenderer>().color;
        r = originalColor.r;
        g = originalColor.g;
    }


 
    //int state = 0;


    void Update()
    {
        colorChange();
    }

    private void colorChange()
    {
        currentTime += Time.deltaTime;
        if (currentTime > gradientDuration) currentTime = 0f;
        GetComponent<SpriteRenderer>().color = colorGradient.Evaluate(currentTime / gradientDuration);
        /*
        if (timeLeft <= Time.deltaTime)
        {
            if ((int)Math.Round(r) >= 0 && (int)Math.Round(b) == 0)
            {
                r -=0.1f;
                g += 0.1f;
            }
            if ((int)Math.Round(g) >= 0 && (int)Math.Round(r) == 0)
            {
                g -= 0.1f;
                b += 0.1f;
            }
            if ((int)Math.Round(b) >= 0 && (int)Math.Round(g) == 0)
            {
                r += 0.1f;
                b -= 0.1f;
            }

            // transition complete
            // assign the target color
            GetComponent<SpriteRenderer>().color = targetColor;

            // start a new transition
            targetColor = new Color(r, g, b, 1f);
            timeLeft = 0.3f;
        }
        else
        {
            // transition in progress
            // calculate interpolated color
            GetComponent<SpriteRenderer>().color = Color.Lerp(GetComponent<SpriteRenderer>().color, targetColor, Time.deltaTime / timeLeft);

            // update the timer
            timeLeft -= Time.deltaTime;
        }
        */


    }

I don’t know what you mean by this.

public class ColorChange : MonoBehaviour
{
    private Camera cam;
    private float duration = 60f;

    private void Start()
    {
        cam = GetComponent<Camera>();
    }

    private void Update()
    {
        float hue = (Time.time * duration) % 360f;
        Color newColor = Color.HSVToRGB(hue / 360f, 0.3f, 1f);
        cam.backgroundColor = newColor;
    }
}

Let’s not over complicate things… XD

1 Like

Closing this thread as a resolution has been found.