vaguely

和歌山に戻りました。ふらふらと色々なものに手を出す毎日。

【Unity】画面サイズと異なる大きさの RenderTexture でクリック位置の WorldPoint が取りたい話

はじめに

Unity では基本的に、 uGUI などの GUI はカメラを使って表示する 3D より手前に表示されます。

ただ、例えばポップアップウインドウのような表示がしたいなど、 GUI より手前に 3D を表示したい場合もあります。

それを解決する方法として RenderTexture を使う方法があります(正しい方法かどうかは不明)。

が、それを使って表示した内容に対してクリックし、その位置を 3D(WorldPoint) に変換しようとしたらうまくいかなかったのでメモです。

準備

  • 画面サイズは 1920 * 1200 とします。
  • RenderTexture のサイズは 1100 * 600 とします。
  • Canvas を下記の内容で準備します。
    1. Canvas Scaler の UI Scale Mode を Scale with Screen Size にし、Reference Resolution を 1920 * 1200 とします。
    2. Raw Image を追加し、サイズを 1100 * 600 に設定します。
    3. 2.の Anchors を X = 0, Y = 1 に設定します。
    4. RenderTexture のサイズを 1100 * 600 で作成し、2.の Texture にアタッチします。
  • 今回は特定の位置に置いた Plane 上のクリック位置を取ります。
    1. カメラの正面に Plane を適当なサイズに置きます(カメラから Z 方向に 10 離す)。
    2. クリック位置が分かりやすいように、適当なサイズの Cube を置きます。
      (初期位置はどこでも良いです)

クリック位置を取ってみる

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MainController : MonoBehaviour {
    public Camera MainCamera;
    // RenderTexture
    public RectTransform TextureTransform;
    // クリック位置上に置く Cube.
    public Transform ClickTransform;
    // Plane
    public Transform PlaneTransform;
    
    private Vector2 texturePosition;
    private Vector2 screenScale;
    private Vector3 screenInputPosition = Vector3.zero;
    
    private void Start () {
        // 実行中の画面サイズを 1920 * 1200 にスケーリング.
        screenScale = new Vector2(
            Screen.width / 1920f,
            Screen.height / 1200f);

        // 今回は RenderTexture の位置が動的に変わらないものとする.
        texturePosition = TextureTransform.position;
    }
    private void Update () {
        if(Input.GetMouseButtonUp(0)) {
            // クリック位置が RenderTexture でのどの位置にあるかを算出.
            // 座標値は画面サイズ 1920 * 1200 での値を使う必要がある.
            screenInputPosition.x = (Input.mousePosition.x - texturePosition.x) / screenScale.x;
            screenInputPosition.y = ((Input.mousePosition.y - texturePosition.y) / screenScale.y) +
                TextureTransform.sizeDelta.y;

            // クリック位置を取りたい Plane と カメラの Z 軸の差分を入れることで、 Plane 上の座標値に変換できる.
            screenInputPosition.z = PlaneTransform.localPosition.z - MainCamera.transform.localPosition.z;
            ClickTransform.position = MainCamera.ScreenToWorldPoint(screenInputPosition);
        }
    }
}

注意点としては、 2D の Y 座標は画面上部が 0 、3D は下が 0 になる、というところと、 ScreenToWorldPoint に渡す座標は画面サイズが 1920 * 1200 であったときのものに合わせる必要がある、ということです。