天天看点

SRPG游戏开发(十)第五章 颜色映射与职业动画 - 二 颜色组(Color Chart)第五章 颜色映射与职业动画

返回目录

第五章 颜色映射与职业动画

二       颜色组(Color Chart)

颜色组是保存许多颜色的一个容器,可以在Swapper中直接创建List<Color>或Color[]来表示一组颜色,但这既不利于职务分担也不利于编辑。所以,我们单独创建一个容器类ColorChart,用它来保存一组颜色,再添加必备方法,Swapper只需调用它的方法就可以了。

1        创建ColorChart.cs

为了便于编辑颜色,需要它继承自ScriptableObject。同时它还是个颜色的容器,我们让它成为迭代器,继承IEnumerable<Color>并添加索引器(Indexer)。
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace DR.Book.SRPG_Dev.ColorPalette
{
    [Serializable]
    [CreateAssetMenu(fileName = "New Color Chart.asset", menuName = "SRPG/Color Chart")]
    public class ColorChart : ScriptableObject, IEnumerable<Color>
    {
        [SerializeField]
        private Color[] m_Colors = new Color[] { };

        public Color this[int index]
        {
            get { return m_Colors[index]; }
            set { m_Colors[index] = value; }
        }

        public IEnumerator<Color> GetEnumerator()
        {
            foreach (Color color in m_Colors)
            {
                yield return color;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return m_Colors.GetEnumerator();
        }
    }
}
           
这样就可以在Create/SRPG/ColorChart菜单下创建它了,还能编辑我们的颜色,我们还需要动态改变它的颜色数量,所以添加count属性。需要注意的是,除非销毁对象,否则我们要让m_Colors不为null。
/// <summary>
        /// 颜色数量
        /// </summary>
        public int count
        {
            get { return m_Colors.Length; }
            set
            {
                if (value < 0)
                {
                    value = 0;
                }

                if (m_Colors.Length != value)
                {
                    Array.Resize<Color>(ref m_Colors, value);
                }
            }
        }
           

其中,Array.Resize方法保证了在颜色数量发生改变时,保留原来的颜色。

再来我们添加一个整体设置颜色的方法SetColors(Color[])。

/// <summary>
        /// 设置颜色
        /// </summary>
        /// <param name="colors"></param>
        public void SetColors(Color[] colors)
        {
            if (colors == null)
            {
                m_Colors = new Color[0];
            }
            else
            {
                m_Colors = new Color[colors.Length];
                Array.Copy(colors, m_Colors, colors.Length);
            }
        }

           
其中,由于直接赋值会变成Color[]的引用,作为容器这在多数情况是不可取的,所以我们使用Array.Copy方法来复制数组。

2        添加所需的方法

整个流程图其实是需要获取到一个转换颜色过后的Texture2D,所以我来建立这么一个尝试获取转换后的Texture2D的方法“public bool TryGetSwapTexture(Texture2DsrcTexture, ColorChart srcChart, out Texture2D swapTexture)”。

首先,要保证源不能为null。

/// <summary>
        /// 尝试获取转换后的Texture2D
        /// </summary>
        /// <param name="srcTexture"></param>
        /// <param name="srcChart"></param>
        /// <param name="swappingTexture"></param>
        /// <returns></returns>
        public bool TryGetSwapTexture(Texture2D srcTexture, ColorChart srcChart, out Texture2D swapTexture)
        {
            swapTexture = null;
            if (srcTexture == null || srcChart == null)
            {
                return false;
            }

            // TODO 获取转换的Texture2D
            return true;
        }
           
其次,获取素材颜色的工作Texture2D中已经有了,所以不用我们自己来写。如果Texture2D不可读的话,Unity会在Console面板中输出Error。
Color[] colors = srcTexture.GetPixels();
           
再次,我们把其它流程再细分:
  • IndexOf(Color color):获取Color在Chart中的Index;
  • GetSwapColor(Color color,ColorChart srcChart):获取转换后颜色。
先来看关于查找Index的方法(IndexOf),这和一般的查找稍稍有不同。平时,我们直接判断是不是相等就可以了,但因为Unity的颜色存储,并不是使用255,255,255,255的方式存储,而是使用了一个0到1的近似值存储的,并不一定精确。在某些Texture2D中,直接判断相等是无法得到Index的。所以,我们要使用近似值来判断Color是否一致。Unity中已经有了判断近似值的方法,它在Mathf中。
/// <summary>
        /// 获取颜色在表中Index,
        /// 由于Color并不是按255,255,255的整形方式存储,
        /// 所以采用近似值的方式判断是不是同一个颜色
        /// </summary>
        /// <param name="color"></param>
        /// <returns></returns>
        public int IndexOf(Color color)
        {
            if (m_Colors != null)
            {
                for (int i = 0; i < m_Colors.Length; i++)
                {
                    Color tmp = m_Colors[i];
                    if (Mathf.Approximately(color.r, tmp.r)
                        && Mathf.Approximately(color.g, tmp.g)
                        && Mathf.Approximately(color.b, tmp.b)
                        && Mathf.Approximately(color.a, tmp.a))
                    {
                        return i;
                    }
                }
            }
            return -1;
        }
           
Mathf.Approximately是用来判断非常小的近似值的,解决了我们的问题。然后我们根据这个Index来查找转换颜色。
/// <summary>
        /// 获取在本表中的对应位置的颜色,如果没有找到,返回原颜色
        /// </summary>
        /// <param name="color"></param>
        /// <param name="srcChart"></param>
        /// <returns></returns>
        public Color GetSwapColor(Color color, ColorChart srcChart)
        {
            if (srcChart == null)
            {
                return color;
            }

            int index = srcChart.IndexOf(color);
            if (index == -1 || index >= m_Colors.Length)
            {
                return color;
            }

            return m_Colors[index];
        }
           
最后,还剩下创建新的Texture2D并填充颜色了。这也是Texture2D中有的方法。
// 和源相同的设置创建Texture2D
            Texture2D clone = new Texture2D(srcTexture.width, srcTexture.height)
            {
                alphaIsTransparency = srcTexture.alphaIsTransparency,
                wrapMode = srcTexture.wrapMode,
                filterMode = srcTexture.filterMode,
            };
            // 填充转换后的颜色,并保存
            clone.SetPixels(colors);
            clone.Apply();
           

新创建的Texture2D一定要用调用Apply()方法保存,否则不会真正填充颜色。

完整的TryGetSwapTexture方法:

/// <summary>
        /// 尝试获取转换后的Texture2D
        /// </summary>
        /// <param name="srcTexture"></param>
        /// <param name="srcChart"></param>
        /// <param name="swappingTexture"></param>
        /// <returns></returns>
        public bool TryGetSwapTexture(Texture2D srcTexture, ColorChart srcChart, out Texture2D swapTexture)
        {
            swapTexture = null;
            if (srcTexture == null || srcChart == null)
            {
                return false;
            }

            // 获取源图所有颜色,并转换颜色
            Color[] colors = srcTexture.GetPixels();
            for (int i = 0; i < colors.Length; i++)
            {
                if (colors[i].a != 0)
                {
                    colors[i] = GetSwapColor(colors[i], srcChart);
                }                
            }

            // 和源相同的设置创建Texture2D
            Texture2D clone = new Texture2D(srcTexture.width, srcTexture.height)
            {
                alphaIsTransparency = srcTexture.alphaIsTransparency,
                wrapMode = srcTexture.wrapMode,
                filterMode = srcTexture.filterMode,
            };
            // 填充转换后的颜色,并保存
            clone.SetPixels(colors);
            clone.Apply();
            swapTexture = clone;
            return true;
        }
           

继续阅读