以前作ったマウス入力管理クラスの追加機能として作成した。UIかどうかの判定はもっといい方法がありそうだが、一旦うまくいったのでメモとして記事にしておく。
マウス入力管理クラスに関しては以下の記事で解説している。
コード
完成したコードがこちら。
public bool IsPointerOnUI(Vector2 screenPosition)
{
if (EventSystem.current == null) { return false; }
PointerEventData eventDataCurrent = new PointerEventData(EventSystem.current);
eventDataCurrent.position = screenPosition;
EventSystem.current.RaycastAll(eventDataCurrent, raycastResults);
if(raycastResults.Count == 0){ return false; }
bool result = raycastResults[0].gameObject.layer == 5;
raycastResults.Clear();
return result;
}
メソッドの外で、リストを作っておく必要がある。
private List<RaycastResult> raycastResults = new List<RaycastResult>();
これは、以下のページのコードにUIかどうかを判定する内容を加えたものである。
参考記事と同様に、引数にはマウスのスクリーン座標を渡す。以下のようにメソッドを呼べば、現在のマウスのスクリーン座標上にUIがあるかどうかが得られる。
IsPointerOnUGUI(Input.mousePosition);
また、このコードはUIのレイヤーがレイヤー番号が5の「UI」であることを前提にしている。そのため、レイヤーが変更されているUIが判定をすり抜けたり、レイヤーがUIになっているオブジェクトがUIと判定されてしまったりするので注意が必要である。
作った経緯
以前作ったマウス入力管理クラスではUIが検知されない。なぜなら、カメラから飛ばしたRayで当たり判定をしていたからである。Rayは衝突の判定にCollderを用いる。そのためColliderを持たないUIをRayでは検知することができないのである。
UIを検知しないRayは、手前にUIがあったとしても奥のオブジェクトの当たり判定を拾ってしまう。つまり、プレイヤーはUI越しにオブジェクトに触れてしまう。そのため、手前にUIがあることを検知するメソッドが必要になった。
中身の解説
EventSystem.RaycastAll
EventSystem.RaycastAll()を利用してマウスが触れているものをすべて取得する。
この関数は、イベントデータと結果を格納するリストを渡すことでリストに結果を入れてくれる。
格納用のリストは、メソッドの外で定義しておく。
private List<RaycastResult> raycastResults = new List<RaycastResult>();
現在のイベントシステムをEventSystem.currentで取得し、イベントデータを作成する。
PointerEventData eventDataCurrent = new PointerEventData(EventSystem.current);
イベントデータに座標を設定する。
eventDataCurrent.position = screenPosition;
用意した変数を引数として渡し、関数を呼び出す。
EventSystem.current.RaycastAll(eventDataCurrent, raycastResults);
UIかどうかの判定
UIかどうかの判定はレイヤーを用いて行っている。UIを作った場合、基本的にはレイヤーがUIになっていると思う。
レイヤーには番号が割り振られており、UIは5に固定されている。そのため、この番号を用いてUIかどうかを判定することにした。
ゲームオブジェクトが何番のレイヤーにあるかは、GameObject.layerで取得できる。
RaycastResultは検知したゲームオブジェクトを変数として持っているので、それを利用してゲームオブジェクトを取得し、そのレイヤーがUI(5)かどうかを調べる。
今回のコードでは、結果をbooleanの変数に格納している。
bool result = raycastResults[0].gameObject.layer == 5;
ちなみに、raycastResultsの最初の要素を取得しているのは、一番手前のゲームオブジェクトがUIかどうか調べるためである。raycastResultsにはカメラの手前から順に情報が格納されているようなので、配列の最初の要素が一番手前のRaycastResultとなる。
まとめ
今回は、EventSystem.RaycastAllとレイヤーを用いて、一番手前にUIがあるかどうかを検知するメソッドを作成した。ただ、ゲームオブジェクトがUIかどうかの判定にレイヤーを用いるのは少々、強引な方法な気もするのでもっといい方法がありそう。
あと、EventSystem.RaycastAllを何度も呼び出すのが、重い処理かどうかがわからない。直感で軽くはなさそうな気がする。しらんけど。
今後、もっといい方法が見つかったら改良していきたいとは思う。