Unity取色器实现

  1. Unity编辑器的取色器很好用,但是如何在项目中使用,这就难倒了我,查查找找,终于完成了功能。于是在这里简单记录一下。

一、简单介绍

在这里插入图片描述

演示

如上所示,一个简单的取色功能,加上一个反向定位取色板功能

二、原理

首先通过一个色环图片,获取到色环上的基础颜色,再通过分辨率,对内部色盘进行HSV转化,最后通过内部色盘获取最终颜色

三、实现

1、取色

  1. 通过鼠标的点击、拖拽更新色环所需要的颜色
	public void OnPointerDown(PointerEventData eventData)
    {
    
    
        CircleMove(eventData);
    }


    public void OnDrag(PointerEventData eventData)
    {
    
    
        CircleMove(eventData);
    }
     public void CircleMove(PointerEventData eventData)
    {
    
    

        //计算选色环的位置
        //Vector2 o = new Vector2(Screen.width / 1920f * m_ColorWheel.rectTransform.rect.width / 2, Screen.height / 1080f * m_ColorWheel.rectTransform.rect.height / 2);
        Vector2 o = new Vector2(m_ColorWheel.rectTransform.rect.width / 2, m_ColorWheel.rectTransform.rect.height / 2);
        float r = m_ColorWheel.rectTransform.rect.width / 2 - m_SelectorWheel.rect.width / 2;

        //鼠标位置 - 圆形位置 = 圆心指向鼠标位置的向量
        Vector3 mousepos = transform.parent.InverseTransformPoint(eventData.position) -
                           transform.parent.InverseTransformPoint(transform.position + (Vector3)o);
        Vector3 rightPos = transform.parent.InverseTransformVector(m_ColorWheel.rectTransform.right);

        o = new Vector2(m_ColorWheel.rectTransform.rect.width / 2, m_ColorWheel.rectTransform.rect.height / 2);
        angle = VectorAngle(mousepos, rightPos);
        newV2.x = o.x + r * Mathf.Cos(angle * Mathf.Deg2Rad);
        newV2.y = o.y + r * Mathf.Sin(angle * Mathf.Deg2Rad);
        m_SelectorWheel.localPosition = newV2;

        //获取调色板图片
        Texture2D tex = m_ColorWheel.sprite.texture;
        //1.计算选色环在Image上的位置占比 (0~1)
        //2.通过位置占比,映射获取到要获取的贴图像素位置
        int x = (int)(m_SelectorWheel.localPosition.x / m_ColorWheel.rectTransform.rect.width * tex.width);
        int y = (int)(m_SelectorWheel.localPosition.y / m_ColorWheel.rectTransform.rect.height * tex.height);
        //通过Texture2D.GetPixel这个方法来获取该位置下的像素的颜色值
        Color color = tex.GetPixel(x,y);        RGBToHSV(color);
        UpdateSaturation(currentColorHSV);
        m_SubColorSelector.UpdateColor(eventData);
        
    }
    float VectorAngle(Vector2 from, Vector2 to)
    {
    
    
        float angle;
        Vector3 cross=Vector3.Cross(from, to);
        angle = Vector2.Angle(from, to);
        return cross.z > 0 ? -angle : angle;
    }
  1. 通过色环的颜色转化成HSV
    private void UpdateSaturation(Vector4 hsv)
    {
    
    
        for (int y = 0; y < piexlHeight; y++)
        {
    
    
            for (int x = 0; x < piexlWidth; x++)
            {
    
    
                Color pixColor = GetSaturation(hsv, x / piexlWidth, y / piexlHeight);
                saturationTexture2D.SetPixel(x, y, pixColor);
            }
        }

        saturationTexture2D.Apply();
        m_ColorSaturation.texture = saturationTexture2D;
    }
    private Color GetSaturation(Vector4 hsv, float x, float y)
    {
    
    
        Vector4 saturationHSV = hsv;
        saturationHSV.y = x;
        saturationHSV.z = y;
        saturationHSV.w = 1;
        return HSVToRGB(saturationHSV);
    }
    private Color HSVToRGB(Vector4 hsv)
    {
    
    
        Color color = Color.HSVToRGB(hsv.x, hsv.y, hsv.z);
        color.a = hsv.w;
        return color;
    }
  1. 通过鼠标的点击、拖拽更新内部色盘最终选择出来的颜色
	public void OnPointerDown(PointerEventData eventData)
    {
    
    
        OnDrag(eventData);
    }
    

    public void OnDrag(PointerEventData eventData)
    {
    
    

        //需要将背景调色板的中心点改为左下角(0,0)
        //PointerEventData.position返回的是当前指针的位置,左下角为原点(0,0),右上角(屏幕宽,屏幕高),根据分辨率来算
        Vector3 selectorWheelPos = transform.InverseTransformPoint(eventData.position);
        
        selectorWheelPos.x = Mathf.Clamp(selectorWheelPos.x,0,m_ColorPickPanel.rectTransform.rect.width - 1);
        selectorWheelPos.y = Mathf.Clamp(selectorWheelPos.y,0,m_ColorPickPanel.rectTransform.rect.height - 1);
        m_SelectorWheel.localPosition = selectorWheelPos;

        //获取调色板图片
        Texture2D tex = (Texture2D)m_ColorPickPanel.texture;
        //获取鼠标(smallIcon)在调色板上的x,y轴上的比例
        float xtemp = m_SelectorWheel.localPosition.x / m_ColorPickPanel.rectTransform.rect.width;
        float ytemp = m_SelectorWheel.localPosition.y / m_ColorPickPanel.rectTransform.rect.height;
        
        //设置圆圈颜色分类
        if (ytemp <= 0.5f || xtemp >= 0.5f) m_SelectorWheelImage.color = Color.white;
        else m_SelectorWheelImage.color = Color.black;
        
        //通过鼠标在调色板的位置比例,获取鼠标在调色板上的具体(X,Y)坐标
        int x = (int)(xtemp * tex.width);
        int y = (int)(ytemp * tex.height);

        //通过Texture2D.GetPixel这个方法来获取该位置下的像素的颜色值
        Color color = tex.GetPixel(x, y);
        //设置颜色
        FinalColor = color;
        ColorPlate.color = FinalColor;
    }

