[unity]カラーのRGBとHSV値をスクリプトで変換する

プログラマーであれば色指定は RGB だ、それ以外は許さん、という RGB 信者は多いでしょう。
ではこの動画のような 12 色が必要になった時、RGB ですぐに作れるでしょうか?

これが unity 純正カラーパレット。
RGB のスライダーを動かしてチャッチャと作成できる人は、まずいないでしょう。
普通はその上の円から色を選び、■の部分で明るさや鮮やかさを選ぶんじゃないでしょうか。

これが HSV です。

Hue .. 色相(どんな色か) 0~360
Satuation .. 彩度(色の鮮やかさ) 0~100
Value .. 明度(色の明るさ) 0~100

HSB という言い方をすることもありますが、HSV と同じです。
HSL というものについては HSV と異なります。ここでは紹介しません。

この unity が提供してくれた優秀なカラーパレットを使って HSV を変更し色を作成、RGB 値をプログラムに入れる。
これで 12 色ならなんとかなりそうです。

では、こんなのはどうでしょう。

このような、動的な虹色変化は RGB だと作り出すのが困難です。
HSV であれば、H の値をスライドしていくだけでこういう色変化が作れます。

ColorEx.cs

というわけで本題。HSV ←→RGB に変換するクラスです。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
using UnityEngine;
public class ColorEx
{
/// <summary>
/// HSV を RGB に変換する
/// </summary>
/// <param name="h">0-359</param>
/// <param name="s">0-100</param>
/// <param name="v">0-100</param>
/// <returns>RGB(0~1) エラーの場合、全て 0</returns>
public static (float R, float G, float B) HSVtoRGB(float h, float s, float v)
{
if (h < 0.0f || h >= 360.0f)
{
Debug.LogError($"[H] out of range] {h}");
return (0, 0, 0);
}
if (s < 0.0f || s > 100.0f)
{
Debug.LogError($"[S] out of range] {s}");
return (0, 0, 0);
}
if (v < 0.0f || v > 100.0f)
{
Debug.LogError($"[V] out of range] {v}");
return (0, 0, 0);
}
float hmax = v;
float hmin = hmax - ((s / 255) * hmax);
float r = 0;
float g = 0;
float b = 0;
s = s / 100.0f;
v = v / 100.0f;
if (s == 0)
{
r = v;
g = v;
b = v;
}
else
{
float dh = Mathf.Floor(h / 60.0f);
float p = v * (1 - s);
float q = v * (1 - s * (h / 60.0f - dh));
float t = v * (1 - s * (1 - (h / 60.0f - dh)));
switch (dh)
{
case 0 : r = v; g = t; b = p; break;
case 1 : r = q; g = v; b = p; break;
case 2 : r = p; g = v; b = t; break;
case 3 : r = p; g = q; b = v; break;
case 4 : r = t; g = p; b = v; break;
default : r = v; g = p; b = q; break;
}
}
return (r, g, b);
}
/// <summary>
/// RGB を HSV に変換する
/// </summary>
/// <param name="r"></param>
/// <param name="g"></param>
/// <param name="b"></param>
/// <returns>H(0-359), S(0-100), V(0-100) エラーの場合、全て 0</returns>
public static (int H, int S, int V) RGBtoHSV(float r, float g, float b)
{
if (r < 0.0f || r > 1.0f)
{
Debug.LogError($"[R] out of range] {r}");
return (0, 0, 0);
}
if (g < 0.0f || g > 1.0f)
{
Debug.LogError($"[G] out of range] {g}");
return (0, 0, 0);
}
if (b < 0.0f || b > 1.0f)
{
Debug.LogError($"[B] out of range] {b}");
return (0, 0, 0);
}
float max = Mathf.Max(r, g, b);
float min = Mathf.Min(r, g, b);
float diff = max - min;
float h = 0.0f;
float s = 0.0f;
float v = 0.0f;
if (max != min)
{
// H(色相)
if (max == r)
{
h = 60.0f * (g - b) / diff;
}
else
if (max == g)
{
h = 60.0f * (b - r) / diff + 120.0f;
}
else
{
h = 60.0f * (r - g) / diff + 240.0f;
}
// S(彩度)
s = diff / max;
}
if (h < 0)
{
h += 360.0f;
}
h = Mathf.Round(h);
if (h >= 360.0f)
{
h -= 360.0f;
}
s = s * 100.0f;
v = max * 100.0f;
return ((int)h, (int)s, (int)v);
}
}
using UnityEngine; public class ColorEx { /// <summary> /// HSV を RGB に変換する /// </summary> /// <param name="h">0-359</param> /// <param name="s">0-100</param> /// <param name="v">0-100</param> /// <returns>RGB(0~1) エラーの場合、全て 0</returns> public static (float R, float G, float B) HSVtoRGB(float h, float s, float v) { if (h < 0.0f || h >= 360.0f) { Debug.LogError($"[H] out of range] {h}"); return (0, 0, 0); } if (s < 0.0f || s > 100.0f) { Debug.LogError($"[S] out of range] {s}"); return (0, 0, 0); } if (v < 0.0f || v > 100.0f) { Debug.LogError($"[V] out of range] {v}"); return (0, 0, 0); } float hmax = v; float hmin = hmax - ((s / 255) * hmax); float r = 0; float g = 0; float b = 0; s = s / 100.0f; v = v / 100.0f; if (s == 0) { r = v; g = v; b = v; } else { float dh = Mathf.Floor(h / 60.0f); float p = v * (1 - s); float q = v * (1 - s * (h / 60.0f - dh)); float t = v * (1 - s * (1 - (h / 60.0f - dh))); switch (dh) { case 0 : r = v; g = t; b = p; break; case 1 : r = q; g = v; b = p; break; case 2 : r = p; g = v; b = t; break; case 3 : r = p; g = q; b = v; break; case 4 : r = t; g = p; b = v; break; default : r = v; g = p; b = q; break; } } return (r, g, b); } /// <summary> /// RGB を HSV に変換する /// </summary> /// <param name="r"></param> /// <param name="g"></param> /// <param name="b"></param> /// <returns>H(0-359), S(0-100), V(0-100) エラーの場合、全て 0</returns> public static (int H, int S, int V) RGBtoHSV(float r, float g, float b) { if (r < 0.0f || r > 1.0f) { Debug.LogError($"[R] out of range] {r}"); return (0, 0, 0); } if (g < 0.0f || g > 1.0f) { Debug.LogError($"[G] out of range] {g}"); return (0, 0, 0); } if (b < 0.0f || b > 1.0f) { Debug.LogError($"[B] out of range] {b}"); return (0, 0, 0); } float max = Mathf.Max(r, g, b); float min = Mathf.Min(r, g, b); float diff = max - min; float h = 0.0f; float s = 0.0f; float v = 0.0f; if (max != min) { // H(色相) if (max == r) { h = 60.0f * (g - b) / diff; } else if (max == g) { h = 60.0f * (b - r) / diff + 120.0f; } else { h = 60.0f * (r - g) / diff + 240.0f; } // S(彩度) s = diff / max; } if (h < 0) { h += 360.0f; } h = Mathf.Round(h); if (h >= 360.0f) { h -= 360.0f; } s = s * 100.0f; v = max * 100.0f; return ((int)h, (int)s, (int)v); } }
using UnityEngine;

public class ColorEx
{
    /// <summary>
    /// HSV を RGB に変換する
    /// </summary>
    /// <param name="h">0-359</param>
    /// <param name="s">0-100</param>
    /// <param name="v">0-100</param>
    /// <returns>RGB(0~1) エラーの場合、全て 0</returns>
    public static (float R, float G, float B) HSVtoRGB(float h, float s, float v)
    {
        if (h < 0.0f || h >= 360.0f)
        {
            Debug.LogError($"[H] out of range] {h}");
            return (0, 0, 0);
        }
        if (s < 0.0f || s > 100.0f)
        {
            Debug.LogError($"[S] out of range] {s}");
            return (0, 0, 0);
        }
        if (v < 0.0f || v > 100.0f)
        {
            Debug.LogError($"[V] out of range] {v}");
            return (0, 0, 0);
        }

        float hmax = v;
        float hmin = hmax - ((s / 255) * hmax);
        float r    = 0;
        float g    = 0;
        float b    = 0;
   
        s = s / 100.0f;
        v = v / 100.0f;
   
        if (s == 0)
        {
            r = v;
            g = v;
            b = v;
        }
        else
        {
            float dh = Mathf.Floor(h / 60.0f);
            float p  = v * (1 - s);
            float q  = v * (1 - s * (h / 60.0f - dh));
            float t  = v * (1 - s * (1 - (h / 60.0f - dh)));
   
            switch (dh)
            {
                case 0  : r = v; g = t; b = p; break;
                case 1  : r = q; g = v; b = p; break;
                case 2  : r = p; g = v; b = t; break;
                case 3  : r = p; g = q; b = v; break;
                case 4  : r = t; g = p; b = v; break;
                default : r = v; g = p; b = q; break;
            }
        }
        
        return (r, g, b);
    }

    /// <summary>
    /// RGB を HSV に変換する
    /// </summary>
    /// <param name="r"></param>
    /// <param name="g"></param>
    /// <param name="b"></param>
    /// <returns>H(0-359), S(0-100), V(0-100) エラーの場合、全て 0</returns>
    public static (int H, int S, int V) RGBtoHSV(float r, float g, float b)
    {
        if (r < 0.0f || r > 1.0f)
        {
            Debug.LogError($"[R] out of range] {r}");
            return (0, 0, 0);
        }
        if (g < 0.0f || g > 1.0f)
        {
            Debug.LogError($"[G] out of range] {g}");
            return (0, 0, 0);
        }
        if (b < 0.0f || b > 1.0f)
        {
            Debug.LogError($"[B] out of range] {b}");
            return (0, 0, 0);
        }

        float max  = Mathf.Max(r, g, b);
        float min  = Mathf.Min(r, g, b);   
        float diff = max - min;

        float h    = 0.0f;
        float s    = 0.0f;
        float v    = 0.0f;
   
        if (max != min)
        {
            // H(色相)  
            if (max == r)
            {
                h = 60.0f * (g - b) / diff;
            }
            else
            if (max == g)
            {
                h = 60.0f * (b - r) / diff + 120.0f;
            }
            else
            {
                h = 60.0f * (r - g) / diff + 240.0f;
            }
     
            // S(彩度)
            s = diff / max;
        }
 
        if (h < 0)
        {
            h += 360.0f;
        }
        h = Mathf.Round(h);
        if (h >= 360.0f)
        {
            h -= 360.0f;
        }

        s = s   * 100.0f;
        v = max * 100.0f;

        return ((int)h, (int)s, (int)v);
    }
}

Exsample.cs(サンプルコード)

動画のサンプルコードも紹介しておきます。(一部はしょりましたが)

ヒエラルキーに TextMeshProUGUI を一つ配置し、合わせて生成された Canvas に次のコードをアタッチします。
ColorEx.cs もプロジェクトに配置しておいてください。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
public class Exsample : MonoBehaviour
{
const int CIRCLE_MAX = 12;
List<TextMeshProUGUI> texts;
// Start is called before the first frame update
void Start()
{
var baseText = GetComponentInChildren<TextMeshProUGUI>();
texts = new List<TextMeshProUGUI>();
// 12 個の●を配置
for (int i = 0; i < CIRCLE_MAX; i++)
{
var angle = 360 * i / CIRCLE_MAX;
var text = Instantiate(baseText, this.transform);
text.SetText("●");
text.fontSize = 100;
var position = Quaternion.Euler(0, 0, angle) * new Vector3(0, 200, 0);
var rect = text.GetComponent<RectTransform>();
rect.localPosition = position;
texts.Add(text);
}
StartCoroutine(color());
}
IEnumerator color()
{
var hsv = ColorEx.RGBtoHSV(texts[0].color.r, texts[0].color.g, texts[0].color.b);
while (true)
{
for (int i = 0; i < CIRCLE_MAX; i++)
{
// 12 個の色相を等間隔に変化させる
int _h = hsv.H + i * (360 / CIRCLE_MAX);
if (_h >= 360)
{
_h -= 360;
}
var text = texts[i];
var rgb = ColorEx.HSVtoRGB(_h, hsv.S, hsv.V);
text.color = new Color(rgb.R, rgb.G, rgb.B, 1);
}
// 少しずつ H(色相)だけ変化させる。SV はそのまま
hsv.H += 2;
if (hsv.H >= 360)
{
hsv.H -= 360;
}
yield return null;
}
}
}
using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; public class Exsample : MonoBehaviour { const int CIRCLE_MAX = 12; List<TextMeshProUGUI> texts; // Start is called before the first frame update void Start() { var baseText = GetComponentInChildren<TextMeshProUGUI>(); texts = new List<TextMeshProUGUI>(); // 12 個の●を配置 for (int i = 0; i < CIRCLE_MAX; i++) { var angle = 360 * i / CIRCLE_MAX; var text = Instantiate(baseText, this.transform); text.SetText("●"); text.fontSize = 100; var position = Quaternion.Euler(0, 0, angle) * new Vector3(0, 200, 0); var rect = text.GetComponent<RectTransform>(); rect.localPosition = position; texts.Add(text); } StartCoroutine(color()); } IEnumerator color() { var hsv = ColorEx.RGBtoHSV(texts[0].color.r, texts[0].color.g, texts[0].color.b); while (true) { for (int i = 0; i < CIRCLE_MAX; i++) { // 12 個の色相を等間隔に変化させる int _h = hsv.H + i * (360 / CIRCLE_MAX); if (_h >= 360) { _h -= 360; } var text = texts[i]; var rgb = ColorEx.HSVtoRGB(_h, hsv.S, hsv.V); text.color = new Color(rgb.R, rgb.G, rgb.B, 1); } // 少しずつ H(色相)だけ変化させる。SV はそのまま hsv.H += 2; if (hsv.H >= 360) { hsv.H -= 360; } yield return null; } } }
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class Exsample : MonoBehaviour
{
    const int CIRCLE_MAX = 12;

    List<TextMeshProUGUI>   texts;

    // Start is called before the first frame update
    void Start()
    {
        var baseText = GetComponentInChildren<TextMeshProUGUI>();
        texts = new List<TextMeshProUGUI>();

        // 12 個の●を配置
        for (int i = 0; i < CIRCLE_MAX; i++)
        {
            var angle = 360 * i / CIRCLE_MAX;

            var text = Instantiate(baseText, this.transform);

            text.SetText("●");
            text.fontSize = 100;

            var position = Quaternion.Euler(0, 0, angle) * new Vector3(0, 200, 0);

            var rect = text.GetComponent<RectTransform>();
            rect.localPosition = position;

            texts.Add(text);
        }

        StartCoroutine(color());
    }

    IEnumerator color()
    {
        var hsv = ColorEx.RGBtoHSV(texts[0].color.r, texts[0].color.g, texts[0].color.b);

        while (true)
        {
            for (int i = 0; i < CIRCLE_MAX; i++)
            {
                // 12 個の色相を等間隔に変化させる
                int _h = hsv.H + i * (360 / CIRCLE_MAX);
                if (_h >= 360)
                {
                    _h -= 360;
                }

                var text = texts[i];
                var rgb  = ColorEx.HSVtoRGB(_h, hsv.S, hsv.V);
                text.color = new Color(rgb.R, rgb.G, rgb.B, 1);
            }

            // 少しずつ H(色相)だけ変化させる。SV はそのまま
            hsv.H += 2;
            if (hsv.H >= 360)
            {
                hsv.H -= 360;
            }

            yield return null;
        }
    }
}

HSV は便利! 覚えておこう

プログラマーといえど、デザインに片足を突っ込むムーブは必要です。(大手だと、分業が進んでいて不要かもしれませんが)

こういうちょっとした事を知ってるだけで表現の幅は広がります。是非活用してください。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA