前言
UnityEngine没有提供类似自带颜色拾取器的组件,但是在工业三维可视化领域可能会用到类似的组件,博主这里结合Unity UGUI源码创建一个高仿unity颜色拾取器的组件,可一键创建,监听接口使用或者直接获取组件颜色。上一篇我们已经讲了部分的难点,这篇我们将剩余的难点解决,文章末尾提供Beta版下载地址(HSV颜色模式暂未开启)。
组件特点
- 无需任何asset
- 导入代码即可生成
- 调用接口方便
实现效果
- 组件效果
- 使用方法
- 实际效果
主要内容
上一篇中我们已经讲解如何去创建颜色拾取器的主体,我们这篇简单讲解一下调色板模式的切换和创建使用
- 调色板模式切换
- 创建与使用
详细讲解
调色板模式切换
通过点击按钮切换当前的调色板模式,调色板模式一共分为六种Hue,Saturation,Brightness,Red,Green,Blue,默认情况下是Hue模式是大家最为熟悉的一种模式
切换实现
切换的实现比较简单,利用状态模式,设置六种状态,在个当前状态赋值新的状态时,更新颜色拾取器相应的其他组件即可,详细见代码。
重要将一下切换模式之后,如何让调色板上的游标跟随到对应颜色的位置
游标跟随
切换颜色模式后,根据当前颜色调整右侧垂直滑动杆的值,这样会让调色板到达一个固定状态,然后根据颜色去色板匹配,但是不能挨个像素进行匹配效率太慢,Saturation、Brightness状态下调色板由两层渐变色带组成,因此需要将颜色拆分两层考虑,Red、Green、Blue模式下只有一层渐变色带,因此需要另一种方式。
Saturation、Brightness
- 获取颜色中rgb三个中较大的两个值,组成主体颜色,定位x轴坐标
- 根据最终颜色和主体颜色计算出三个通道中另一个通道的分量,定位y轴坐标
private Vector2 getPositionByMixedCT( Color color0 )
{
var result = Vector2.zero;
var size = m_colorPalette.GetComponent<RectTransform>().sizeDelta;
switch (PaletteMode)
{
case E_PaletteMode.Hue:
break;
case E_PaletteMode.Saturation:
// 在获取第一层的主题色并计算出x轴坐标
var x1 = m_firstLayerCT.GetPosition(color0).x;
// 获取分量
var red1 = color0.r;
var green1 = color0.g;
var blue1 = color0.b;
// 分量排序
ArrayList array1 = new ArrayList() { red1 , green1 , blue1 };
array1.Sort();
Color mainColor1;
// 根据排序前两个最大值定下主体颜色
if(array1[0].Equals(red1))
mainColor1 = new Color(0 , green1 , blue1 , 1);
else if(array1[0].Equals(green1))
mainColor1 = new Color(red1,0,blue1,1);
else
mainColor1 = new Color(red1,green1,0,1);
// 设置垂直色带
m_verticalFirstCT.SetColors(new Color[] { mainColor1 , Color.white });
// 设置调色板第二层蒙版层
m_secondLayerCT.SetColors(new Color[]
{
new Color(0, 0, 0, 0) * (1 - (float)array1[0]) + new Color(1, 1, 1, 1) * ((float)array1[0]) ,
Color.black
});
// 获取y轴坐标,以上算法重复了一次主题色计算在m_firstLayerCT.GetPosition(color0)中已经计算过一次,后续优化掉这部分算法
float y1 = (1- (float)array1[0] ) * size.y - size.y/2.0f;
result = new Vector2(x1,y1);
break;
case E_PaletteMode.Brightness:
break;
}
return result;
}
public virtual Vector2 GetPosition( Color color0 )
{
// .... 获取主体色
// 得到主题色之后,根据色带方向在最接近的两个颜色之间再通过通道分量计算出更精确的位置
switch (TapeDirection)
{
case E_DrawDirection.Vertical:
// 距离主题色相邻最近的两个基础色的色带位置
var position1 = new Vector2(0 , RectSize.y / 2.0f - pos1 * RectSize.y / ( m_Colors.Count - 1 ));
var position2 = new Vector2(0 , RectSize.y / 2.0f - pos2 * RectSize.y / ( m_Colors.Count - 1 ));
// 判断颜色距离那个颜色通道更近
int sign1 = 1;
if (position1.y > position2.y)
sign1 = -1;
else
sign1 = 1;
// 获取精确坐标位置
return position1 + new Vector2(0 , ( RectSize.y / ( m_Colors.Count - 1 ) ) * offset) * sign1;
case E_DrawDirection.Horizontal:
//..同上操作
}
return Vector2.zero;
}
Red、Green、Blue
- 获取三个通道的分量
- 根据分量和当前模式定位xy坐标
public Vector2 GetPosition(Color color0, ColorPicker.E_PaletteMode mode)
{
// 获取分量
var red = color0.r;
var green = color0.g;
var blue = color0.b;
// 调色板左下角为原点
var origin = new Vector2(-RectSize.x / 2.0f , -RectSize.y / 2.0f);
// 根据不同的模式直接在色板上定位
switch (mode)
{
case ColorPicker.E_PaletteMode.Red:
return origin + new Vector2(RectSize.x * blue , RectSize.y * green);
case ColorPicker.E_PaletteMode.Green:
return origin + new Vector2(RectSize.x * blue, RectSize.y * red);
case ColorPicker.E_PaletteMode.Blue:
return origin + new Vector2(RectSize.x * red , RectSize.y * green);
}
return Vector2.zero;
}
通过以上核心算法讲解,应该对游标定位有了一个大概了解,详细设计见源码。
创建与使用
创建
看过之前几个组件的同学应该已经很熟悉是如何在Unity引擎中插入自己的组件了,这里我们不具体讲解了,实现传送门
public class SpringGUIMenuOptions
{
[MenuItem("GameObject/UI/SpringGUI/ColorPicker" , false , 2071)]
public static void AddColorPicker( MenuCommand menuCommand )
{
GameObject colorPicker =
SpringGUIDefaultControls.CreateColorPicker(GetStandardResources());
PlaceUIElementRoot(colorPicker,menuCommand);
colorPicker.transform.localPosition = Vector3.zero;
colorPicker.AddComponent<ColorPicker>();
}
}
public static class SpringGUIDefaultControls
{
/// <summary>
/// Create Color Picker
/// </summary>
/// <param name="resources"></param>
/// <returns></returns>
public static GameObject CreateColorPicker( Resources resources )
{
//...
//代码量过长,具体参考源码
}
}
在SpringGUI体系下加入以上代码即可在UI中创建ColorPicker组件,接下来我们看看应该如何使用监听
使用
使用原理
public class ColorPicker : UIBehaviour
{
[Serializable]
public class ColorPickerEvent : UnityEvent<Color> { }
[SerializeField]
private ColorPickerEvent m_evnet = new ColorPickerEvent();
public ColorPickerEvent onPicker
{
get { return m_evnet; }
set { m_evnet = value; }
}
}
- 提供继承至UntiyEvent的事件类型并提供一个Color参数
- 监听该事件即可获取到颜色拾取器当前的颜色
- 该事件接口也会在在Inspector面板显示,可以通过拓展的方式实现监听
具体使用
- 代码监听
public class Program : MonoBehaviour
{
public Image image = null;
public ColorPicker Picker = null;
private void Awake()
{
Picker = GameObject.Find("Canvas/ColorPicker").GetComponent<ColorPicker>();
Picker.onPicker.AddListener(color =>
{
// 在颜色拾取变化时即可个Iamge组件赋值颜色
image.color = color;
});
}
}
- Inspector面板监听
public class Program : MonoBehaviour
{
public Image image = null;
public void SetColor( Color color )
{
image.color = color;
}
}
后续拓展
- 颜色拾取器的博客内容就更新到这里,但是颜色拾取器组件还有部分功能尚未实现,因为都是逻辑问题,所以博主会抽空优化代码并把残缺的部分补足。
- 后续的博文中会更新一些Unity讨论群中经常遇到的问题,比如ScrollRect优化(加上缓冲池),Iamge组件的拓展,实现PPT中各种进入、推出的效果甚至是翻书效果,还有如何实现全景照片,还有之前写好的条形图和折线图等,如果你觉得以上组件对你有帮助,关注我的博客,我会持续更新。