简体   繁体   中英

Unity3d: Write a generic color changing class

I wrote this little Unity3D class to fade an array of UI.Image in or out by reducing or increasing the alpha value of their color property. Then I wanted to use it for an array of materials on meshrenderers but realized that materials and UI.Image, though they both have a color property which can be changed, are totally different classes and you access their color properties differently so I cannot use this class for them both.

I tried making a list of just the colors to be changed, but in unity a Color is a struct and cannot be passed by reference.

Do you have any idea how to make this class generic so it can change the color property on several different classes? I sort of wrote myself into a corner here, I'd rather not just copy the class and change it for meshrenderer.material, that would be ugly

        public class FadeThingInOut : MonoBehaviour
        {
            //public Setting fields
            public Image[] ThingsToFade;
            public bool Visible = true;
            public float FadeTime = 1f;

            //private fields
            Color[] InColour;
            Color[] OutColor;

            void Start()
            {
                if (Visible)
                {
                    SetColours(ref InColour, ref OutColor,0f);
                }
                else
                {
                    SetColours(ref OutColor,ref InColour,1f);
                }
            }

            void SetColours(ref Color[] one,ref Color[] two,float other)
            {
                one = new Color[ThingsToFade.Length];
                two = new Color[ThingsToFade.Length];
                for (int i = 0; i < ThingsToFade.Length; i++)
                {
                    one[i] = ThingsToFade[i].color;
                    two[i] = Tools.colorfromalpha(one[i], other);
                }
            }

            public void Fade(bool inout)
            {
                if (inout && !Visible)
                {
                    StartCoroutine(FadeLerp(OutColor,InColour,inout));
                }
                else if (!inout && Visible)
                {
                    StartCoroutine(FadeLerp(InColour, OutColor, inout));
                }
            }

            IEnumerator FadeLerp(Color[] from, Color[] to,bool endstate)
            {
                float nowtime = 0f;
                while (nowtime < FadeTime)
                {
                    nowtime += Time.deltaTime;
                    float ratio = nowtime / FadeTime;
                    for (int i = 0; i < ThingsToFade.Length; i++)
                    {
                        ThingsToFade[i].color = Color.Lerp(from[i], to[i], ratio);
                        yield return null;
                    }
                }
                for (int i = 0; i < ThingsToFade.Length; i++)
                {
                    ThingsToFade[i].color = to[i];
                }
                Visible = endstate;
            }

}

UPDATE

Heres what I ended up doing.

I keep an array of the components and then cast them to one of three types that I am dealing with. There is a series of if statements to deal with the different classes differntly.

I'm still interested in a more elegant solution that may use some aspects of .net or C# that I'm not familiar with though

    public class FadeThingInOut : MonoBehaviour
    {
        //public Setting fields
        public Component[] ThingsToFade;
        public bool Visible = true;
        public float FadeTime = 1f;
        public float Max = 1f;

        //private fields
        Color[] InColour;
        Color[] OutColor;

        void Start()
        {
            if (Visible)
            {
                SetColours(ref InColour, ref OutColor,0f);
            }
            else
            {
                SetColours(ref OutColor, ref InColour, Max);
            }
        }

        void SetColours(ref Color[] one,ref Color[] two,float other)
        {
            one = new Color[ThingsToFade.Length];
            two = new Color[ThingsToFade.Length];
            for (int i = 0; i < ThingsToFade.Length; i++)
            {
                one[i] = GetColour(i);
                two[i] = Tools.colorfromalpha(one[i], other);
            }
        }

        public void Fade(bool inout)
        {
            if (inout && !Visible)
            {
                StartCoroutine(FadeLerp(OutColor,InColour,inout));
            }
            else if (!inout && Visible)
            {
                StartCoroutine(FadeLerp(InColour, OutColor, inout));
            }
        }

        Color GetColour(int index)
        {
            if(ThingsToFade[index].GetType() == typeof(MeshRenderer))
            {
                return (ThingsToFade[index] as MeshRenderer ).material.color;
            }
            else if (ThingsToFade[index].GetType() == typeof(Image))
            {
                return (ThingsToFade[index] as Image).color;
            }
            else if (ThingsToFade[index].GetType() == typeof(RawImage))
            {
                return (ThingsToFade[index] as RawImage).material.color;
            }
            return Color.black;
        }

        void SetColour(int index,Color col)
        {
            if (ThingsToFade[index].GetType() == typeof(MeshRenderer))
            {
                (ThingsToFade[index] as MeshRenderer).material.color = col;
                return;
            }
            else if (ThingsToFade[index].GetType() == typeof(Image))
            {
                (ThingsToFade[index] as Image).color = col;
                return;
            }
            else if (ThingsToFade[index].GetType() == typeof(RawImage))
            {
                (ThingsToFade[index] as RawImage).material.color = col;
                return;
            }
        }

        IEnumerator FadeLerp(Color[] from, Color[] to,bool endstate)
        {
            float nowtime = 0f;
            while (nowtime < FadeTime)
            {
                nowtime += Time.deltaTime;
                float ratio = nowtime / FadeTime;
                for (int i = 0; i < ThingsToFade.Length; i++)
                {
                    SetColour(i, Color.Lerp(from[i], to[i], ratio));
                    yield return null;
                }
            }
            for (int i = 0; i < ThingsToFade.Length; i++)
            {
                SetColour(i, to[i]);
            }
            Visible = endstate;
        }
    }

This isn't super ideal but you can do it this way.

public interface ISetColor
{
     public Color color { get; set; }
}

public class ImageSetColor : MonoBehaviour, ISetColor
{

    public Image m_Image
    public Color color { get {return m_Image.color;} set { m_Image.color = value}

}


public class MaterialSetColor : MonoBehaviour, ISetColor
{

    public Material m_Material
    public Color color { get {return m_Material.color;} set { m_Material.color = value}

}

Then instead of an Image array you can do an array of public MonoBehaviour[] ThingsToFade then anytime you use ThingsToFade you need to cast it to an ISetColor.

Then any component you want to be able to set colors with will need to create a class that inherits from MonoBehaviour and implements the ISetColor interface

This isn't the easiest solution to use in Unity but it should work

You should not write a class. You should write an extension method. Because structs are immutable though, you cannot change their value, so you should write a static function that has as an argument a reference to color, by using the "ref" keyword. Using structs as mutable objects is not recommended most of the time, but it's the best option here.

I wrote this little Unity3D class to fade an array of UI.Image in or out by reducing or increasing the alpha value of their color property.

I dont see why to overcomplicate everything.

Edit: This will fade by adjusting the alpha directly, using floats instead of color swapping. Faster, cheaper, shorter.

IEnumerator FadeLerp(float _from, float _to, bool endstate)
{
    float nowtime = 0f;
    while (nowtime < FadeTime)
    {
        nowtime += Time.deltaTime;
        float ratio = nowtime / FadeTime;
        for (int i = 0; i < ThingsToFade.Length; i++)
        {
            Color _color = ThingsToFade[i].color;
            _color.a = Mathf.Lerp(_from, _to, ratio);
            ThingsToFade[i].color = _color;
            yield return null;
        }
    }
    for (int i = 0; i < ThingsToFade.Length; i++)
    {
        ThingsToFade[i].renderer.enabled = endstate;
    }
}

use color.lerp function. it will fade the color nicely, and if you know how to use the lerp, with a counter and with an array, there should not be a problem :) if tomorrow you are still stucked i'll write the lerping function for you, but keep trying :) It will be a sweet experience if you do it on your own :D

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM