0
\$\begingroup\$

I want to be able to change the enum animation mode at runtime.

This way will start the coroutine every frame in Update, but I want to start the coroutine only if the value of the enum variable has changed.

public enum AnimationType { None, SingleColorMorph, MultiColorMorph, Shuffle, Shift };
    public AnimationType myAnimationType;
    LineRenderer myLineRenderer;
    public float morphTime;

    void Start()
    {
        myLineRenderer = this.GetComponent<LineRenderer>();
        switch (myAnimationType)
        {
            case AnimationType.SingleColorMorph:
                StartCoroutine(RandomSingleColorMorphing(myLineRenderer, morphTime));
                break;
            case AnimationType.MultiColorMorph:
                StartCoroutine(RandomMultiColorMorphing(myLineRenderer, morphTime));
                break;
            case AnimationType.Shuffle:
                StartCoroutine(ShuffleGradient(myLineRenderer, .5f));
                break;
            case AnimationType.Shift:
                StartCoroutine(AnimateLoop(myLineRenderer));
                break;
        }
    }

    private void Update()
    {
        switch (myAnimationType)
        {
            case AnimationType.SingleColorMorph:
                StartCoroutine(RandomSingleColorMorphing(myLineRenderer, morphTime));
                break;
            case AnimationType.MultiColorMorph:
                StartCoroutine(RandomMultiColorMorphing(myLineRenderer, morphTime));
                break;
            case AnimationType.Shuffle:
                StartCoroutine(ShuffleGradient(myLineRenderer, .5f));
                break;
            case AnimationType.Shift:
                StartCoroutine(AnimateLoop(myLineRenderer));
                break;
        }
    }

The full script :

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


[RequireComponent(typeof(LineRenderer))]
public class LineRendererColors : MonoBehaviour
{
    public enum AnimationType { None, SingleColorMorph, MultiColorMorph, Shuffle, Shift };
    public AnimationType myAnimationType;
    LineRenderer myLineRenderer;
    public float morphTime;
    void Start()
    {
        myLineRenderer = this.GetComponent<LineRenderer>();
        switch (myAnimationType)
        {
            case AnimationType.SingleColorMorph:
                StartCoroutine(RandomSingleColorMorphing(myLineRenderer, morphTime));
                break;
            case AnimationType.MultiColorMorph:
                StartCoroutine(RandomMultiColorMorphing(myLineRenderer, morphTime));
                break;
            case AnimationType.Shuffle:
                StartCoroutine(ShuffleGradient(myLineRenderer, .5f));
                break;
            case AnimationType.Shift:
                StartCoroutine(AnimateLoop(myLineRenderer));
                break;
        }
    }

    void SetSingleColor(LineRenderer lineRendererToChange, Color newColor)
    {
        lineRendererToChange.startColor = newColor;
        lineRendererToChange.endColor = newColor;
    }

    void SetSingleColor2(LineRenderer lineRendererToChange, Color newColor)
    {
        Gradient tempGradient = new Gradient();

        GradientColorKey[] tempColorKeys = new GradientColorKey[2];
        tempColorKeys[0] = new GradientColorKey(newColor, 0);
        tempColorKeys[1] = new GradientColorKey(newColor, 1);

        tempGradient.colorKeys = tempColorKeys;

        lineRendererToChange.colorGradient = tempGradient;
    }

    void SetSingleColor3(LineRenderer lineRendererToChange, Color newColor)
    {
        Gradient tempGradient = lineRendererToChange.colorGradient;

        GradientColorKey[] tempColorKeys = tempGradient.colorKeys;
        for (int i = 0; i < tempColorKeys.Length; i++)
        {
            tempColorKeys[i].color = newColor;
        }

        tempGradient.colorKeys = tempColorKeys;

        lineRendererToChange.colorGradient = tempGradient;
    }

    IEnumerator ShuffleGradient(LineRenderer targetLineRenderer, float waitTime)
    {
        while (true)
        {
            ShuffleGradient(targetLineRenderer);
            yield return new WaitForSeconds(waitTime);
        }
    }