2、根据颜色设置取色器

  1. 根据颜色计算HSV
Color.RGBToHSV(color, out float hue, out float saturation, out float brightness);
  1. 计算色环的选取位置
 		//获取内部色盘的最亮色
        RGBToHSV(Color.HSVToRGB(hue, 1, 1));
        //计算色环小圈的位置
        Vector2 o = new Vector2(m_ColorWheel.rectTransform.rect.width / 2, m_ColorWheel.rectTransform.rect.height / 2);
        float r = m_ColorWheel.rectTransform.rect.width / 2 - m_SelectorWheel.rect.width / 2;
        newV2.x = o.x + r * Mathf.Cos(hue*360 * Mathf.Deg2Rad);
        newV2.y = o.y + r * Mathf.Sin(hue*360 * Mathf.Deg2Rad);
        m_SelectorWheel.localPosition = newV2;
  1. 更新内部色盘图片
    private void UpdateSaturation(Vector4 hsv)
    {
    
    
        for (int y = 0; y < piexlHeight; y++)
        {
    
    
            for (int x = 0; x < piexlWidth; x++)
            {
    
    
                Color pixColor = GetSaturation(hsv, x / piexlWidth, y / piexlHeight);
                saturationTexture2D.SetPixel(x, y, pixColor);
            }
        }

        saturationTexture2D.Apply();
        m_ColorSaturation.texture = saturationTexture2D;
    }
  1. 计算内部色盘选取位置

        //计算内部色盘小圈位置
        m_SubColorSelector.m_SelectorWheel.transform.localPosition = new Vector2(saturation * m_SubColorSelector.RectTransform.rect.width, brightness * m_SubColorSelector.RectTransform.rect.height);

