1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

UnityのGUILayout.Boxを角丸のまま背景色を付ける方法

Last updated at Posted at 2023-03-25

はじめに

UnityのIMGUIのGUILayout.Boxは手軽にUIを作れるので何かと便利ですが、GUI.backgroundColor が効かず、背景色を変えるにはTexture2Dを指定します。しかし、Texture2D.redTexture; のようなデフォルトのテクスチャを指定すると、元々角丸だったBoxの角が四角になって、形が変わってしまいます。さらに、枠線も消えてしまいます。

Boxの形状をそのままに背景の色を色々と設定する方法を探しても見つからなかったので、力技で作成してみました。

使用例

以下のコードのように、GUILayout.Box の代わりに ColoredBoxHelper.ColoredBox を呼び出して使用します。

ColoredBoxTest.cs
using UnityEngine;

public class ColoredBoxTest : MonoBehaviour
{
    private void OnGUI()
    {
        GUILayout.Box("デフォルトのボックス");
        
        ColoredBoxHelper.ColoredBox("色付きのボックス", Color.magenta);
        ColoredBoxHelper.ColoredBox("半透明のボックス\n\t半透明のボックス\n\t半透明のボックス", new Color(0f, 1f, 0f, 0.2f));
        ColoredBoxHelper.ColoredBox("半透明 + 文字色", new Color(0f, 1f, 0f, 0.2f), Color.black);
    }
}

すると、以下のように角丸のまま指定した背景色が表示されます。
スクリーンショット 2023-03-25 184639.jpg

Boxの形状は手動で作成しているので、元のBoxとの細かい差異はあると思います。

コード

利用するには以下のコードをプロジェクト内に追加します。

ColoredBoxHelper.cs
using System.Collections.Generic;
using UnityEngine;

public static class ColoredBoxHelper
{
    /* config */
    // use texture2D cache for each color ever used. 
    private static readonly bool EnableCacheTexture2D = true;
    // radius value for rounding box corners
    private static readonly float CornerRadius = 3f;


    /// <summary>
    /// Utility method to use colored GUILayout.Box(...);
    /// Usage:
    /// ColoredBoxHelper.ColoredBox(text, new new Color(1f, 0f, 0f, 0.4f));
    /// </summary>
    public static void ColoredBox(string text, Color boxColor)
    {
        ColoredBox(text, boxColor, Color.white);
    }

    /// <summary>
    /// Utility method to use colored GUILayout.Box(...);
    /// Usage:
    /// ColoredBoxHelper.ColoredBox(text, new new Color(1f, 0f, 0f, 0.4f));
    /// </summary>
    public static void ColoredBox(string text, Color boxColor, Color textColor)
    {
        var originalColor = GUI.contentColor;
        GUI.contentColor = textColor;
        GUILayout.Box(text, GetCustomBoxStyle(boxColor, textColor));
        GUI.contentColor = originalColor;
    }

    private static GUIStyle GetCustomBoxStyle(Color boxColor, Color textColor)
    {
        var style = new GUIStyle(GUI.skin.box);
        style.normal.background = GetOrCreateTexture(boxColor);
        style.normal.textColor = textColor;
        return style;
    }

    private static Dictionary<Color, Texture2D> _texture2DCache = new Dictionary<Color, Texture2D>();

    private static Texture2D GetOrCreateTexture(Color boxColor)
    {
        if (!EnableCacheTexture2D)
            return GenerateRoundedCornerTexture2D(boxColor);

        if (!_texture2DCache.ContainsKey(boxColor))
        {
            _texture2DCache[boxColor] = GenerateRoundedCornerTexture2D(boxColor);
        }

        return _texture2DCache[boxColor];
    }

    private static Texture2D GenerateRoundedCornerTexture2D(Color color)
    {
        int width = 64;
        int height = 64;

        Texture2D texture = new Texture2D(width, height);
        Color[] pixels = new Color[width * height];

        float radius = CornerRadius;

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                int dy = Mathf.Min(y, height - 1 - y);

                if (dy < radius)
                {
                    int dx = Mathf.Min(x, width - 1 - x);
                    var r = Mathf.Sqrt((radius - dx) * (radius - dx) + (radius - dy) * (radius - dy));
                    
                    if (r > radius && dx < radius) continue; // 角の円の外のpixelを除外

                    bool IsBoarder()
                    {
                        if (dx > radius) return false;

                        var rdx = Mathf.Sqrt((radius - dx + 1) * (radius - dx + 1) + (radius - dy) * (radius - dy));
                        var rdy = Mathf.Sqrt((radius - dx) * (radius - dx) + (radius - dy + 1) * (radius - dy + 1));
                        return (rdx > radius || rdy > radius);
                    }

                    if (IsBoarder())
                    {
                        pixels[x + y * width] = Color.black;
                        continue;
                    }
                }

                bool isBoarder = x == 0 || y == 0 || x == width - 1 || y == height - 1;
                Color pixel = isBoarder ? Color.black : color;
                pixels[x + y * width] = pixel;
            }
        }

        for (int x = 1; x < width - 1; x++)
        {
            for (int y = 1; y < height - 1; y++)
            {
                if (pixels[x + y * width] == Color.black)
                {
                    bool hasNearbyBoarderY = false;
                    bool hasNearbyBoarderX = false;
                    
                    if (pixels[x + 1 + y * width] == Color.black) hasNearbyBoarderX = true;
                    if (pixels[x + (y+1) * width] == Color.black) hasNearbyBoarderY = true; 
                    if (pixels[x-1 + y * width] == Color.black) hasNearbyBoarderX = true;
                    if (pixels[x + (y-1) * width] == Color.black) hasNearbyBoarderY = true;

                    if (hasNearbyBoarderX && hasNearbyBoarderY)
                    {
                        // prune angular boarder pixel;
                        pixels[x + y * width] = Color.clear;
                    }
                }
            }
        }

        texture.SetPixels(pixels);
        texture.Apply();

        return texture;
    }
}
  • Texture2D を pixel単位で手動生成して、力技で角丸四角形を描画しています。
  • 毎フレームTextureを作成するのは気がひけるので、デフォルトでは作成したTexture2Dをキャッシュします。
  • コード内部のCornerRadius などの値を調整することで角の丸みを調整することもできます。ただ、丸みを大きくすると、曲線の歪みが目立ちます。
  • 後処理で枠線をなめらかにしようとして頑張っています
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?