返回目录
第五章 颜色映射与职业动画
二 颜色组(Color Chart)
颜色组是保存许多颜色的一个容器,可以在Swapper中直接创建List<Color>或Color[]来表示一组颜色,但这既不利于职务分担也不利于编辑。所以,我们单独创建一个容器类ColorChart,用它来保存一组颜色,再添加必备方法,Swapper只需调用它的方法就可以了。
1 创建ColorChart.cs
为了便于编辑颜色,需要它继承自ScriptableObject。同时它还是个颜色的容器,我们让它成为迭代器,继承IEnumerable<Color>并添加索引器(Indexer)。这样就可以在Create/SRPG/ColorChart菜单下创建它了,还能编辑我们的颜色,我们还需要动态改变它的颜色数量,所以添加count属性。需要注意的是,除非销毁对象,否则我们要让m_Colors不为null。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(); } } }
/// <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[])。
其中,由于直接赋值会变成Color[]的引用,作为容器这在多数情况是不可取的,所以我们使用Array.Copy方法来复制数组。/// <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); } }
2 添加所需的方法
整个流程图其实是需要获取到一个转换颜色过后的Texture2D,所以我来建立这么一个尝试获取转换后的Texture2D的方法“public bool TryGetSwapTexture(Texture2DsrcTexture, ColorChart srcChart, out Texture2D swapTexture)”。
首先,要保证源不能为null。
其次,获取素材颜色的工作Texture2D中已经有了,所以不用我们自己来写。如果Texture2D不可读的话,Unity会在Console面板中输出Error。/// <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; }
再次,我们把其它流程再细分:Color[] colors = srcTexture.GetPixels();
先来看关于查找Index的方法(IndexOf),这和一般的查找稍稍有不同。平时,我们直接判断是不是相等就可以了,但因为Unity的颜色存储,并不是使用255,255,255,255的方式存储,而是使用了一个0到1的近似值存储的,并不一定精确。在某些Texture2D中,直接判断相等是无法得到Index的。所以,我们要使用近似值来判断Color是否一致。Unity中已经有了判断近似值的方法,它在Mathf中。
- IndexOf(Color color):获取Color在Chart中的Index;
- GetSwapColor(Color color,ColorChart srcChart):获取转换后颜色。
Mathf.Approximately是用来判断非常小的近似值的,解决了我们的问题。然后我们根据这个Index来查找转换颜色。/// <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; }
最后,还剩下创建新的Texture2D并填充颜色了。这也是Texture2D中有的方法。/// <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 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; }