    void ShuffleGradient(LineRenderer targetLineRenderer)
    {
        GradientColorKey[] newColorKeys = targetLineRenderer.colorGradient.colorKeys;
        for (int i = 0; i < newColorKeys.Length; i++)
        {
            Color tempColor = newColorKeys[i].color;
            int randomIndex = Random.Range(0, newColorKeys.Length - 1);
            newColorKeys[i].color = newColorKeys[randomIndex].color;
            newColorKeys[randomIndex].color = tempColor;
        }
        Gradient tempGradient = targetLineRenderer.colorGradient;
        tempGradient.colorKeys = newColorKeys;
        targetLineRenderer.colorGradient = tempGradient;
    }


    IEnumerator RandomMultiColorMorphing(LineRenderer lineRendererToChange, float timeToMorph)
    {
        float time = 0;

        while (true)
        {
            GradientColorKey[] initialColorKeys = lineRendererToChange.colorGradient.colorKeys;
            GradientColorKey[] newColorKeys = GenerateRandomColorKeys(initialColorKeys);
            time = 0;
            while (time < timeToMorph)
            {
                time += Time.deltaTime;
                float progress = time / timeToMorph;
                GradientColorKey[] currentColorKeys = GradientColorKeyLerp(initialColorKeys, newColorKeys, progress);
                Gradient tempGradient = lineRendererToChange.colorGradient;
                tempGradient.colorKeys = currentColorKeys;
                lineRendererToChange.colorGradient = tempGradient;
                yield return null;
            }
            yield return null;
        }
    }

    GradientColorKey[] GradientColorKeyLerp(GradientColorKey[] initialColorKeys, GradientColorKey[] endColorKeys, float progress)
    {
        GradientColorKey[] newColorKeys = new GradientColorKey[initialColorKeys.Length];
        for (int i = 0; i < newColorKeys.Length; i++)
        {
            newColorKeys[i].color = Color.Lerp(initialColorKeys[i].color, endColorKeys[i].color, progress);
            newColorKeys[i].time = initialColorKeys[i].time;
        }
        return newColorKeys;
    }

    //assigns new color to each colorkey and uses Time from incomingColorKeys
    GradientColorKey[] GenerateRandomColorKeys(GradientColorKey[] incomingColorKeys)
    {
        GradientColorKey[] newColorKeys = new GradientColorKey[incomingColorKeys.Length];
        for (int i = 0; i < newColorKeys.Length; i++)
        {
            newColorKeys[i].color = RandomColor();
            newColorKeys[i].time = incomingColorKeys[i].time;
        }
        return newColorKeys;
    }

    //asumes Single color, 2 colorkeys
    IEnumerator RandomSingleColorMorphing(LineRenderer lineRendererToChange, float timeToMorph)
    {
        float time = 0;
        Color initialColor = lineRendererToChange.colorGradient.colorKeys[0].color;
        //this reduces colorkey amount to 2 just in case.
        SetSingleColor2(lineRendererToChange, initialColor);

        while (true)
        {
            initialColor = lineRendererToChange.colorGradient.colorKeys[0].color;
            Color targetColor = RandomColor();
            time = 0;
            while (time < timeToMorph)
            {
                time += Time.deltaTime;
                float progress = time / timeToMorph;
                Color currentColor = Color.Lerp(initialColor, targetColor, progress);
                SetSingleColor(lineRendererToChange, currentColor);
                yield return null;
            }
            yield return null;
        }
    }

    //Basically Color.Lerp?
    Color ColorLerpMath(Color firstColor, Color secondColor, float progress)
    {
        Vector3 firstRGB = new Vector3(firstColor.r, firstColor.g, firstColor.b);
        Vector3 secondRGB = new Vector3(secondColor.r, secondColor.g, secondColor.b);
        Vector3 difference = secondRGB - firstRGB;
        Vector3 lerpedRGB = firstRGB + (progress * difference);
        return new Color(lerpedRGB.x, lerpedRGB.y, lerpedRGB.z);
    }