四、完整代码

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class ColorSelector : MonoBehaviour,  IPointerDownHandler, IDragHandler
{
    
    

    public SubColorSelector m_SubColorSelector;
    //色环
    public Image m_ColorWheel;
    //调色板
    public RawImage m_ColorSaturation;
    //选择图标
    public RectTransform m_SelectorWheel;
    
    //调色板图片
    private Texture2D saturationTexture2D;

    private readonly float piexlWidth = 256.0f;
    private readonly float piexlHeight = 256.0f;
    private readonly float huePiexWidth = 50.0f;
    //当前HSV
    private Vector4 currentColorHSV = new Vector4(0, 1, 1, 1);
    

    void Awake()
    {
    
    
        saturationTexture2D = new Texture2D((int)piexlWidth, (int)piexlHeight);
        m_ColorSaturation.texture = saturationTexture2D;
    }
    
    void Start()
    {
    
    
        Init();
    }
    public void SetSelectorWheel(Image Ima)
    {
    
    
        //计算传入颜色的HSV
        Color.RGBToHSV(Ima.color, out float hue, out float saturation, out float brightness);
        //获取内部色盘的最亮色
        RGBToHSV(Color.HSVToRGB(hue, 1, 1));
        //计算色环小圈的位置
        Vector2 o = new Vector2(m_ColorWheel.rectTransform.rect.width / 2, m_ColorWheel.rectTransform.rect.height / 2);
        float r = m_ColorWheel.rectTransform.rect.width / 2 - m_SelectorWheel.rect.width / 2;
        newV2.x = o.x + r * Mathf.Cos(hue*360 * Mathf.Deg2Rad);
        newV2.y = o.y + r * Mathf.Sin(hue*360 * Mathf.Deg2Rad);
        m_SelectorWheel.localPosition = newV2;
        //更新内部色盘图片
        UpdateSaturation(currentColorHSV);
        //计算内部色盘小圈位置
        m_SubColorSelector.m_SelectorWheel.transform.localPosition = new Vector2(saturation * m_SubColorSelector.RectTrans.rect.width, brightness * m_SubColorSelector.RectTrans.rect.height);

        m_SubColorSelector.ColorPlate.color = Ima.color;
    }
    public void Init()
    {
    
    
        //计算选色环的位置
        Vector2 o = new Vector2(m_ColorWheel.rectTransform.rect.width / 2, m_ColorWheel.rectTransform.rect.height / 2);
        float r = m_ColorWheel.rectTransform.rect.width / 2 - m_SelectorWheel.rect.width / 2;

        //鼠标位置 - 圆形位置 = 圆心指向鼠标位置的向量
        Vector3 mousepos = transform.parent.InverseTransformPoint(m_SelectorWheel.position) -
                           transform.parent.InverseTransformPoint(transform.position + (Vector3)o);
        Vector3 rightPos = transform.parent.InverseTransformVector(m_ColorWheel.rectTransform.right);

        angle = VectorAngle(mousepos, rightPos);
        newV2.x = o.x + r * Mathf.Cos(angle * Mathf.Deg2Rad);
        newV2.y = o.y + r * Mathf.Sin(angle * Mathf.Deg2Rad);
        m_SelectorWheel.localPosition = newV2;

        //获取调色板图片
        Texture2D tex = m_ColorWheel.sprite.texture;
        //1.计算选色环在Image上的位置占比 (0~1)
        //2.通过位置占比,映射获取到要获取的贴图像素位置
        int x = (int)(m_SelectorWheel.localPosition.x / m_ColorWheel.rectTransform.rect.width * tex.width);
        int y = (int)(m_SelectorWheel.localPosition.y / m_ColorWheel.rectTransform.rect.height * tex.height);
        //通过Texture2D.GetPixel这个方法来获取该位置下的像素的颜色值
        Color color = tex.GetPixel(x, y);
        RGBToHSV(color);
        UpdateSaturation(currentColorHSV);
        m_SubColorSelector.UpdateColor();
    }

    /// <summary>
    /// 更新调色板
    /// </summary>
    /// <param name="hsv"></param>
    private void UpdateSaturation(Vector4 hsv)
    {
    
    
        for (int y = 0; y < piexlHeight; y++)
        {
    
    
            for (int x = 0; x < piexlWidth; x++)
            {
    
    
                Color pixColor = GetSaturation(hsv, x / piexlWidth, y / piexlHeight);
                saturationTexture2D.SetPixel(x, y, pixColor);
            }
        }

        saturationTexture2D.Apply();
        m_ColorSaturation.texture = saturationTexture2D;
    }

    /// <summary>
    /// 根据设置分辨率转化HSV
    /// </summary>
    /// <param name="hsv"></param>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <returns></returns>
    private Color GetSaturation(Vector4 hsv, float x, float y)
    {
    
    
        Vector4 saturationHSV = hsv;
        saturationHSV.y = x;
        saturationHSV.z = y;
        saturationHSV.w = 1;
        return HSVToRGB(saturationHSV);
    }

    private Color HSVToRGB(Vector4 hsv)
    {
    
    
        Color color = Color.HSVToRGB(hsv.x, hsv.y, hsv.z);
        color.a = hsv.w;
        return color;
    }

    private void RGBToHSV(Color color)
    {
    
    
        Color.RGBToHSV(color, out currentColorHSV.x, out currentColorHSV.y, out currentColorHSV.z);
        // 更新调色板的位置
    }

    

    public void OnPointerDown(PointerEventData eventData)
    {
    
    
        CircleMove(eventData);
    }


    public void OnDrag(PointerEventData eventData)
    {
    
    
        CircleMove(eventData);
    }

    public float angle;
    private Vector2 newV2 =  Vector2.zero;
    
    public void CircleMove(PointerEventData eventData)
    {
    
    

        //计算选色环的位置
        //Vector2 o = new Vector2(Screen.width / 1920f * m_ColorWheel.rectTransform.rect.width / 2, Screen.height / 1080f * m_ColorWheel.rectTransform.rect.height / 2);
        Vector2 o = new Vector2(m_ColorWheel.rectTransform.rect.width / 2, m_ColorWheel.rectTransform.rect.height / 2);
        float r = m_ColorWheel.rectTransform.rect.width / 2 - m_SelectorWheel.rect.width / 2;

        //鼠标位置 - 圆形位置 = 圆心指向鼠标位置的向量
        Vector3 mousepos = transform.parent.InverseTransformPoint(eventData.position) -
                           transform.parent.InverseTransformPoint(transform.position + (Vector3)o);
        Vector3 rightPos = transform.parent.InverseTransformVector(m_ColorWheel.rectTransform.right);

        o = new Vector2(m_ColorWheel.rectTransform.rect.width / 2, m_ColorWheel.rectTransform.rect.height / 2);
        angle = VectorAngle(mousepos, rightPos);
        newV2.x = o.x + r * Mathf.Cos(angle * Mathf.Deg2Rad);
        newV2.y = o.y + r * Mathf.Sin(angle * Mathf.Deg2Rad);
        m_SelectorWheel.localPosition = newV2;

        //获取调色板图片
        Texture2D tex = m_ColorWheel.sprite.texture;
        //1.计算选色环在Image上的位置占比 (0~1)
        //2.通过位置占比,映射获取到要获取的贴图像素位置
        int x = (int)(m_SelectorWheel.localPosition.x / m_ColorWheel.rectTransform.rect.width * tex.width);
        int y = (int)(m_SelectorWheel.localPosition.y / m_ColorWheel.rectTransform.rect.height * tex.height);
        //通过Texture2D.GetPixel这个方法来获取该位置下的像素的颜色值
        //Debug.Log(m_SelectorWheel.localPosition + "!!!!!!!!!!!!!!!!");
        Color color = tex.GetPixel(x,y);
        //Debug.Log("+++++++图片中当前像素的颜色值:" + color);
        RGBToHSV(color);
        UpdateSaturation(currentColorHSV);
        m_SubColorSelector.UpdateColor(eventData);
        
    }
    
    float VectorAngle(Vector2 from, Vector2 to)
    {
    
    
        float angle;
        Vector3 cross=Vector3.Cross(from, to);
        angle = Vector2.Angle(from, to);
        return cross.z > 0 ? -angle : angle;
    }
}


