RectTransformの重なり判定とは?

UIを作っていると、2つのUI要素が重なっているかどうかを判定したい場面があります。

たとえば以下のようなケースです。

  • ドラッグ&ドロップで、アイテムをスロットに重ねたときの判定
  • UI同士が重なっていないかのチェック
  • 特定のエリアにUI要素が入ったかどうかの検出

こうした判定は、RectTransformの矩形情報を使えば実現できます。

GetWorldCornersで矩形を取得する

RectTransformの矩形情報を取得するには、GetWorldCorners()を使います。

このメソッドは、矩形の四隅のワールド座標をVector3の配列に格納します。

var corners = new Vector3[4];
rectTransform.GetWorldCorners(corners);

取得される4つの角は、以下の順番で格納されます。

  • corners[0] … 左下
  • corners[1] … 左上
  • corners[2] … 右上
  • corners[3] … 右下

返ってくるのはワールド座標です。

ローカル座標ではないので、親のTransformによるスケールや回転の影響を受けた後の値が返ります。

Rect.Overlapsで重なりを判定する

2つのRectTransformが重なっているかどうかは、Rect.Overlaps()で判定できます。

Rect.Overlaps()は、2つのRectが重なっていればtrueを返すメソッドです。

GetWorldCornersからRectを作る

GetWorldCorners()で取得した座標からRectを作り、Overlaps()に渡します。

以下のようなヘルパーメソッドを用意すると便利です。

using UnityEngine;

public static class RectTransformOverlapHelper
{
    private static readonly Vector3[] _cornersA = new Vector3[4];
    private static readonly Vector3[] _cornersB = new Vector3[4];

    /// <summary>
    /// 2つのRectTransformが重なっているかどうかを判定する
    /// </summary>
    public static bool Overlaps(RectTransform rectA, RectTransform rectB)
    {
        rectA.GetWorldCorners(_cornersA);
        rectB.GetWorldCorners(_cornersB);

        var rectAWorld = ToRect(_cornersA);
        var rectBWorld = ToRect(_cornersB);

        return rectAWorld.Overlaps(rectBWorld);
    }

    /// <summary>
    /// GetWorldCornersの結果からRectを生成する
    /// </summary>
    private static Rect ToRect(Vector3[] corners)
    {
        // corners[0]が左下、corners[2]が右上
        var min = corners[0];
        var max = corners[2];
        return new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
    }
}

使い方はシンプル。

using UnityEngine;

public class OverlapChecker : MonoBehaviour
{
    [SerializeField] private RectTransform _targetA;
    [SerializeField] private RectTransform _targetB;

    private void Update()
    {
        if (RectTransformOverlapHelper.Overlaps(_targetA, _targetB))
        {
            Debug.Log("重なっています");
        }
    }
}

Canvas Render Modeごとの挙動

GetWorldCorners()はワールド座標を返すため、CanvasのRender Modeによって値のスケールが変わります。

Screen Space - Overlayの場合、ワールド座標がスクリーン座標に近い値になるので直感的に扱いやすいです。

Screen Space - CameraやWorld Spaceの場合は、カメラとの距離や配置によってワールド座標の値が変わります。

ただし、同じCanvas内のUI同士であれば座標系は同じなので、Overlaps()の比較は問題なく動きます。

異なるCanvas間で比較するときは座標系が異なるため、注意してください。

RectTransformUtility.RectangleContainsScreenPointとの使い分け

似たような判定として、RectTransformUtility.RectangleContainsScreenPoint()があります。

こちらは「ある点がRectTransformの矩形内にあるか」を判定するメソッドです。

// マウス座標がRectTransformの範囲内にあるかどうか
bool isInside = RectTransformUtility.RectangleContainsScreenPoint(
    rectTransform,
    Input.mousePosition,
    camera
);

用途が違うので、以下のように使い分けます。

  • 矩形同士の重なり → GetWorldCorners() + Rect.Overlaps()
  • 点が矩形内にあるか → RectTransformUtility.RectangleContainsScreenPoint()

マウスやタッチの座標とUIの当たり判定ならRectangleContainsScreenPoint()、UI同士の重なりならRect.Overlaps()です。

まとめ

RectTransform同士の重なり判定は、GetWorldCorners()で四隅を取得してRectに変換し、Rect.Overlaps()で比較するのが手軽です。

同じCanvas内であればRender Modeを問わず動くので、ドラッグ&ドロップや重複チェックなど色々な場面で使えます。

「点がUI内にあるか」ならRectangleContainsScreenPoint()など、目的に応じて使い分けてみてください。