我们使用Unity或者其他软件都有一个颜色选择功能,但是这个组件只能在编辑模式中用。因此我就自己开发了一个颜色选择器的插件。
在网上也看到其他人的实现方法,都大同小异,通过新建一张texture,再用代码在图片上渲染颜色。正好我刚刚学习了Shader Forge,所以就想着用shader来渲染颜色,也可以复习新学到的知识。
开发环境:Unity2017.1.0f3 ShaderForge1.38
实现初效果
界面使用UGUI开发,选择器分为三个模块,Saturation,Hue,Color。
Hue是 七色渐变图,实现多种颜色的选择。
Saturation是Hue选择的颜色的饱和度。
Color是最后选择的颜色。
下面是每个功能的逐个实现。
首先用UGUI创建界面,可以全部使用Image
Saturation大小是80,轴点放到左上角。创建子节点SatDot,大小为5。
Hue高80,宽10,轴点放到最上方居中。创建子节点HueDot,高为3,宽为10。
ShaderForge创建2D shader,命名为HueShader,节点图如下
HueShader自带有Hue节点,直接使用即可。因为Hue是从上到下,所以UV只需要V点的区域。
ShaderForge创建2D shader,命名为SaturationShader,节点图如下
因为需要在Hue上选择的颜色,只要将HueDot的y坐标相对Hue的高度的比例传给Value。
ShaderForge创建2D shader,命名为ColorShader,节点图如下
Value是HueDot位置相对Hue的高度的比例(同上),u是SatDot的x坐标相对于Saturation的宽度的比例,v是SatDot的y坐标相对于Saturation的高度的比例。
shader创建完成,在创建3个材质球,将3个shader分别拖到材质球,把材质球对应给不同的Image。
下面就开始一些简单的C#代码实现颜色的选择。
首先SatDot要在Saturation范围内通过鼠标拖动可以上下左右移动,HueDot要在Hue范围内只能上下移动,不能左右移动。
创建C#脚本,将脚本挂到SatDot和HueDot。如下代码即可使鼠标拖动SatDot和HueDot。
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class DragDot : MonoBehaviour,IDragHandler,IBeginDragHandler,IEndDragHandler
{
public void OnDrag(PointerEventData eventData)
{
if(this.transform.parent.name=="Saturation"){
Saturation (eventData.position);
}else{
Hue(eventData.position);
}
}
public void Saturation(Vector3 mousePos){
this.transform.position = mousePos;
if(this.transform.localPosition.x<0f){
this.transform.localPosition = new Vector2 (0f, this.transform.localPosition.y);
}
if(this.transform.localPosition.x>80f){
this.transform.localPosition = new Vector2 (80f, this.transform.localPosition.y);
}
if(this.transform.localPosition.y>0){
this.transform.localPosition = new Vector2 (this.transform.localPosition.x,0);
}
if(this.transform.localPosition.y<-80f){
this.transform.localPosition = new Vector2 (this.transform.localPosition.x,-80f);
}
}
public void Hue(Vector3 mousePos){
this.transform.position = new Vector2 (this.transform.position.x, mousePos.y);
if(this.transform.localPosition.y>0){
this.transform.localPosition = new Vector2 (this.transform.localPosition.x,0);
}
if(this.transform.localPosition.y<-80f){
this.transform.localPosition = new Vector2 (this.transform.localPosition.x,-80f);
}
}
}
然后需要实现点击Saturation和Hue两个Image,两个Dot分别到点击的位置。
创建C#脚本,将脚本挂到Saturation和Hue。如下代码即可使SatDot和HueDot到鼠标点击位置。
using UnityEngine;
using UnityEngine.EventSystems;
public class ClickImage : MonoBehaviour ,IPointerClickHandler
{
public DragPoint dragPoint;
void IPointerClickHandler.OnPointerClick (PointerEventData eventData)
{
if(this.name=="Saturation"){
dragPoint.Saturation (eventData.position);
}else{
dragPoint.Hue(eventData.position);
}
}
再新建一个脚本,可以使shader之间相互传值。
using UnityEngine;
public class ColorPicker : MonoBehaviour
{
public Material satMat;
public Material colorMat;
public void GetHueColor(float hueValue){
satMat.SetFloat ("_Value",hueValue);
}
public void GetSaturationColor(float x,float y,float hueValue){
colorMat.SetFloat ("_Value",hueValue);
colorMat.SetFloat ("_U",x);
colorMat.SetFloat ("_V",y);
}
}
最后,在DragDot 脚本中计算出拖动Dot时,Dot相对于父节点的比例,在调用GetHueColor方法和GetSaturationColor方法。
附上 完整的DragDot 的代码
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class DragPoint : MonoBehaviour,IDragHandler,IBeginDragHandler,IEndDragHandler
{
public void OnDrag(PointerEventData eventData)
{
if(this.transform.parent.name=="Saturation"){
Saturation (eventData.position);
}else{
Hue(eventData.position);
}
}
public void Saturation(Vector3 mousePos){
if (this.name == "HueDot")
return;
this.transform.position = mousePos;
if(this.transform.localPosition.x<0f){
this.transform.localPosition = new Vector2 (0f, this.transform.localPosition.y);
}
if(this.transform.localPosition.x>80f){
this.transform.localPosition = new Vector2 (80f, this.transform.localPosition.y);
}
if(this.transform.localPosition.y>0){
this.transform.localPosition = new Vector2 (this.transform.localPosition.x,0);
}
if(this.transform.localPosition.y<-80f){
this.transform.localPosition = new Vector2 (this.transform.localPosition.x,-80f);
}
float x = Mathf.Abs (this.transform.localPosition.x) / 80f;
float y = Mathf.Abs (this.transform.localPosition.y) / 80f;
//ColorPicker中创建单例
//hueValue是在ColorPicker中初始换
ColorPicker._instance.GetSaturationColor (x, y,ColorPicker._instance.hueValue);
}
public void Hue(Vector3 mousePos){
this.transform.position = new Vector2 (this.transform.position.x, mousePos.y);
if(this.transform.localPosition.y>0){
this.transform.localPosition = new Vector2 (this.transform.localPosition.x,0);
}
if(this.transform.localPosition.y<-80f){
this.transform.localPosition = new Vector2 (this.transform.localPosition.x,-80f);
}
float rate = (Mathf.Abs(this.transform.localPosition.y) - 2) / 80f;
ColorPicker._instance.hueValue = rate;
ColorPicker._instance.GetHueColor (rate);
GameObject dot = GameObject.Find ("SatDot");
dot.GetComponent<DragPoint> ().Saturation (dot.transform.position);
}
}
PC、安卓完美实现。还有一些其他功能,后续开发中…