【Unity】UGUIのRectTransformを100倍快適に扱うためのユーティリティを作った
概要
この記事はCluster,Inc. Advent Calendar 2017の14日目の記事です。
こんな感じのWindowのようなものを作っていたのですが、 RectTransformの具体的なサイズが知りたい!
親Transformの左端、右端から、相対的に位置を指定したい!
など、RectTransformに思うところが多かったので、RectTransformを便利に扱うためのユーティリティを作ってみました。
コード
/** MIT Lisence Copyright (c) 2017 Cluster,Inc. This software is released under the MIT License. http://opensource.org/licenses/mit-license.php */ public enum Corner { LeftTop, RightTop, LeftBottom, RightBottom, }
/** MIT Lisence Copyright (c) 2017 Cluster,Inc. This software is released under the MIT License. http://opensource.org/licenses/mit-license.php */ using System; using System.Linq; using UnityEngine; public static class RectTransformUtil { public static Bounds GetWorldBounds(this RectTransform rectTransform) { var corners = new Vector3[4]; rectTransform.GetWorldCorners(corners); var center = corners.Aggregate(Vector3.zero, (current, corner) => current + corner) / corners.Length; var size = new Vector3( corners.Max(corner => corner.x) - corners.Min(corner => corner.x), corners.Max(corner => corner.y) - corners.Min(corner => corner.y), 1); return new Bounds(center, size); } public static Vector2 GetRelativePosition(this RectTransform rectTransform, Corner corner) { var parentBounds = rectTransform.parent.GetComponent<RectTransform>().GetWorldBounds(); var bounds = rectTransform.GetWorldBounds(); var pos = Vector2.zero; switch (corner) { case Corner.LeftBottom: case Corner.RightBottom: pos.y = bounds.min.y - parentBounds.min.y; break; case Corner.LeftTop: case Corner.RightTop: pos.y = parentBounds.max.y - bounds.max.y; break; default: throw new ArgumentOutOfRangeException("corner", corner, null); } switch (corner) { case Corner.LeftTop: case Corner.LeftBottom: pos.x = bounds.min.x - parentBounds.min.x; break; case Corner.RightTop: case Corner.RightBottom: pos.x = parentBounds.max.x - bounds.max.x; break; default: throw new ArgumentOutOfRangeException("corner", corner, null); } return pos; } public static void SetRelativePosition(this RectTransform rectTransform, Vector2 pos, Corner corner) { var parentBounds = rectTransform.parent.GetComponent<RectTransform>().GetWorldBounds(); var bounds = rectTransform.GetWorldBounds(); var anchoredPosition = rectTransform.position; switch (corner) { case Corner.LeftBottom: case Corner.RightBottom: anchoredPosition.y += parentBounds.min.y - bounds.min.y + pos.y; break; case Corner.LeftTop: case Corner.RightTop: anchoredPosition.y += parentBounds.max.y - bounds.max.y - pos.y; break; default: throw new ArgumentOutOfRangeException("corner", corner, null); } switch (corner) { case Corner.LeftTop: case Corner.LeftBottom: anchoredPosition.x += parentBounds.min.x - bounds.min.x + pos.x; break; case Corner.RightTop: case Corner.RightBottom: anchoredPosition.x += parentBounds.max.x - bounds.max.x - pos.x; break; default: throw new ArgumentOutOfRangeException("corner", corner, null); } rectTransform.position = anchoredPosition; } }
つかいかた
rectTransform.GetWorldBounds()
のように、拡張メソッドとして使えるようになっています。
GetWorldBounds
Boundsとして、ワールド空間でのRectTransformの位置やサイズ、各点の位置などが取得できます。
GetRelativePosition / SetRelativePosition
右側/左側/上側/下側が、親の端からどのくらい離れているかを相対位置で取得/設定できます。 親のオブジェクトをはみ出さないように位置を制限する、といった挙動の実装に便利です。
微妙なところ
パフォーマンス
メソッドが呼ばれるたびにGetComponent<RectTransform>()
を2回も呼んでいるので、それなりにパフォーマンスが悪いです。
適宜、必要に応じてキャッシュする仕組みなどを作ると良いと思います。
親がRectTransformじゃない場合の挙動は?
わからん 😱
親にスケールがかかっていた場合の挙動は?
わからん😇😇😇 なんかもっといいアプローチがあったら教えてください