【Unity】EventSystemsから受け取った座標をRectTransform.localPositionに設定する方法
uGUIのEventSystems(IBeginDragHandler, IDragHandler, IEndDragHandlerなど)から受け取った座標をそのままRectTransform.localPositionに設定すると、Canvasのscaleによってはぶっ飛んだ位置(描画領域外)に移動してしまうことがあります。
EventSystemsから渡される座標はスクリーン座標のため、scaleが影響してぶっ飛んでしまうわけですが、今回はこのぶっ飛び現象を回避するためにスクリーン座標→ローカル座標へ変換する方法を共有します。
CanvasのRender Modeごとにやり方が違う・・・!
CanvasのRender Modeには、以下の3種類があります。詳しい説明は公式を参照。
・Screen Space - Overlay
・Screen Space - Camera
・World Space
上記それぞれで、ローカル座標への変換方法が異なります。
public class EventHandler : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler { private RectTransform rectTransform; private void Awake() { rectTransform = GetComponent<RectTransform>(); } public void OnDrag(PointerEventData eventData) { Vector2 localPosition = GetLocalPosition(eventData.position); rectTransform.localPosition = localPosition; } private Vector2 GetLocalPosition(Vector2 screenPosition) { Vector2 result = Vector2.zero; // TODO: ここで、スクリーン座標→ローカル座標に変換したい return result; } }
Screen Space - Overlayの場合
この場合が一番簡単、以下のコードで変換できます。
private Vector2 GetLocalPosition(Vector2 screenPosition) { return transform.InverseTransformPoint(screenPosition); }
Screen Space - Cameraの場合
こちらの場合は少し面倒。RectTransformUtilityを使って解決します。
private Vector2 GetLocalPosition(Vector2 screenPosition) { Vector2 result = Vector2.zero; RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPosition, Camera.main, out result); return result; }
World Spaceの場合
World Spaceの場合は未確認ですが、調べてみたところ以下の方法で取得できる様子。
private Vector2 GetLocalPosition(Vector2 screenPosition) { Vector2 result = Vector2.zero; RectTransformUtility.ScreenPointToWorldPointInRectangle(rectTransform, screenPosition, Camera.main, out result); return result; }
この現象、いざ発生すると気付きにくいです。普段からちゃんと座標空間を意識してコーディングしないと。。。