MRが楽しい

MRやVRについて学習したことを書き残す

Unityでスクリーンへのタッチ状態を取得する その3(タップからレイキャストを飛ばして3Dポインターのように動かす)

本日はUnityの小ネタ枠です。
Unityでスクリーンへのタッチ状態を取得する方法です。
今回は更にタッチ状態を参照してタップ座標からレイキャストを飛ばして3Dポインターのように動かしてみます。

Camera.ScreenPointToRay

Camera.ScreenPointToRayメソッドはカメラスクリーンの特定座標からレイキャストを飛ばして3D空間上のヒット箇所を返します。
スクリーンスペースはピクセルで定義されます。画面の左下端は (0,0) 、右上端は (pixelWidth -1,pixelHeight -1) になります。
docs.unity3d.com

タッチ情報から取得できるTouch.positionはピクセルで定義された左下端 (0,0) 、右上端 (pixelWidth -1,pixelHeight -1) のタップ座標が返ります。
このため、Touch.positionの値をそのままCamera.ScreenPointToRayの引数に引き渡すことができます。
docs.unity3d.com
docs.unity3d.com
docs.unity3d.com

void Update()
{
    string message = String.Empty;;
    // ひとつ以上のタッチがある場合
    if (Input.touchCount > 0)
    {
        // 一つ目のタッチを取得
        Touch touch = Input.GetTouch(0);

        // ポイント座標からレイを飛ばす
        Ray ray = Camera.main.ScreenPointToRay(touch.position);
        if (Physics.Raycast(ray, out RaycastHit hit))
        {
            // ヒット情報をもとに処理を行う
            // ...
        }
    }
}

サンプルスクリプト

タップ位置からレイを飛ばしてタップ先の3Dモデルにポインタを表示する以下のサンプルスクリプトを作成しました。
・MultiTouchTest.cs

using UnityEngine;

public class ScreenPointToRayTest : MonoBehaviour
{
    [SerializeField]
    private GameObject pointerPrefabs;
    
    private GameObject spawnedPointer;
    
    void Update()
    {
        // ひとつ以上のタッチがある場合
        if (Input.touchCount > 0)
        {
            // 一つ目のタッチを取得
            Touch touch = Input.GetTouch(0);

            // タッチ開始時のみスポーン処理を行う
            if (touch.phase == TouchPhase.Began)
            {
                // 生成済みのポインターオブジェクトが残っている場合は削除する
                if (spawnedPointer != null) Destroy(spawnedPointer);
            
                // ポイント座標からレイを飛ばしたところにポインターオブジェクトを生成する
                Ray ray = Camera.main.ScreenPointToRay(touch.position);
                // レイの上限距離は5m
                if (Physics.Raycast(ray, out RaycastHit hit, 5f))
                {
                    spawnedPointer = Instantiate(pointerPrefabs, hit.point, Quaternion.identity);
                }
            }
            
            // タッチ移動時はポインターオブジェクトを移動する
            if (touch.phase == TouchPhase.Moved)
            {
                if (spawnedPointer == null) return;
                
                Ray ray = Camera.main.ScreenPointToRay(touch.position);
                if (Physics.Raycast(ray, out RaycastHit hit, 5f))
                {
                    spawnedPointer.transform.position = hit.point;
                }
            }
            
            // タッチ終了時はポインターオブジェクトを削除する
            if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
            {
                if (spawnedPointer != null) Destroy(spawnedPointer);
            }
        }
    }
}

以下の通りタッチした座標をもとに3Dモデルへのポインタを表示することができます。