【Unity】上下左右を表すThickness型を作成する

C#のGUI表現技術ののXAMLには上下左右を表す Thickness 型というものがありますが Unity にはありません(ないですよね?

なので今回はこの Thickness 型を作成して特定の操作を簡単にしたいと思います。GUIの上下端を表したり

確認環境

今回実装・確認を行う環境は以下の通りです。

  • Unity 2020.3.14f1
  • VisualStudio 2019

Editor上のみで確認しています。

実装コード

ではさっそく実装の紹介です。

Thicknessクラス

まずは上下左右を表す Thickness クラスの実装です。以前も紹介している ValueObject のテンプレートを改造して immutable になるように作成します。この方は JsonUtility などでシリアライズできるように readobly フィールドを使用しないようにしています。

using System;
using UnityEngine;

[Serializable]
public /*readonly*/ struct Thickness : IEquatable<Thickness>
{
    // JsonUtility でシリアライズする事を考慮

    // Fields
    //public readonly float Left;
    //public readonly float Top;
    //public readonly float Right;
    //public readonly float Bottom;
#pragma warning disable IDE0044
    [SerializeField] private float left;
    [SerializeField] private float top;
    [SerializeField] private float right;
    [SerializeField] private float bottom;
#pragma warning restore IDE0044

    // Props
    public float Left => left;
    public float Top => top;
    public float Right => right;
    public float Bottom => bottom;

    public Thickness(float l, float t, float r, float b)
    {
        this.left = l;
        this.top = t;
        this.right = r;
        this.bottom = b;
    }

    // 演算子のオーバーライド
    public static bool operator ==(in Thickness a, in Thickness b) => Equals(a, b);
    public static bool operator !=(in Thickness a, in Thickness b) => !Equals(a, b);

    // 4つの組み合わせからHSVオブジェクトを作成する
    public static implicit operator Thickness((float l, float t, float r, float b) thickness)
    {
        return new Thickness(thickness.l, thickness.t, thickness.r, thickness.b);
    }

    // 等値比較演算子の実装
    public readonly override bool Equals(object obj) => (obj is Thickness _obj) && this.Equals(_obj);

    // IEquatable<T> の implement
    public readonly bool Equals(Thickness other)
    {
        // 個別に記述する
        return ReferenceEquals(this, other) ||
               this.left == other.left &&
               this.top == other.top &&
               this.right == other.right &&
               this.bottom == other.bottom;
    }

    public readonly override int GetHashCode()
    {
        unchecked
        {
            var hashCode = this.left.GetHashCode();
            hashCode = (hashCode * 397) ^ this.top.GetHashCode();
            hashCode = (hashCode * 397) ^ this.right.GetHashCode();
            hashCode = (hashCode * 397) ^ this.bottom.GetHashCode();
            return hashCode;
        }
    }
}

RectTransformExtensionクラス

上記の型をGUIと連携させるために以下のような拡張メソッドを定義します。

using System;
using UnityEngine;

public static class RectTransformExtension
{
    /// <summary>
    /// このオブジェクトを <see cref="RectTransform"/> を仮定して 
    /// <see cref="GetLocalSize(RectTransform)"/> に処理を転送します。
    /// </summary>
    public static Thickness GetLocalSize(this Transform t)
    {
        if (t is RectTransform rt)
        {
            return GetLocalSize(rt);
        }
        throw new InvalidOperationException($"t is not RectTransform.");
    }

    /// <summary>
    /// このオブジェクトの中央を(アンカーなどの設定に関わらず固定で)ゼロとしてローカルサイズを取得します。
    /// </summary>
    /// <remarks>
    /// RectTransform.localPostion と対応関係がある数値になる。
    /// </remarks>
    public static Thickness GetLocalSize(this RectTransform rt)
    {
        UnityEngine.Rect rect = rt.rect;
        float hw = rect.x / 2f;
        float hh = rect.y / 2f;
        return new Thickness(-hw, hh, hw, -hh);
    }
}

使い方

使用方法は以下の通りです。

public void Foo(Image img)
{
    Thickness th = img.transform.GetLocalSize(); // 上下左右の境界の位置を取得する
    float top = th.Top;
    float bottom = th.Bottom;
    float left = th.Left;
    float right = th.Right;
}

こうすれば少しはコードの記述が簡単になるかと思います。

関連記事

takap-tech.com

takap-tech.com