【Unity】汎用的なイベントクラスのリファクタリング

接触中のオブジェクトを検出し、カスタムイベントを実行(つまり、指定するメソッドを実行)する方法について説明します
コードを変更することなく、自由に呼び出すメソッドが変更できること、また実行中にも臨機応変に呼び出すメソッドが変更できることが特徴です

基本のコード

using UnityEngine;
using System;
using UnityEngine.Events;

[RequireComponent(typeof(Collider))]
public class CollisionDetector : MonoBehaviour
{
    [SerializeField] private TriggerEvent onTriggerStay = new TriggerEvent();

    private void OnTriggerStay(Collider other)
    {
        onTriggerStay.Invoke(other);
    }
}

[Serializable]
public class TriggerEvent : UnityEvent<Collider>
{

}

このコードはUnityで使うCollisionDetectorクラスを定義しています。[RequireComponent(typeof(Collider))]属性により、このスクリプトがアタッチされたGameObjectにColliderコンポーネントが必須であることを保証します。TriggerEventUnityEvent<Collider>を継承したシリアライズ可能なカスタムイベントクラスで、Collider型のパラメータを持つイベントを定義しています。onTriggerStayフィールドはエディタから設定可能で、OnTriggerStayメソッドが他のオブジェクトとの接触を検出するとこのイベントが呼び出されます。これにより、特定の接触時のカスタムロジックを実装できます。

リファクタリングしたコード

using UnityEngine;
using UnityEngine.Events;

[RequireComponent(typeof(Collider))]
public class CollisionDetector : MonoBehaviour
{
    // 明示的な初期化を省略してシリアライズフィールドを宣言
    [SerializeField] private UnityEvent<Collider> onTriggerStay;

    private void OnTriggerStay(Collider other)
    {
        onTriggerStay?.Invoke(other); // イベントがnullでない場合にのみInvokeを呼び出す
    }
}

このコードでは、onTriggerStayフィールドの初期化を省略しています。Unityのシリアライズシステムは、インスペクターからアクセス可能なUnityEvent型のフィールドに自動的にインスタンスを割り当てます。このため、スクリプトがコンポーネントとしてGameObjectにアタッチされている限り、onTriggerStayはnullにならず、安全に使用できます。

ただし、コード内でイベントを動的に扱う場合(例えば、スクリプトからイベントリスナーを追加するなど)は、null条件演算子(?.)を使用してInvokeを呼び出すことで、onTriggerStayがnullでないことを保証すると良いでしょう。これは、スクリプトの実行中にonTriggerStayが明示的にnullに設定される可能性があるためです。しかし、通常のUnityの使用法では、このようなケースは稀であり、インスペクターからの設定のみを行う場合には、nullチェックは必要ありません。

イベントハンドラ(実行したいメソッド)に渡す引数をカスタムできるようにしたコード

using UnityEngine;
using System;

// コライダーイベントのデータを保持するクラス。
[Serializable]
public class ColliderEventData
{
    public Collider Collider { get; private set; }
    public string Tag { get; private set; }
    public int Layer { get; private set; }

    public ColliderEventData(Collider collider)
    {
        Collider = collider;
        Tag = collider.tag;
        Layer = collider.gameObject.layer;
    }
}
using UnityEngine;
using UnityEngine.Events;

[RequireComponent(typeof(Collider))]
public class CollisionDetector : MonoBehaviour
{
    // ColliderEventDataを引数に取るUnityEventを直接使用
    [SerializeField] private UnityEvent<ColliderEventData> onColliderStay = new UnityEvent<ColliderEventData>();

    private void OnTriggerStay(Collider other)
    {
        var eventData = new ColliderEventData(other);
        onColliderStay.Invoke(eventData);
    }
}

使い方

シンプルな使用例を提供します。この例では、CollisionDetectorスクリプトを使って、UnityのColliderコンポーネントが他のオブジェクトと接触している間にイベントを発生させ、そのイベントをリッスンして何らかのアクションを実行するシンプルなスクリプトを作成します。

ステップ 1: CollisionDetector スクリプトの設定

まず、CollisionDetector スクリプトを含むGameObjectにはColliderコンポーネントが必要です(Is Triggerオプションを有効にします)。そして、このスクリプトにはonColliderStayイベントが公開されており、Unityエディタから直接イベントリスナーを設定できます。

ステップ 2: イベントリスナーを作成

次に、CollisionDetectoronColliderStayイベントをリッスンする簡単なリスナースクリプトを作成します。このスクリプトは、特定のコライダーとの接触を検出した時に実行されるアクションを定義します。

using UnityEngine;

// イベントリスナーとして機能するクラス
public class CollisionListener : MonoBehaviour
{
    public void OnStayDetected(ColliderEventData eventData)
    {
        Debug.Log($"Stay Detected with object: {eventData.Collider.gameObject.name}, Tag: {eventData.Tag}, Layer: {eventData.Layer}");
    }
}

ステップ 3: Unity エディタでイベントを設定

  • CollisionDetectorスクリプトをアタッチしたGameObjectに、Colliderコンポーネントがアタッチされていることを確認し、Is Triggerを有効にします。
  • CollisionListenerスクリプトをシーン内の任意のGameObjectにアタッチします。
  • CollisionDetectorスクリプトのonColliderStayイベントセクションに移動し、プラスアイコンをクリックして新しいイベントリスナーを追加します。
  • CollisionListenerスクリプトをアタッチしたGameObjectをonColliderStayの空のフィールドにドラッグ&ドロップします。
  • ドロップダウンメニューからCollisionListener -> OnStayDetected(ColliderEventData)関数を選択します。

これで、CollisionDetectorが他のオブジェクトと接触している間、CollisionListenerOnStayDetectedメソッドが呼び出され、コンソールに接触しているオブジェクトの情報がログとして表示されます。このシンプルな例を通じて、カスタムイベントを使用してUnityでのオブジェクト間のインタラクションをどのように実装するかを理解できます。

Unity

Posted by hidepon