背景:类似于Unreal的Blueprint里,不同的数据类型,有不同的颜色来表示。
颜色方案有RGBA, HSL,HSV。直接用RGBA当然可以,只是经常容易导致颜色偏暗饱和度不够等问题,因此,直接选用HSV。(至于为什么没有选用HSL,呵呵,没比较,团队中其他人更喜欢HSV,这个问题在业界也是众说纷纭)。
令S=1.0f, V=1.0f,H则在0~359之间变化。
最终显示出来必须是RGBA,因此需要有HSV到RGBA的算法,这个网上很多,参见wiki: http://en.wikipedia.org/wiki/HSL_and_HSV
using System;
using UnityEngine;
class ColorHelper
{
//reference: http://en.wikipedia.org/wiki/HSL_and_HSV
public static Color ColorFromHSV(double _hue, double _saturation, double _value)
{
int hi = Convert.ToInt32(Math.Floor(_hue / 60)) % 6;
double f = _hue / 60 - Math.Floor(_hue / 60);
_value = _value * 255;
int v = Convert.ToInt32(_value);
int p = Convert.ToInt32(_value * (1 - _saturation));
int q = Convert.ToInt32(_value * (1 - f * _saturation));
int t = Convert.ToInt32(_value * (1 - (1 - f) * _saturation));
if (hi == 0)
return new Color((float)v / 255.0f, (float)t / 255.0f, (float)p / 255.0f, 1.0f);
else if (hi == 1)
return new Color((float)q / 255.0f, (float)v / 255.0f, (float)p / 255.0f, 1.0f);
else if (hi == 2)
return new Color((float)p / 255.0f, (float)v / 255.0f, (float)t / 255.0f, 1.0f);
else if (hi == 3)
return new Color((float)p / 255.0f, (float)q / 255.0f, (float)v / 255.0f, 1.0f);
else if (hi == 4)
return new Color((float)t / 255.0f, (float)p / 255.0f, (float)v / 255.0f, 1.0f);
else
return new Color((float)v / 255.0f, (float)p / 255.0f, (float)q / 255.0f, 1.0f);
}
}
需要提到的一点是,Unity里的RGBA里各颜色通道的取值被归一化为0~1.0的float了,不是0~255(当然,Debug时,还是将它们转化为0~255区间的值吧)。这里不小心被坑了下……
关于Hue如何取得,基本要求是同样的类型,颜色必须一样。
方案0:GetInstanceId(),噢噢噢,这个只有UnityEngine.Object才有这接口,System.Object木有这个接口。 此方案不通。
方案1:GetHashCode(),确实能保证同样的类型,在每次的运行过程中,颜色保持一致。但是,GetHashCode()在再次运行程序时得到的HashCode可能会改变。这是有问题的。
改进之,不知道_Type.GetHashCode()的算法如何,不过似乎字符串.GetHashCode(),同样的字符串,多次运行程序得到的HashCode是一样的。于是可以用_Type.FullName.GetHashCode()。
发现通过类型名称字符串得到的HashCode很相近,导致hue相近,RGBA颜色很接近。于是继续改进,将HashCode作一些处理,偏移,倍乘扩大。。。
在改进的路上尝试了一阵,发现,无论怎么改进hashCode,18种基本类型的颜色还是容易接近。。。
嗯,也许有很高大上很好的算法能解决这个问题,但是……对于这种小规模的事情,就解决事情而言,都不如HardCode来得简便有效!
最终的解决方案:
HSV空间下,S=1.0f, V=1.0f,360种颜色如下:
同时,为了方便扩展,也预留一个接口,自定义类型如果不想HardCode配色,也可以通过GetHashCode()来得到Hue从而得到相应颜色。
public Color GetTypeColor()
{
int hue = 0;
if ( TypeHelper .registerTypeHueMap.TryGetValue(paramType, out hue) )
{
return ColorHelper .ColorFromHSV(hue, 1.0f, 1.0f);
}
else
{
int hashcode = paramType.FullName.GetHashCode();
return ColorHelper .ColorFromHSV(hashcode, 1.0f, 1.0f);
}
}
附上测试颜色的代码:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using NewAge;
using System.Reflection;
public class DrawColor : MonoBehaviour {
bool bShowAllHueColors = true;
float top = 0.0f;
public static Dictionary<Type, int> registerTypeHueMap = new Dictionary<Type, int>();
float width = 100.0f;
void Start()
{
Debug.Log("Draw Sth.");
registerTypeHueMap.Add(typeof(bool), 0);
registerTypeHueMap.Add(typeof(int), 120);
registerTypeHueMap.Add(typeof(float), 30);
registerTypeHueMap.Add(typeof(string), 240);
registerTypeHueMap.Add(typeof(Vector2), 50);
registerTypeHueMap.Add(typeof(Vector3), 60);
registerTypeHueMap.Add(typeof(Vector4), 85);
registerTypeHueMap.Add(typeof(Transform), 180);
registerTypeHueMap.Add(typeof(Quaternion), 210);
registerTypeHueMap.Add(typeof(TransformInfo), 255);
registerTypeHueMap.Add(typeof(Shader), 280);
registerTypeHueMap.Add(typeof(object), 160);
registerTypeHueMap.Add(typeof(Matrix4x4), 10);
registerTypeHueMap.Add(typeof(Material), 300);
registerTypeHueMap.Add(typeof(GameObject), 190);
registerTypeHueMap.Add(typeof(Component), 330);
registerTypeHueMap.Add(typeof(AudioClip), 260);
registerTypeHueMap.Add(typeof(AnimationClip), 230);
}
void DrawTypeColor(Type _type)
{
Color colorToUse = ColorHelper.ColorFromHSV(registerTypeHueMap[_type], 1.0f, 1.0f);
GUI.contentColor = colorToUse;
Color debugColor = new Color(colorToUse.r * 255.0f, colorToUse.g * 255.0f, colorToUse.b * 255.0f, colorToUse.a * 255.0f);
top += 30.0f;
GUI.Label(new Rect(20.0f, top, 600.0f, 25.0f), _type.ToString() + " Hue:" + registerTypeHueMap[_type] + " " + debugColor.ToString()); //"show me the color"
}
void DrawAllTypeColors()
{
top = 0.0f;
DrawTypeColor(typeof(int));
DrawTypeColor(typeof(float));
DrawTypeColor(typeof(bool));
DrawTypeColor(typeof(string));
DrawTypeColor(typeof(Vector2));
DrawTypeColor(typeof(Vector3));
DrawTypeColor(typeof(Vector4));
DrawTypeColor(typeof(Transform));
DrawTypeColor(typeof(Quaternion));
DrawTypeColor(typeof(TransformInfo));
DrawTypeColor(typeof(Shader));
DrawTypeColor(typeof(object));
DrawTypeColor(typeof(Matrix4x4));
DrawTypeColor(typeof(Material));
DrawTypeColor(typeof(GameObject));
DrawTypeColor(typeof(Component));
DrawTypeColor(typeof(AudioClip));
DrawTypeColor(typeof(AnimationClip));
}
void DrawHueColor(int _hue, float left, float top)
{
Color colorToUse = ColorHelper.ColorFromHSV(_hue, 1.0f, 1.0f);
GUI.contentColor = colorToUse;
Color debugColor = new Color(colorToUse.r * 255.0f, colorToUse.g * 255.0f, colorToUse.b * 255.0f, colorToUse.a * 255.0f);
// GUI.Label(new Rect(left, top, 400.0f, 25.0f), "Color " + " Hue:" + _hue + " " + debugColor.ToString());
GUI.Label(new Rect(left, top, width, 25.0f), "Color " + " Hue:" + _hue);
}
void DrawAllHueColors()
{
float top = 0.0f;
float left = 20.0f;
for (int i = 0; i < 360; ++i)
{
if (top + 25.0f > 730.0f)
{
top = 0.0f;
left += width;
}
DrawHueColor(i, left, top);
top += 25.0f;
}
}
void OnGUI()
{
if (bShowAllHueColors)
{
DrawAllHueColors();
}
else
{
DrawAllTypeColors();
}
}
}
test