🔲
RectTransformのサイズを取得する方法(アンカー対応)
ネットの記事にはsizeDeltaやrect.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;
}
}
Discussion