Unity 汎用コリジョン & トリガーイベント管理スクリプト

はじめに

Unityでゲーム開発を行う際、オブジェクトの衝突(コリジョン)や接触(トリガー)を検出し、それに応じた処理を実装することがよくあります。しかし、オブジェクトごとに個別にスクリプトを記述すると管理が煩雑になります。本資料では、汎用的に利用できるコリジョンおよびトリガーイベント管理スクリプトを紹介します。

目的

  • コリジョン(物理衝突)とトリガー(接触)の両方に対応する
  • 外部からイベントを登録・解除できる設計にする
  • さまざまなオブジェクトで再利用できるようにする

スクリプト実装

1. CollisionTriggerHandler.cs

このスクリプトは、コリジョンおよびトリガーイベントを検知し、それぞれのイベントを外部から登録・解除できるようにします。

using System;
using UnityEngine;

public class CollisionTriggerHandler : MonoBehaviour
{
    // コリジョンイベント
    public event Action<Collision> OnCollisionEnterEvent;
    public event Action<Collision> OnCollisionExitEvent;
    public event Action<Collision> OnCollisionStayEvent;

    // トリガーイベント
    public event Action<Collider> OnTriggerEnterEvent;
    public event Action<Collider> OnTriggerExitEvent;
    public event Action<Collider> OnTriggerStayEvent;

    private void OnCollisionEnter(Collision collision)
    {
        OnCollisionEnterEvent?.Invoke(collision);
    }

    private void OnCollisionExit(Collision collision)
    {
        OnCollisionExitEvent?.Invoke(collision);
    }

    private void OnCollisionStay(Collision collision)
    {
        OnCollisionStayEvent?.Invoke(collision);
    }

    private void OnTriggerEnter(Collider other)
    {
        OnTriggerEnterEvent?.Invoke(other);
    }

    private void OnTriggerExit(Collider other)
    {
        OnTriggerExitEvent?.Invoke(other);
    }

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

このスクリプトは、前回とほぼ同じ機能を持っていますが、デリゲートを「event」として宣言している点が異なります。以下、主なポイントごとに解説します。


1. usingディレクティブとクラス定義

using System;
using UnityEngine;
  • System: C#の基本機能(ここではActionなど)を利用するために必要です。
  • UnityEngine: Unity固有のクラス(MonoBehaviour、Collision、Colliderなど)を利用するために必要です。

クラスはMonoBehaviourを継承しているので、UnityのGameObjectにアタッチして利用できます。


2. イベントの宣言(eventキーワードの利用)

// コリジョンイベント
public event Action<Collision> OnCollisionEnterEvent;
public event Action<Collision> OnCollisionExitEvent;
public event Action<Collision> OnCollisionStayEvent;

// トリガーイベント
public event Action<Collider> OnTriggerEnterEvent;
public event Action<Collider> OnTriggerExitEvent;
public event Action<Collider> OnTriggerStayEvent;
  • eventキーワード:
  • イベントとして宣言することで、外部からの直接の代入を防ぎ、「+=」や「-=」でのみ登録・解除できるようになります。
  • これにより、イベントの呼び出しはクラス内部でのみ行えるため、誤ってイベントが上書きされるリスクを減らすことができます。
  • Action:
  • 「戻り値がvoid」でT型の引数を持つメソッドの参照を保持するデリゲートです。
  • ここでは、衝突(Collision)やトリガー(Collider)を引数に取るイベントが定義されています。

3. Unityのイベント関数の実装

各メソッドは、Unityが自動的に呼び出すイベント関数です。イベント発生時に登録されたメソッドを呼び出す仕組みとなっています。

衝突イベント

private void OnCollisionEnter(Collision collision)
{
    OnCollisionEnterEvent?.Invoke(collision);
}

private void OnCollisionExit(Collision collision)
{
    OnCollisionExitEvent?.Invoke(collision);
}

private void OnCollisionStay(Collision collision)
{
    OnCollisionStayEvent?.Invoke(collision);
}
  • OnCollisionEnter:
    • 他のオブジェクトとの衝突が始まったときに呼び出され、登録されているイベントハンドラに衝突情報(Collisionオブジェクト)を渡して実行します。
  • OnCollisionExit / OnCollisionStay:
    • 同様に、衝突が終了したときや継続中の場合に、登録されたイベントハンドラを呼び出します。

トリガーイベント

private void OnTriggerEnter(Collider other)
{
    OnTriggerEnterEvent?.Invoke(other);
}

private void OnTriggerExit(Collider other)
{
    OnTriggerExitEvent?.Invoke(other);
}

private void OnTriggerStay(Collider other)
{
    OnTriggerStayEvent?.Invoke(other);
}
  • OnTriggerEnter:
    • オブジェクトがトリガー領域に入ったときに呼び出され、登録されたハンドラにCollider情報を渡して実行します。
  • OnTriggerExit / OnTriggerStay:
    • トリガー領域から出たときや滞在中の場合に、同様の処理を行います。

※ 「?.Invoke」構文を使用することで、もしイベントに何も登録されていなくても(nullの場合)、エラーにならずに安全に呼び出すことができます。


4. 利用例とイベントのメリット

利用例

別のスクリプトからこのイベントに対して処理を登録する例を示します:

public class Example : MonoBehaviour
{
    public CollisionTriggerHandler collisionHandler;

