【Unity】UIと画面クリック判定を区別、UIの処理無効にする

当サイトで紹介する商品・サービス等の外部リンクは、アフィリエイト広告を含む場合があります。
スポンサーリンク
本記事を読むと以下の実行ができます

UI上でクリックしても、同時に画面クリックを判定しない。

「Input.GetMouseButton」は、マウスクリックを行うと処理が行われるメソッドです。
例えば、マウスを左クリックすると、変数iの値を増加させる(インクリメント)。
このような処理を行うには、次のようにプログラムします。

if (Input.GetMouseButtonDown(0))
{
    i++;
}

一方で、UIボタンを押すと、変数iの値を減少させる(デクリメント)には、メソッドを使用します。

public void downScore()
{
    i--;
}

UIボタンは左クリックするので、2つのプログラムは同時に処理されてしまい、
変数iの値を増加・減少が同時に起こってしまいます。

「Input.GetMouseButtonDown()」は画面押下で、会話文を進めるときに使用して、
UIボタンは、メニュー画面を選択で使用するように、2つとも使用されることはよくあります。

Input.GetMouseButtonを解説しています。

本記事では、画面とUIボタンのクリック判定を別々にするコードを紹介します。

本記事は次の人におすすめ
  • UIボタンをクリックしても画面クリックが反応しない方法を知りたい。
  • Input.GetMouseButtonとUIを使い分けたい。
スポンサーリンク

EventSystem.current.IsPointerOverGameObject()

「EventSystem.current.IsPointerOverGameObject()」は、ゲーム内のUI(ユーザーインターフェース)にポインタ(マウスやタッチ)が重なっているかどうかを確認します。

UIボタンと画面クリックの見極めは、UIとマウスポイントが重なっていると、処理を実行しないようにします。

要するに、UI上をクリックしていると、処理をキャンセルするコードです。
ポインタ・タッチはスマホやタブレットでタッチスクリーンを使う場合はコードが少し異なります。

本メソッドを使うには、名前空間に「using UnityEngine.EventSystems;」を追加しなければエラーが発生するので、注意してください。

「EventSystem.current.IsPointerOverGameObject()」は「EventSystem.current」と「IsPointerOverGameObject()」に分けることができます。

EventSystem.current

EventSystem.current は、Unityのイベントシステムの現在のインスタンスを表します。
UIや入力などのイベントを処理するための仕組みがあり、その中核となるのがイベントシステムです。

情報をキャプチャし、適切なオブジェクトにそれらの情報を伝達します。
これにより、ユーザーの操作に応じてゲーム内の動作を制御することができます。

IsPointerOverGameObject()

現在のポインタがUI要素上にあるかどうかを判定するメソッドです。
もしポインタがUI要素上にある場合、このメソッドはtrueを返します。
UI要素全般なので、ボタン、テキストフィールド、パネルなども対象です。

指定されたポインター(通常はマウスカーソルやタッチ)が現在のフレームでどのGameObjectの上にあるかを判定します。
主に、UI要素上にポインターがあるかどうかをチェックするために使用されます。

if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)    //画面に触れた瞬間
{
    if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId)) return;    //スマホ入力
}

引数なしの「IsPointerOverGameObject()」は、マウスの左クリック(pointerId = -1)を調べます。タッチの場合は、どの指かを表す「fingerId」を引数に渡します。
また、タッチがないフレームで「Input.GetTouch(0)」を呼ぶとエラーになるので、先に「Input.touchCount > 0」で指が触れているかを確かめます。公式リファレンスでも、タッチでは触れた瞬間(TouchPhase.Began)に調べる使い方が紹介されています。

スマホ向けのゲームの場合はマウス入力では正常に処理されません。
UI上の入力しても画面クリックが判定されてしまうので、コードの加筆修正が必要です。

if(EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))  //スマホ入力

処理の一例

ここで、「EventSystem.current.IsPointerOverGameObject()」を使った簡単な処理を紹介します。

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

