【Unity】ボタンタッチとそれ以外の場所のタッチを識別する


こんにちは！エンジニアをしておりますアツシといいます！

ボタンを押した際はボタンのイベント、それ以外の場所を押した際はそのイベント、と識別したい場合の方法として実際にいくつか使用した例を記録しようと思います。

今回のテーマを挙げた理由として、画面タッチした際に指定の関数を呼びたい時、個人的にはInput.GetMouseButtonDown(0)を使用することが多かったんですが、その際UI上にボタンを配置してボタンを押すと「ボタンのイベント」の前に「画面タッチのイベント」が呼ばれてしまう為、任意の挙動にならない事があった為です。

何かご意見・ご指摘等ございましたら下記にご連絡ください！
[<font color="LightSkyBlue">Twitterアカウント</font>](https://twitter.com/atsh_mtsk0122)
[<font color="LightSkyBlue">Instagramアカウント</font>](https://www.instagram.com/whisroll/)

####◎ButtonはOnClick 、画面タッチ は Input.GetMouseButtonDown(0)で実装した場合↓
![VerticalFailure.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514335/fda2269a-f21a-12c8-8946-0e312655c7d4.gif)



####◎識別した場合↓
![Vertical.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514335/95d280ac-bc4f-7ad9-0677-dcf03bb4d041.gif)

##動作環境
- Unity2019.3.7f1
- Windows10

##対処法
使用したのは以下の方法です。

1. 画面サイズのImageにEventTriggerを追加して識別する方法
2. 座標で識別する方法
3. Rayを飛ばして判定する方法

順に説明していきます。


##①画面サイズのImageにEventTriggerを追加して識別する方法
→インスペクターで設定できますが、実装環境では動的に生成する必要があった為コードで管理しました。
参考：[【Unity/uGUI】ButtonをClick押した瞬間に実行するにはEventTrigger/PointerDown【VR】
](https://bibinbaleo.hatenablog.com/entry/2020/01/23/122943)

画面いっぱいのサイズのImageにEventTriggerを追加しその上にボタン等を配置すれば、
ボタンを押した際はボタンのイベント、そこ以外の画面を押した際は画面のイベントが起こります。
Imageは不透明度0にすれば見えません。RaycastTargetのチェックを外すと利かなくなるので注意です。

この方法が一番スマートな気がします。
ボタンの方もEventTriggerのPointerDownで管理すると押した瞬間に関数が呼ばれるのでいいのでは、と思います。
（※ButtonのonClickは、ボタンを押した瞬間ではなく、ボタンを押して離した時に指（マウス）がボタンの上に乗っていれば指定の関数が呼ばれる）

※EventTriggerを使用可能にするには下記をusingします。


```C#:
using UnityEngine.EventSystems;
```


```C#:EventTrigger
    void Start()
    {
        //EventTriggerコンポーネントを取得
        EventTrigger eventTrigger = gameObject.GetComponent<EventTrigger>();

        //イベントの設定に入る
        EventTrigger.Entry entry = new EventTrigger.Entry();

        //PointerDown(押した瞬間に実行する)イベントタイプを設定
        entry.eventID = EventTriggerType.PointerDown;

        //関数を設定
        entry.callback.AddListener((x) =>
        {
            Trigger();
        });

        //イベントの設定をEventTriggerに反映
        eventTrigger.triggers.Add(entry);
    }

    public void Trigger()
    {
        Debug.Log("Trigger");
    }
```

##②座標で識別する方法
言葉通り、押した座標位置で、ボタンかそれ以外かを識別する方法です。
①の方法だと、画面が固定されていなければ問題ないのですが、ScrollRect等を使用している場合画面スクロールができなくなってしまいます。そういった場合で、且つ、ボタンのエリアと識別したい画面のエリアがざっくり分かれている時、この方法が手っ取り早いかと思います。

##③Rayを飛ばして判定する方法
タッチした場所にボタンがあるかないかを判定する方法です。

下記リンク先参考にしました。
[ゲーム画面クリックと、画面上のボタンクリックをきっちり分けて処理をする方法](https://unity-senpai.hatenablog.com/entry/2019/03/27/012403)

上記リンクと異なる点はRaycastの位置です。
リンク先のコードではCanvas の RenderMode を "World Space"にし、Cameraコンポーネントの Size を調節する必要がありました。
Input.mousePositionにすれば、Canvas > RenderMode が "ScreenSpace - Overlay"の場合でも正しい判定がされます。


```C#:RaycastTest
    void Start()
    {
        //ボタンクリックしたらボタンイベント関数を呼ぶ
        btn.onClick.AddListener(BtnEvent);
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {

            // Rayを発射
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit2D hit2d = Physics2D.Raycast((Vector2)Input.mousePosition, (Vector2)ray.direction);

            // Rayで何もヒットしなかったら画面タッチイベント関数を呼ぶ
            if (!hit2d)
            {
                ScreenEvent();
            }

            ////Sceneビュー確認用
            //Debug.DrawRay(ray.origin, ray.direction);
            //Debug.DrawRay(Input.mousePosition,ray.direction);
        }
    }

    //画面タッチ用イベント
    void ScreenEvent()
    {
        txt.text = "Screen Pushed";
    }

    //ボタンクリック用イベント
    void BtnEvent()
    {
        txt.text = "Button Pushed";
    }
```