    private void Start()
    {
        // イベントに処理を登録(+= で追加)
        collisionHandler.OnCollisionEnterEvent += HandleCollisionEnter;
        collisionHandler.OnTriggerEnterEvent += HandleTriggerEnter;
    }

    private void HandleCollisionEnter(Collision collision)
    {
        Debug.Log("衝突開始: " + collision.gameObject.name);
    }

    private void HandleTriggerEnter(Collider other)
    {
        Debug.Log("トリガー領域に入りました: " + other.gameObject.name);
    }
}
  • 登録の仕方:
    • イベントに対しては、直接代入するのではなく「+=」を使って複数のハンドラを登録できます。
    • 「-=」で解除することも可能です。

イベント(event)のメリット

  • 安全性:
    • 外部のコードから直接イベントの内容を上書きすることができないため、予期しない動作を防げます。
  • カプセル化:
    • イベントの発生(Invoke)はクラス内部に限定され、外部はあくまでハンドラの登録・解除のみ行えます。

まとめ

  • イベント宣言:
    • 「event」キーワードを使うことで、外部からの直接変更を防ぎ、信頼性の高いイベントシステムを実装できます。
  • Actionの利用:
    • 衝突やトリガーに応じたイベントハンドラを簡潔に定義でき、コードの再利用性が向上します。
  • Unityとの連携:
    • Unityの組み込みイベント関数(OnCollisionEnter、OnTriggerEnterなど)と連携して、各種イベントを効率よく処理できるようになっています。

2. CollisionTest.cs

このスクリプトでは、CollisionTriggerHandler を利用し、実際にコリジョンやトリガーイベントが発生した際に処理を実行します。

using UnityEngine;

public class CollisionTest : MonoBehaviour
{
    private void Start()
    {
        CollisionTriggerHandler handler = GetComponent<CollisionTriggerHandler>();

        if (handler != null)
        {
            handler.OnCollisionEnterEvent += OnCollisionEntered;
            handler.OnTriggerEnterEvent += OnTriggerEntered;
        }
    }

    private void OnCollisionEntered(Collision collision)
    {
        Debug.Log("Collision Entered: " + collision.gameObject.name);
    }

