Help us understand the problem. What is going on with this article?

Unityで画面をタップした時のRayの撃ち方を考えてみた

More than 5 years have passed since last update.

某所で、Unityのスクリプトを読んだときになんか違うんじゃないかと思ったのでメモ。

そのスクリプトは、タッチイベントを受け取りたい各GameObjectがそれぞれUpdate()内でRayを撃ってたわけだけど、それだと、オブジェクトが増えれば増えるほど動作が重くなりそうな気がした(気にならない程度かもしれないが、そもそも設計方針が変だと思われる)。

誰がRayを撃つべきか?

なんでもいいが、Rayを撃つのは1つのオブジェクトだけにしたい。複数のオブジェクトが自分勝手にRayを撃つのはダメすぎる。
Main Camera、あるいは、空のオブジェクトを作ってそこで撃てばいいだろう。

実装

実装方針を考えてみる。
1.Rayを撃つのは1つのオブジェクトだけ
2.タッチイベントを受け取りたいオブジェクトは、特定のスクリプトをコンポーネントとして持たせれば良さそう

実際のコードを考えていく。

タッチイベントの取得側

まず、2のほう。
タッチイベントなので、「タッチした」「タッチした指を離した」の2種類のイベントを受け取れればいいか…?
となると、コードはこんな感じ?

TapBehaviour.cs
using UnityEngine;
using System.Collections;

public abstract class TapBehaviour : MonoBehaviour {
    // タッチしたときに呼ばれる。
    public virtual void TapDown(ref RaycastHit hit){}
    // タッチを離したときに呼ばれる。
    public virtual void TapUp(ref RaycastHit hit){}
}

上記のクラスを継承したクラスを各オブジェクトの仕様にあわせて実装すれば良い。

Rayを受け取りタッチイベントを呼び出す側

1のほう。
こっちのクラスはMain Cameraにでもつけとくことにする(空のゲームオブジェクトでも可)。
で、タッチを感知したら、Rayを撃って、当たったオブジェクトがTapBehaviourの子クラスを保持していれば、
TapDownかTapUpメソッドを呼び出すことにする。

TouchHandler.cs
using UnityEngine;
using System.Collections;

public class TouchHandler : MonoBehaviour {
    public float distance = 10; // Rayの届く距離

    void Update () {
        // タッチされたとき
        if(Input.GetMouseButtonDown(0)){
            // メインカメラからクリックしたポジションに向かってRayを撃つ。
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit = new RaycastHit();

            if (Physics.Raycast(ray, out hit, distance)) {
                GameObject selectedGameObject = hit.collider.gameObject;
                TapBehaviour target = selectedGameObject.GetComponent(typeof(TapBehaviour)) as TapBehaviour;
                if(target != null){
                    target.TapDown(ref hit);
                }
            }

        // 指を離したとき
        }else if(Input.GetMouseButtonUp(0)){
            // メインカメラからクリックしたポジションに向かってRayを撃つ。
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit = new RaycastHit();

            if (Physics.Raycast(ray, out hit, distance)) {
                GameObject selectedGameObject = hit.collider.gameObject;
                TapBehaviour target = selectedGameObject.GetComponent(typeof(TapBehaviour)) as TapBehaviour;
                if(target != null){
                    target.TapUp(ref hit);
                }
            }

        }
    }
}

上のコードは、Input.GetMouseButtonDown(Up)を使ったが、マルチタップに対応したい場合は、Input.touchesを使う方法もあるだろう。

void Update(){
    foreach (Touch touch in Input.touches) {
        if(touch.phase == TouchPhase.Began){
            // メインカメラからクリックしたポジションに向かってRayを撃つ。
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit = new RaycastHit();

            if (Physics.Raycast(ray, out hit, distance)) {
                GameObject selectedGameObject = hit.collider.gameObject;
                TapBehaviour target = selectedGameObject.GetComponent(typeof(TapBehaviour)) as TapBehaviour;
                if(target != null){
                    target.TapDown(ref hit);
                }
            }

        }else if(TouchPhase.Ended){
            // メインカメラからクリックしたポジションに向かってRayを撃つ。
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit = new RaycastHit();

            if (Physics.Raycast(ray, out hit, distance)) {
                GameObject selectedGameObject = hit.collider.gameObject;
                TapBehaviour target = selectedGameObject.GetComponent(typeof(TapBehaviour)) as TapBehaviour;
                if(target != null){
                    target.TapUp(ref hit);
                }
            }

        }
    }
}

(いちおう書いてみた。動作テストはしていない。)

タッチイベントの受け取り方

タッチイベントを受け取りたいオブジェクトでは、次のようにTapBehaviourの子クラスを書いてコンポーネントとして持たせておく。

KurekureTouch.cs
public class KurekureTouch : TapBehaviour {
    public override void TapDown (ref RaycastHit hit){
        // タップされたときの処理
    }

    public override void TapUp (ref RaycastHit hit){
        // タップを離したときの処理
    }

    void Update(){
        // タップされてるかをフラグで管理したりして適当に更新する。
    }
}

クラス名とか適当すぎる気はしてます、はい…

JunSuzukiJapan
基本、未来の自分あてに備忘録として書いてます。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away