public class NewBehaviourScript : MonoBehaviour
{
    void Update()
    {
        // マウスカーソルがUI上にあるか確認します。
        if (EventSystem.current.IsPointerOverGameObject())
        {
            Debug.Log("The mouse cursor is over a UI element.");
        }
    }
}

マウスカーソルがUI上にある場合はconsoleウィンドウで処理が実行されます。

画面上に、UIテキストとUIボタンを追加して、スクリプトをアタッチして、再生ボタンを押します。

UIボタンを押しても画面が反応せずに、処理を実行する

「EventSystem.current.IsPointerOverGameObject()」を使用して、
目標であるUIボタンをクリックしても画面クリックが反応しない処理をします。

ソースコード

using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;   //追加(UI上の動作稼働)

public class count : MonoBehaviour
{
    public TextMeshProUGUI countText; // UIテキストオブジェクト
    int i = 0; // カウンターの初期値

    void Start()
    {

        UpdateCountText();
    }

    void Update()
    {
        //ボタンを押したときは画面クリック無効
        if (EventSystem.current.IsPointerOverGameObject()) return;

        if (Input.GetMouseButtonDown(0)) // マウスの左ボタンが押された場合
        {
            i++; // iをインクリメント
            UpdateCountText();
        }
    }

    // UIボタンが押されたときに呼び出される関数
    public void DecrementCount()
    {
        i--; // iをデクリメント
        UpdateCountText();
    }


    // カウントを表示するテキストを更新する関数
    void UpdateCountText()
    {
        countText.text = i.ToString();
        Debug.Log("Count:" + i);
    }
}

解説

・名前空間

「using TMPro」は、TextMeshProUGUIクラスを使用するために必要な名前空間です。
TextMeshProUGUIは、UIテキストを操作する際に必要なクラスが含まれています。

TextMeshProUGUIの使い方を紹介しています。
  • フィールド(メンバ変数)

「countText」:UI上でカウントを表示するテキストオブジェクトを参照するための変数です。
この変数は、TextMeshProUGUI型であり、UI上のテキストを更新する際に使用されます。
「i」:カウンターの値を保持するための変数です。
ユーザーのアクションによりカウントの値が変化します。

  • Startメソッド

UpdateCountTextメソッドが呼び出され、初期状態でUI上にカウントが表示されます。

  • Updateメソッド

最初に、マウスカーソルがUIの上にあるかを確認し、UIの上にある場合はreturnで処理を終わらせて画面クリックを無効にします。
UIの上にない状態で左クリックされた場合は、カウンターをインクリメントし、UpdateCountTextメソッドを呼び出してUI上の表示を更新します。

  • DecrementCountメソッド

UI上のボタンが押されたときに呼び出される公開されたメソッドです。
カウンターの値をデクリメントし、UpdateCountTextメソッドを呼び出してUI上の表示を更新します。

  • UpdateCountTextメソッド

UI上のテキストを更新するためのメソッドです。
現在のカウンターの値を文字列に変換し、UIテキストオブジェクトのtextプロパティに代入します。
また、デバッグログも出力され、カウンターの値が更新されたことが確認できます。

変数を文字列に変える方法を紹介しています。

実演

Hierarchyウィンドウにスクリプトをアタッチして、数値を示すテキストを追加します。
また、UIボタンのOnClickにDecrementCount()メソッドを付加します。

再生ボタンを押すと、冒頭のように画面クリックができるようになります。

ボタン・トグル・スライダーの上だけクリックを無視する

背景の画像やセリフの枠のように、画面いっぱいのUIを置いていると、IsPointerOverGameObject()はどこをクリックしてもtrueになります。その結果、画面クリックの処理が動かなくなります。

そのUIでもクリックを受け取りたい(Raycast Targetを外せない)ときは、クリックした位置の一番手前にあるUIを調べ、それが操作できる部品かどうかで判定します。

readonly List<RaycastResult> results = new List<RaycastResult>();