using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class SubColorSelector : MonoBehaviour,  IPointerDownHandler, IDragHandler
{
    
    

    //显示板
    public Image ColorPlate;
    //最终颜色
    public Color FinalColor;
    //调色板
    public RawImage m_ColorPickPanel;
    //选择图标
    public RectTransform m_SelectorWheel;
    public RectTransform RectTrans;

    private RawImage m_SelectorWheelImage;
    //记录之前的位置
    Vector3 oldposition;


    void Awake()
    {
    
    
        RectTrans = transform as RectTransform;
        oldposition = m_SelectorWheel.position;
        m_SelectorWheelImage = m_SelectorWheel.GetComponent<RawImage>();
    }

    

    public void OnPointerDown(PointerEventData eventData)
    {
    
    
        OnDrag(eventData);
    }
    

    public void OnDrag(PointerEventData eventData)
    {
    
    

        //需要将背景调色板的中心点改为左下角(0,0)
        //PointerEventData.position返回的是当前指针的位置,左下角为原点(0,0),右上角(屏幕宽,屏幕高),根据分辨率来算
        Vector3 selectorWheelPos = transform.InverseTransformPoint(eventData.position);
        
        selectorWheelPos.x = Mathf.Clamp(selectorWheelPos.x,0,m_ColorPickPanel.rectTransform.rect.width - 1);
        selectorWheelPos.y = Mathf.Clamp(selectorWheelPos.y,0,m_ColorPickPanel.rectTransform.rect.height - 1);
        m_SelectorWheel.localPosition = selectorWheelPos;

        //获取调色板图片
        Texture2D tex = (Texture2D)m_ColorPickPanel.texture;
        //获取鼠标(smallIcon)在调色板上的x,y轴上的比例
        float xtemp = m_SelectorWheel.localPosition.x / m_ColorPickPanel.rectTransform.rect.width;
        float ytemp = m_SelectorWheel.localPosition.y / m_ColorPickPanel.rectTransform.rect.height;
        
        //设置圆圈颜色分类
        if (ytemp <= 0.5f || xtemp >= 0.5f) m_SelectorWheelImage.color = Color.white;
        else m_SelectorWheelImage.color = Color.black;
        
        //通过鼠标在调色板的位置比例,获取鼠标在调色板上的具体(X,Y)坐标
        int x = (int)(xtemp * tex.width);
        int y = (int)(ytemp * tex.height);

        //通过Texture2D.GetPixel这个方法来获取该位置下的像素的颜色值
        Color color = tex.GetPixel(x, y);
        //设置颜色
        FinalColor = color;
        ColorPlate.color = FinalColor;
    }

    /// <summary>
    /// 更新颜色
    /// </summary>
    /// <param name="eventData"></param>
    public void UpdateColor(PointerEventData eventData = null)
    {
    
    
        //获取调色板图片
        Texture2D tex = (Texture2D)m_ColorPickPanel.texture;
        float xtemp = m_SelectorWheel.localPosition.x / m_ColorPickPanel.rectTransform.rect.width;
        float ytemp = m_SelectorWheel.localPosition.y / m_ColorPickPanel.rectTransform.rect.height;
        int x = (int)(xtemp * tex.width);
        int y = (int)(ytemp * tex.height);
        Color color = tex.GetPixel(x, y);
        FinalColor = color;
        ColorPlate.color = FinalColor;
    }
}

五、示例资源

后续更新

已更新
示例场景

猜你喜欢

转载自blog.csdn.net/weixin_43856084/article/details/130408773