🔲

RectTransformのサイズを取得する方法(アンカー対応)

に公開

ネットの記事にはsizeDeltarect.sizeが紹介されているものが多いのですが、アンカー対応の情報があまりないようですので、今回紹介させていただきます。

以下のコードでは、再帰的に親のRectTransformを探索していき、アンカー設定をしていない親のサイズから子に順番に、アンカーを考慮してサイズを計算していっています。
最終的には、アンカーを使用しないCanvasまでたどり着き計算されますので、CanvasのRenderModeがWorldSpaceでアンカーを設定している場合を除いて、使用できるかと思います。

※スケールや回転は考慮しておりません。
※再帰を使用しています。私の環境にはなりますが、30階層でテストしたところ、問題なさそうでしたので、ループやリストを使うよりも再帰を採用しております。
※RectTransformの性質上、GameObjectがActiveになっていないと使用できません。

rectTransform.gameObject.activeInHierarchyで弾く条件を追加しても良いのですが、RectTransform機能全般がActiveでないと使用できないことと、OnRectTransformDimensionsChangeが非Activeでも呼ばれるようでしたので、今回のコードには記載しておりません。

不具合や改善点があればコメントをお願いいたします。

RectTransformExtensions.cs
using System;
using UnityEngine;

public static class RectTransformExtensions
{
    static bool HasAnchorX(RectTransform rectTransform)
    {
        return Mathf.Abs(rectTransform.anchorMin.x - rectTransform.anchorMax.x) > float.Epsilon;
    }

    static bool HasAnchorY(RectTransform rectTransform)
    {
        return Mathf.Abs(rectTransform.anchorMin.y - rectTransform.anchorMax.y) > float.Epsilon;
    }

    public static float CalculateWidth(this RectTransform rectTransform)
    {
        if (!rectTransform)
        {
            throw new ArgumentNullException(nameof(rectTransform));
        }

        if (!HasAnchorX(rectTransform))
        {
            return rectTransform.sizeDelta.x;
        }

        if (rectTransform.parent is not RectTransform parent)
        {
            Debug.LogError($"Failed to calculate RectTransform width. (Invalid RectTransform '{rectTransform.gameObject.name}')");
            return -1;
        }

        return (CalculateWidth(parent) * (rectTransform.anchorMax.x - rectTransform.anchorMin.x)) + rectTransform.sizeDelta.x;
    }

    public static float CalculateHeight(this RectTransform rectTransform)
    {
        if (!rectTransform)
        {
            throw new ArgumentNullException(nameof(rectTransform));
        }

        if (!HasAnchorY(rectTransform))
        {
            return rectTransform.sizeDelta.y;
        }

        if (rectTransform.parent is not RectTransform parent)
        {
            Debug.LogError($"Failed to calculate RectTransform height. (Invalid RectTransform '{rectTransform.gameObject.name}')");
            return -1;
        }

        return (CalculateHeight(parent) * (rectTransform.anchorMax.y - rectTransform.anchorMin.y)) + rectTransform.sizeDelta.y;
    }

    public static Vector2 CalculateSize(this RectTransform rectTransform)
    {
        if (!rectTransform)
        {
            throw new ArgumentNullException(nameof(rectTransform));
        }

        bool widthOk = !HasAnchorX(rectTransform);
        bool heightOk = !HasAnchorY(rectTransform);

        if (widthOk && heightOk)
        {
            return rectTransform.sizeDelta;
        }

        if (rectTransform.parent is not RectTransform parent)
        {
            Debug.LogError($"Failed to calculate RectTransform size. (Invalid RectTransform '{rectTransform.gameObject.name}')");
            return new Vector2(-1, -1);
        }

        if (!widthOk && heightOk)
        {
            return new Vector2(CalculateWidth(parent), rectTransform.sizeDelta.y);
        }

        if (widthOk && !heightOk)
        {
            return new Vector2(rectTransform.sizeDelta.x, CalculateHeight(parent));
        }

        Vector2 size = CalculateSize(parent);
        size.x = (size.x * (rectTransform.anchorMax.x - rectTransform.anchorMin.x)) + rectTransform.sizeDelta.x;
        size.y = (size.y * (rectTransform.anchorMax.y - rectTransform.anchorMin.y)) + rectTransform.sizeDelta.y;
        return size;
    }
}

https://docs.unity3d.com/6000.0/Documentation/ScriptReference/RectTransform.html

Discussion