    Color RandomColor()
    {
        return new Color(Random.Range(0f, 1f), Random.Range(0f, 1f), Random.Range(0f, 1f));
    }

    //returns the gradient with a copy of the first key for intersection purposes.
    Gradient AddInitialCopy(Gradient incomingGradient)
    {
        List<GradientColorKey> newColorKeys = new List<GradientColorKey>(incomingGradient.colorKeys);
        Color interSectionColor = newColorKeys[0].color;
        newColorKeys.Insert(0, new GradientColorKey(interSectionColor, 0));
        Gradient newInitGradient = new Gradient();
        newInitGradient.colorKeys = newColorKeys.ToArray();
        return newInitGradient;
    }

    //remove first and last keys since they dont shift.
    List<GradientColorKey> RemoveFirstAndLast(Gradient incomingGradient)
    {
        List<GradientColorKey> currentColorKeys = new List<GradientColorKey>(incomingGradient.colorKeys);
        currentColorKeys.RemoveAt(currentColorKeys.Count - 1);
        currentColorKeys.RemoveAt(0);
        return currentColorKeys;
    }

    Color GetIntersectionColor(List<GradientColorKey> incomingKeys, int lowestIndex, int highestIndex)
    {
        Color firstColor = incomingKeys[lowestIndex].color;
        Color lastColor = incomingKeys[highestIndex].color;
        float distance = 1 - (incomingKeys[highestIndex].time - incomingKeys[lowestIndex].time);
        float colorLerpAmount = (1f - incomingKeys[highestIndex].time) / distance; ;
        Color newIntersectionColor = Color.Lerp(lastColor, firstColor, colorLerpAmount);
        return newIntersectionColor;
    }

    //accepts max 7 colors, 1st and last should be at 0 and 1
    IEnumerator AnimateLoop(LineRenderer lineRendererToChange, float movementPerTick = .001f)
    {
        lineRendererToChange.colorGradient = AddInitialCopy(lineRendererToChange.colorGradient);

        while (true)
        {
            List<GradientColorKey> currentColorKeys = RemoveFirstAndLast(lineRendererToChange.colorGradient);
            float highestTime = 0;
            float lowestTime = 1;
            int highestIndex = currentColorKeys.Count - 1;
            int lowestIndex = 0;
            //Move all inner ones.
            for (int i = 0; i < currentColorKeys.Count; i++)
            {
                GradientColorKey tempColorKey = currentColorKeys[i];
                float newTime = tempColorKey.time + movementPerTick;

                if (newTime > 1)
                {
                    newTime = newTime - 1;
                }
                tempColorKey.time = newTime;
                currentColorKeys[i] = tempColorKey;
                if (newTime < lowestTime)
                {
                    lowestTime = newTime;
                    lowestIndex = i;
                }
                if (newTime > highestTime)
                {
                    highestTime = newTime;
                    highestIndex = i;
                }
            }
            Color newIntersectionColor = GetIntersectionColor(currentColorKeys, lowestIndex, highestIndex);
            currentColorKeys.Insert(0, new GradientColorKey(newIntersectionColor, 0));
            currentColorKeys.Add(new GradientColorKey(newIntersectionColor, 1));
            Gradient tempGradient = lineRendererToChange.colorGradient;
            tempGradient.colorKeys = currentColorKeys.ToArray();
            lineRendererToChange.colorGradient = tempGradient;
            yield return null;
        }
    }

    void AssignGradient(LineRenderer targetLineRenderer, Gradient newGradient)
    {
        targetLineRenderer.colorGradient = newGradient;
    }

    void DrawTestLine()
    {
        Vector3 firstPos = new Vector3(-5, 0, 0);
        Vector3 secondPos = new Vector3(5, 0, 0);
        int resolution = 100;
        myLineRenderer.positionCount = resolution;
        myLineRenderer.SetPositions(MakeLine(firstPos, secondPos, 100));
    }