    private void OnTriggerEntered(Collider other)
    {
        Debug.Log("Trigger Entered: " + other.gameObject.name);
    }
}

このスクリプトは、先ほど説明した CollisionTriggerHandler コンポーネントを使って、衝突イベントとトリガーイベントが発生した際に特定の処理を実行する例になっています。以下、コードの各部分について解説します。


1. クラスの基本構造

using UnityEngine;

public class CollisionTest : MonoBehaviour
{
    // ...
}
  • using UnityEngine;
    Unityの基本機能を利用するために必要な宣言です。
  • MonoBehaviourの継承:
    このクラスは MonoBehaviour を継承しているので、Unity の GameObject にアタッチして利用できます。

2. Start() メソッドでのコンポーネント取得とイベント登録

private void Start()
{
    CollisionTriggerHandler handler = GetComponent<CollisionTriggerHandler>();

    if (handler != null)
    {
        handler.OnCollisionEnterEvent += OnCollisionEntered;
        handler.OnTriggerEnterEvent += OnTriggerEntered;
    }
}
  • GetComponent()
    この関数は、同じ GameObject にアタッチされている CollisionTriggerHandler コンポーネントを取得します。
  • nullチェック:
    取得したコンポーネントが存在するか確認しています。存在しない場合は何もせず、エラーを回避します。
  • イベントの登録:
    • handler.OnCollisionEnterEvent += OnCollisionEntered;
      CollisionTriggerHandler の衝突開始イベントに対して、 OnCollisionEntered メソッドを登録します。
    • handler.OnTriggerEnterEvent += OnTriggerEntered;
      同様に、トリガーに入ったときのイベントに OnTriggerEntered メソッドを登録します。

3. イベントハンドラーの実装

衝突イベントハンドラー

private void OnCollisionEntered(Collision collision)
{
    Debug.Log("Collision Entered: " + collision.gameObject.name);
}
  • 引数 Collision:
    衝突したオブジェクトの情報が含まれています。
  • Debug.Log:
    衝突が発生した際に、衝突相手の GameObject の名前をコンソールに出力します。

トリガーイベントハンドラー

private void OnTriggerEntered(Collider other)
{
    Debug.Log("Trigger Entered: " + other.gameObject.name);
}
  • 引数 Collider:
    トリガー領域に入ったオブジェクトの情報を受け取ります。
  • Debug.Log:
    トリガーに入ったオブジェクトの名前をコンソールに出力します。

4. 全体の流れ

  1. 開始時の準備:
    Start() メソッド内で、同じ GameObject にある CollisionTriggerHandler を取得し、存在する場合はイベントのリスナー(OnCollisionEntered と OnTriggerEntered)を登録します。
  2. イベント発生時:
  • 何かが衝突すると、CollisionTriggerHandler 内で OnCollisionEnter イベントが発生し、登録された OnCollisionEntered が実行され、衝突したオブジェクトの名前が表示されます。
  • 同様に、トリガー領域に入ると OnTriggerEntered が呼び出され、対象のオブジェクトの名前が表示されます。

まとめ

  • コンポーネントの取得:
    GetComponent<T>() を使って、他のコンポーネント(ここでは CollisionTriggerHandler)を参照する方法を学びます。
  • イベントの登録:
    += を用いて、イベントに対してメソッド(ハンドラー)を登録する方法が示されています。これにより、特定のイベントが発生したときに自分の処理を呼び出すことができます。
  • デバッグ出力:
    Debug.Log を利用して、実際にイベントが発生した際の情報(対象オブジェクトの名前)をコンソールに出力する仕組みを実装しています。

使用方法

  1. CollisionTriggerHandler を衝突を検出したいオブジェクトにアタッチする。
  2. CollisionTest などのスクリプトを作成し、イベントを登録する。
  3. OnCollisionEnterEventOnTriggerEnterEvent などに外部から関数を登録し、適切な処理を記述する。

応用例

1. タグごとの処理を追加

オブジェクトのタグに応じて異なる処理を実行できます。

private void OnTriggerEntered(Collider other)
{
    if (other.CompareTag("Enemy"))
    {
        Debug.Log("Enemy detected!");
    }
    else if (other.CompareTag("Item"))
    {
        Debug.Log("Item collected!");
    }
}

2. イベントを解除する

不要になったイベントを解除することで、不要な処理の実行を防げます。

private void OnDestroy()
{
    CollisionTriggerHandler handler = GetComponent<CollisionTriggerHandler>();
    if (handler != null)
    {
        handler.OnCollisionEnterEvent -= OnCollisionEntered;
        handler.OnTriggerEnterEvent -= OnTriggerEntered;
    }
}

まとめ

コリジョンとトリガーの両方に対応
外部からイベント登録・解除が可能
汎用的で再利用しやすい設計

このスクリプトを活用することで、オブジェクトごとのスクリプト管理を簡素化し、メンテナンス性を向上させることができます。