【Unity】【uGUI】RectTransformUtilityでスクリーン座標をUIのローカル座標やワールド座標に変換する

RectTransformUtilityを使ってスクリーン座標をRectTransform上ののローカル座標やワールド座標に変換する方法です。

Unity2018.3.1

スクリーン座標をRectTransform上のUIのローカル座標に変換する

スクリーン座標をUIのローカル座標に変換するにはRectTransformUtility.ScreenPointToLocalPointInRectangleを使います。
これは、スクリーン座標を引数に与えたRectTransform平面上の点に変換します。

using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(Graphic))]
public class Example : MonoBehaviour
{
    [SerializeField]
    private GameObject _obj;

    void Update()
    {
        if (Input.GetMouseButtonDown(0)) {
            // クリックしたスクリーン座標
            var screenPoint = Input.mousePosition;
            
            // Canvasにセットされているカメラを取得
            var graphic = GetComponent<Graphic>();
            var camera = graphic.canvas.worldCamera;

            // Overlayの場合はScreenPointToLocalPointInRectangleにnullを渡さないといけないので書き換える
            if (graphic.canvas.renderMode == RenderMode.ScreenSpaceOverlay) {
                camera = null;
            } 

            // クリック位置に対応するRectTransformのlocalPositionを計算する
            var localPoint = Vector2.zero;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(graphic.rectTransform, screenPoint, camera, out localPoint);

            // ローカル座標にインスタンス生成
            var instance = Instantiate(_obj, transform);
            instance.GetComponent<RectTransform>().localPosition = localPoint;
            instance.SetActive(true);
        }
    }
}

引数にCameraがありますが、Render ModeがオーバーレイのときにはCameraをnullにしないといけないので注意が必要です。

結果はこんな感じになります。

f:id:halya_11:20190206233944g:plain

Render ModeをWorldにしてPerspectiveなCameraを使えばこんなことも。

f:id:halya_11:20190206234337g:plain

スクリーン座標をRectTransform平面上のワールド座標に変換する

スクリーン座標をワールド座標に変換するにはScreenPointToWorldPointInRectangleを使います。

using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(Graphic))]
public class Example : MonoBehaviour
{
    [SerializeField]
    private GameObject _obj;

    void Update()
    {
        if (Input.GetMouseButtonDown(0)) {
            // クリックしたスクリーン座標
            var screenPoint = Input.mousePosition;
            
            // Canvasにセットされているカメラを取得
            var graphic = GetComponent<Graphic>();
            var camera = graphic.canvas.worldCamera;

            // Overlayの場合はScreenPointToWorldPointInRectangleにnullを渡さないといけないので書き換える
            if (graphic.canvas.renderMode == RenderMode.ScreenSpaceOverlay) {
                camera = null;
            } 

            // クリック位置に対応するRectTransform上のワールド座標を計算する
            var worldPoint = Vector3.zero;
            RectTransformUtility.ScreenPointToWorldPointInRectangle(graphic.rectTransform, screenPoint, camera, out worldPoint);

            // ワールド座標にインスタンス生成
            var instance = Instantiate(_obj);
            instance.transform.position = worldPoint;
            instance.SetActive(true);
        }
    }
}

こちらもRender ModeがオーバーレイのときにはCameraをnullにしないといけないので注意が必要です。

結果はこのようになります。

f:id:halya_11:20190206234650g:plain

ワールド座標をスクリーン座標に変換

ワールド座標をスクリーン座標に変換するにはRectTransformUtility.WorldToScreenPointを使います。

using UnityEngine;
using UnityEngine.UI;

[RequireComponent(typeof(Graphic))]
public class Example : MonoBehaviour
{
    [SerializeField]
    private Transform _transform;
    [SerializeField]
    private Camera _camera;

    void Update()
    {
        var camera = _camera;

        // Cameraにnullを入れるとOverlay用のCameraのスクリーン座標が得られる
        var screenPos = RectTransformUtility.WorldToScreenPoint(camera, _transform.position);

        Debug.Log(screenPos);
    }
}

どのカメラにとってのスクリーン座標に変換するのかを定義しなければならないのでCameraを渡すわけですが、
Overlay用のCameraにおけるスクリーン座標に変換したい場合にはCameraにnullを渡します。

参考

docs.unity3d.com

docs.unity3d.com