// クリック位置の一番手前が、ボタン・トグル・スライダー・スクロールなどか
public bool IsPointerOverInteractiveUI()
{
    if (EventSystem.current == null) return false;

    var data = new PointerEventData(EventSystem.current) { position = Input.mousePosition };
    results.Clear();
    EventSystem.current.RaycastAll(data, results);   // 手前のUIから順に並ぶ
    if (results.Count == 0) return false;

    GameObject top = results[0].gameObject;
    return top.GetComponentInParent<Selectable>() != null
        || top.GetComponentInParent<ScrollRect>() != null;
}

このコードは、名前空間にSystem.Collections.Generic・UnityEngine.EventSystems・UnityEngine.UIが必要です。Updateの中で、IsPointerOverGameObject()の代わりに呼びます。

Selectableは、Button・Toggle・Slider・Scrollbar・InputFieldなどの「操作できるUI」の元になっているクラスです。これを持っているかどうかで、背景やテキストの上と区別できます。

著者の体験談
著者の体験談

自作のノベルゲームでは、最初はボタンにだけ「押されたフラグ」を立てる仕組みにしていました。ところが、オート再生のトグルや設定のスライダーを操作すると本文まで進んでしまったため、この判定に置き換えています。

うまく判定されないときの確認ポイント

UIボタンを押しても画面クリックが反応してしまう、または逆に、どこをクリックしても反応しない場合は、次の点を確認してください。

  • シーンに「EventSystem」があるか:EventSystemがないと「EventSystem.current」が空(null)になり、エラーが発生します。UIを追加すると自動で作られますが、消してしまった場合はHierarchyの「+」→「UI」→「Event System」で追加します。
  • UIの「Raycast Target」:テキストや背景の画像も、Raycast Targetにチェックが入っているとUIとして判定されます。クリックを止めたくないUIは、チェックを外します。
  • カメラの「Physics Raycaster」:カメラにPhysics Raycasterを付けていると、Event Maskに含まれるレイヤーの3Dオブジェクトの上でもtrueになります。UIだけを判定したいときは、Event Maskを見直します。
  • クリックした瞬間に調べているか:「Input.GetMouseButtonDown(0)」と同じUpdateの中で調べます。

新しいInput Systemを使っているとき

新しいバージョンのUnityで作ったプロジェクトでは、「Input.GetMouseButtonDown」を使うとエラーになる場合があります。その場合は、Edit > Project Settings > Player > Other Settingsの「Active Input Handling」を「Both」に変えると、本記事のコードのまま動きます。

新しいInput System(UnityEngine.InputSystem)で書く場合も、「IsPointerOverGameObject()」はそのまま使えます。

using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;    //追加(新しいInput System)

public class ClickCheck : MonoBehaviour
{
    void Update()
    {
        if (Mouse.current == null) return;    //マウスがないとき

        if (Mouse.current.leftButton.wasPressedThisFrame)    //左クリックした瞬間
        {
            if (EventSystem.current.IsPointerOverGameObject()) return;    //UIの上なら何もしない
            Debug.Log("画面をクリックしました");
        }
    }
}

ただし、Input Actionsの「performed」などのコールバックの中で「IsPointerOverGameObject()」を呼ぶと、警告が出て、1フレーム前の状態が返ります。UIの状態は、入力の処理のあとに更新されるためです。公式のマニュアルでも、Updateの中で調べる方法が一番かんたんだと案内されています。

まとめ

今回は、ボタンクリックで画面クリック判定しない方法を紹介しました。

UI上の処理をTrueにするには「EventSystem.current.IsPointerOverGameObject()」を使います。ただし、名前空間に「using UnityEngine.EventSystems;」を入れないとエラーが発生するので、注意が必要です。

スマホやタブレットでのゲームを開発する場合は、「EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId)」とする必要があります。指が触れているか(Input.touchCount > 0)を先に確かめてください。

うまく判定されないときは、EventSystem・Raycast Target・Input Systemの設定を確認しましょう。

タイトルとURLをコピーしました