    //makes a line from point A to point B with resolution of size points
    Vector3[] MakeLine(Vector3 initPos, Vector3 endPos, int points)
    {
        Vector3 difference = endPos - initPos;
        Vector3[] newLine = new Vector3[points];
        Vector3 differencePerPoint = difference / (float)(points - 1);
        for (int i = 0; i < points; i++)
        {
            newLine[i] = initPos + (differencePerPoint * i);
        }
        return newLine;
    }
}
\$\endgroup\$
2
  • 1
    \$\begingroup\$ What does your coroutine do? Does it really needs to be started every Update? And cant you just change the modes at runtime when you assign a new value to myAnimationType? \$\endgroup\$
    – Zibelas
    May 13, 2022 at 14:17
  • \$\begingroup\$ Maybe you need a SetMyAnimationType method and start the coroutine in it, instead of checking it every frame. \$\endgroup\$
    – Mangata
    May 13, 2022 at 14:19

1 Answer 1

1
\$\begingroup\$

It looks like you might want to do something like this:

// Make your animation type a property, with a setter method that
// switches the animation automatically when its value changes.
[SerializeField]
AnimationType _animationType;
public AnimationType {
    get { return _animationType; }
    set { if(value != _animationType) StartAnimation(value); }
}

public float morphTime;

LineRenderer _lineRenderer;

// Keep a handle to the old animation, so we can stop it.
Coroutine _animationInProgress;

// The setter method won't be called by the Inspector, so in-editor
// we need to watch for changes ourselves. I've wrapped this in
// pre-processor directives to strip it out of game builds, so you
// pay no runtime overhead for this editor convenience.
#if UNITY_EDITOR
AnimationType _cachedAnimationType;
void OnValidate() {
    if (_animationType != _cachedAnimationType) {
        if (_animationInProgress == null)
            _cachedationAnimType = _animationType;
        else
            StartAnimation(_animationType);
    }        
}
#endif

void StartAnimation(AnimationType animType) {
    _animationType = animType;
    #if UNITY_EDITOR
    _cachedAnimationType = animType;
    #endif

    if (_animationInProgress != null)
        StopCoroutine(_animationInProgress);

    _animationInProgress = StartCoroutine(SelectAnimation(animType));
}

IEnumerator SelectAnimation(AnimationType animType) {
    switch (animType) {
        case AnimationType.SingleColorMorph:
            return RandomSingleColorMorphing(_lineRenderer, morphTime);
        case AnimationType.MultiColorMorph:
            return RandomMultiColorMorphing(_lineRenderer, morphTime);
        case AnimationType.Shuffle:
            return ShuffleGradient(_lineRenderer, .5f);
        case AnimationType.Shift:
            return AnimateLoop(_lineRenderer);
        default:
            return null;
    }
}

void Start() {
    _lineRenderer = GetComponent<LineRenderer>();
    StartAnimation(_animationType);
}

// No Update() method needed.
\$\endgroup\$
4
  • \$\begingroup\$ I'm getting errors. errors on the get/set the _animationType not exist. then also errors in the IEnumerator SelectAnimation the AnimationType not exist in this context. i added to my question the full script too. \$\endgroup\$
    – Daniel Lip
    May 13, 2022 at 15:25
  • 1
    \$\begingroup\$ You are experienced enough to solve trivial compilation errors like that yourself, by ensuring you have declared the AnimationType enum in that context. \$\endgroup\$
    – DMGregory
    May 13, 2022 at 15:37
  • \$\begingroup\$ let me solve it on my own. \$\endgroup\$
    – Daniel Lip
    May 13, 2022 at 15:45
  • 1
    \$\begingroup\$ If you have a new question, post a new question. Comments are not for new feature requests. \$\endgroup\$
    – DMGregory
    May 13, 2022 at 16:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .