【Unity】UnityEventを用いてマウスの入力管理クラスを作った

柔軟性の高いマウスの入力管理クラスを作りたいと思い、UnityEventを使ってみた。

マウスの入力管理クラスにUnityEventを使うのが一般的かどうかはよくわからないが、とりあえず形になったのでメモを残しておく。

クラスを作った理由

今回作ったのは、オブジェクトに触れているときにマウスからの入力があれば、登録されているイベントを実行するクラス。

Unityを触ったことがある人は、「それEventTriggerで良くない?」と思うかもしれない。ですが、自分が作っていたプロジェクトではEventTriggerでの実装が難しく、MouseControllerクラスの実装に至った。

EventTriggerでの実装が難しい例

今回、以下の2点を理由にEventTriggerでの実装をやめた。逆に、ここで挙げている2点に該当しない場合はEventTriggerを使っていいかも。

  1. マウスがどのオブジェクトに触れているか、一つのクラスで管理したい場合

    EventTriggerでわかるのはアタッチされているオブジェクトに対して操作があるかどうか。その情報をもとに管理するクラスに通知を送ってもいい気はするが、オブジェクト側に通知を送る関数を書かなきゃいけないのは不便。

    マウス入力用のクラスを作り、そのクラスが触れているオブジェクトを取得したほうがシンプルでいいだろう。

  2. マウスが触れている座標を取得したい場合

    実はEventTriggerからはマウスがオブジェクトのどこに触れているかは取得できない。取得したい場合は別の方法を考えなくてはならない。

    今回のコードでは、Rayとの当たり判定をもちいて取得する。

    使っていないので、詳しくは調べられていないが、座標の取得だけなら、EventSystemとIPointerEnterHandlerでも実装できるっぽい。IPointerEnterHandlerはイベントシステムのインターフェイスである。

参考ドキュメント

EventSystems.EventTrigger - Unity スクリプトリファレンス
イベントシステムからイベントを受け取ったり、各イベントに登録された関数を呼び出します。
EventSystems.IPointerEnterHandler - Unity スクリプトリファレンス
OnPointerEnter のコールバックを受け取りたいときにインターフェースを実装します。

汎用性の追求

また、マウスからの入力を受け取るだけなら、わざわざUnityEventを導入する必要はない。UnityEventを導入したのは柔軟性を高めるためです。マウスの入力を受け取る専用のクラスをつくっておけばいろんなところで使える。

UnityEventを使えば、任意の関数やメソッドを登録しておき、それを実行できるようになるので汎用性の高いコードになる。

UnityEventとは

公式のドキュメントと解説してくれている人の記事を読むのが早い。

公式ドキュメント

Events.UnityEvent - Unity スクリプトリファレンス
A zero argument persistent callback that can be saved with the Scene.

解説記事

UnityC# デリゲートとイベントとUnityActionの使い方
今回の記事ではデリゲートとイベントについて解説していきます。 前回は辞書の使い方について解説しましたね。 デリゲートとイベントを使うことでメソッドを値として使用することができます。メソッドを値にして利点があるのかと思われますが、アプリででき

自分は関数を変数みたいな箱にいれて保持できるって感じに理解している。C#にもActionやEventがあるが、今回はメソッドが便利だったのでUnityEventを選んだ。

コードの概要

MouseControllerクラスでEventを定義し、それぞれの操作がされた際にアクションを実行するようになっている。

マウスとオブジェクトの当たり判定は以下の記事を参考にした。

[Unity] マウス座標の方向にRayを飛ばして衝突した座標を得る方法  - Qiita
######説明マウスポインタ座標の方向にレイを飛ばして当たった座標を得るコードです。using System.Collections;using System.Collections.Gen…

RaycastHitでどのような値が取得できるかは以下のドキュメント参照。これらの値が扱えることもMouseControllerクラスを導入するメリットの一つ。

RaycastHit - Unity スクリプトリファレンス
レイキャストによる情報を得るための構造体

コード

MouseControllerクラス

今回はマウスは入ったとき、ドラッグされているとき、離されたときのみを実装しているが、Eventの種類を増やせば、他のマウス入力にも対応可能。

using System;
using UnityEngine;
using UnityEngine.Events;

public class MouseController : MonoBehaviour
{
    public UnityEvent<RaycastHit> MouseOnEvent;
    public UnityEvent MouseDragEvent;
    public UnityEvent MouseUpEvent;
    [SerializeField] private Camera cam;

    void Update()
    {
        Ray ray = cam.ScreenPointToRay(Input.mousePosition);
        RaycastHit hit;

        if (Physics.Raycast(ray, out hit))
        {
            MouseOnEvent?.Invoke(hit);

            if (Input.GetMouseButton(0))
            {
                MouseDragEvent?.Invoke();
            }

            if (Input.GetMouseButtonUp(0))
            {
                MouseUpEvent?.Invoke();
            }
        }
    }
}

マウス入力を受け取って処理をしたいクラス

まず、マウスコントローラーを取得し、変数に格納しておく。

[SerializeField] private MouseController mouseCS;

プロジェクトの任意の位置で、Eventを登録する。ここで、AddListenerの引数として渡すのは、登録したいメソッド名。Eventの登録と削除を使えば、実行されるメソッドを動的に変更することもできる。

mouseCS.MouseOnEvent.AddListener(MouseEnter);
mouseCS.MouseDragEvent.AddListener(MouseDrag);
mouseCS.MouseUpEvent.AddListener(MouseUp);

登録して、実行されるメソッドも定義しておく、MouseOnEventに登録するメソッドはRaycastHitを引数として持っているので登録するメソッドもRaycastHitを引数として持つようにする。

private void MouseEnter(RaycastHit hit)
{
	// マウスが触れているときの処理
}

private void MouseDrag()
{
	// マウスがドラッグされているときの処理
}

private void MouseUp()
{
	// マウスが離されたときの処理
}

登録しているイベントを全部消して、一度リセットしたい場合は、RemoveAllListeners()メソッドを使う。

mouseCS.MouseOnEvent.RemoveAllListeners();
mouseCS.MouseDragEvent.RemoveAllListeners();
mouseCS.MouseUpEvent.RemoveAllListeners();

参考

【Unity】ActionとUnityEventについて - Qiita
ActionAction とは、関数を参照型として扱い、沢山保持して一斉に実行できる参照型です。Action を使って依存関係を逆転すれば、再利用しやすいクラスが作れます。例として、OnCol…
UnityC# デリゲートとイベントとUnityActionの使い方
今回の記事ではデリゲートとイベントについて解説していきます。 前回は辞書の使い方について解説しましたね。 デリゲートとイベントを使うことでメソッドを値として使用することができます。メソッドを値にして利点があるのかと思われますが、アプリででき
[Unity] マウス座標の方向にRayを飛ばして衝突した座標を得る方法  - Qiita
######説明マウスポインタ座標の方向にレイを飛ばして当たった座標を得るコードです。using System.Collections;using System.Collections.Gen…