子オブジェクトのイベント処理実装

以下は、子オブジェクト毎のイベント処理の実装手法を、引数なし/引数ありの両パターンとして、以下の3つの方式(個別スクリプト方式、親通知方式、UnityEvent方式)の資料になります。

次の資料の続きになります


1. 個別スクリプト方式

1-1. 引数なしの実装

各子オブジェクトに専用のスクリプトをアタッチし、イベント発生時に単にログ出力などの固定処理を実装する例です。

using UnityEngine;

public class ChildColliderHandler_NoArgs : MonoBehaviour
{
    void OnTriggerEnter(Collider other)
    {
        Debug.Log($"{gameObject.name} で Trigger イベント発生:{other.gameObject.name}");
        // 引数なしの処理(固定の処理を実装)
    }

    void OnCollisionEnter(Collision collision)
    {
        Debug.Log($"{gameObject.name} で Collision イベント発生:{collision.gameObject.name}");
        // 引数なしの処理(固定の処理を実装)
    }
}
C#

メリット・デメリット(引数なし)

  • メリット
    • 実装がシンプルで分かりやすい
    • 各子オブジェクトで直接制御できる
  • デメリット
    • イベント詳細(相手情報など)を活用できない
    • 変更が必要な際、各スクリプトの修正が必要

1-2. 引数ありの実装

イベント発生時に、引数としてイベント情報(例:衝突相手のColliderやCollision情報)を利用して処理を行う例です。

using UnityEngine;

public class ChildColliderHandler_WithArgs : MonoBehaviour
{
    void OnTriggerEnter(Collider other)
    {
        HandleTriggerEvent(other);
    }

    void OnCollisionEnter(Collision collision)
    {
        HandleCollisionEvent(collision);
    }

    // Triggerイベントの引数付きハンドラ
    private void HandleTriggerEvent(Collider other)
    {
        Debug.Log($"{gameObject.name} で Trigger イベント発生:相手は {other.gameObject.name}");
        // 引数(other)を用いた処理を実装
    }

    // Collisionイベントの引数付きハンドラ
    private void HandleCollisionEvent(Collision collision)
    {
        Debug.Log($"{gameObject.name} で Collision イベント発生:相手は {collision.gameObject.name}");
        // 引数(collision)を用いた処理を実装
    }
}
C#

メリット・デメリット(引数あり)

  • メリット
    • イベント詳細情報を柔軟に利用できる
    • 各子オブジェクトでイベントの発生状況を正確に把握可能
  • デメリット
    • 実装が各スクリプトに分散するため、共通処理の変更が煩雑になる可能性がある

2. 親通知方式

子オブジェクト側でイベントが発生した際に、親オブジェクトへ通知し、親側で一元管理・分岐処理を実装する方式です。

2-1. 引数なしの実装

子側スクリプト

using UnityEngine;

public class ChildColliderNotifier_NoArgs : MonoBehaviour
{
    void OnTriggerEnter(Collider other)
    {
        // 引数なしで親に通知
        transform.parent.SendMessage("OnChildTriggerEvent_NoArgs", gameObject, SendMessageOptions.DontRequireReceiver);
    }

    void OnCollisionEnter(Collision collision)
    {
        // 引数なしで親に通知
        transform.parent.SendMessage("OnChildCollisionEvent_NoArgs", gameObject, SendMessageOptions.DontRequireReceiver);
    }
}
C#

親側スクリプト

using UnityEngine;

public class ParentEventManager_NoArgs : MonoBehaviour
{
    void OnChildTriggerEvent_NoArgs(GameObject child)
    {
        Debug.Log($"親で受信(引数なし):{child.name} から Trigger イベント");
        // 共通処理または子ごとの処理分岐を実装
    }

    void OnChildCollisionEvent_NoArgs(GameObject child)
    {
        Debug.Log($"親で受信(引数なし):{child.name} から Collision イベント");
        // 共通処理または子ごとの処理分岐を実装
    }
}
C#

メリット・デメリット(引数なし)

  • メリット
    • 親側で一元管理でき、全体の流れが把握しやすい
    • 実装がシンプル
  • デメリット
    • イベントの詳細情報が通知されないため、処理内容が限定的

2-2. 引数ありの実装

カスタムイベントデータクラス

using UnityEngine;

public class ChildEventInfo
{
    public GameObject Child;   // イベント発生元の子オブジェクト
    public GameObject Other;   // 衝突相手のオブジェクト(ColliderまたはCollisionの相手)
    public string EventType;   // "Trigger" または "Collision"
    // 必要に応じて、追加情報も持たせる
}
C#

子側スクリプト

using UnityEngine;

public class ChildColliderNotifier_WithArgs : MonoBehaviour
{
    void OnTriggerEnter(Collider other)
    {
        ChildEventInfo info = new ChildEventInfo {
            Child = gameObject,
            Other = other.gameObject,
            EventType = "Trigger"
        };
        transform.parent.SendMessage("OnChildEvent_WithArgs", info, SendMessageOptions.DontRequireReceiver);
    }

    void OnCollisionEnter(Collision collision)
    {
        ChildEventInfo info = new ChildEventInfo {
            Child = gameObject,
            Other = collision.gameObject,
            EventType = "Collision"
        };
        transform.parent.SendMessage("OnChildEvent_WithArgs", info, SendMessageOptions.DontRequireReceiver);
    }
}
C#

親側スクリプト

using UnityEngine;

public class ParentEventManager_WithArgs : MonoBehaviour
{
    void OnChildEvent_WithArgs(ChildEventInfo info)
    {
        if (info.EventType == "Trigger")
        {
            Debug.Log($"親で受信(引数あり):{info.Child.name} から Trigger イベント(相手:{info.Other.name})");
            // Triggerイベント用の詳細処理
        }
        else if (info.EventType == "Collision")
        {
            Debug.Log($"親で受信(引数あり):{info.Child.name} から Collision イベント(相手:{info.Other.name})");
            // Collisionイベント用の詳細処理
        }
    }
}
C#

メリット・デメリット(引数あり)

  • メリット
    • イベント詳細情報を親側で一元管理できるため、柔軟な共通処理や分岐が可能
    • カスタムクラスで複数の引数をまとめて渡せる
  • デメリット
    • SendMessageは型安全性に欠け、パフォーマンスやデバッグが難しい場合がある
    • 親側での分岐処理が複雑になる可能性がある

3. UnityEvent方式

UnityEventを利用することで、Inspector上から直感的にイベントの割り当てが可能となり、プログラマー以外のメンバーも設定可能な方式です。

3-1. 引数なしの実装

using UnityEngine;
using UnityEngine.Events;

public class ChildEventTrigger_NoArgs : MonoBehaviour
{
    [Header("UnityEvent設定(引数なし)")]
    public UnityEvent OnChildTrigger;
    public UnityEvent OnChildCollision;

    void OnTriggerEnter(Collider other)
    {
        Debug.Log($"{gameObject.name} で Trigger 発生:{other.gameObject.name}");
        OnChildTrigger?.Invoke();
    }

    void OnCollisionEnter(Collision collision)
    {
        Debug.Log($"{gameObject.name} で Collision 発生:{collision.gameObject.name}");
        OnChildCollision?.Invoke();
    }
}
C#

メリット・デメリット(引数なし)

  • メリット
    • エディタ上で直感的にメソッドをドラッグ&ドロップで割り当て可能
    • シンプルな実装で、柔軟性に優れる
  • デメリット
    • イベント詳細情報(例:衝突相手など)を受け取ることができない

3-2. 引数ありの実装

事前定義:パラメータ付きUnityEventクラス

using UnityEngine;
using UnityEngine.Events;

[System.Serializable]
public class ColliderEvent : UnityEvent<Collider> { }

[System.Serializable]
public class CollisionEvent : UnityEvent<Collision> { }
C#

子側スクリプト

using UnityEngine;
using UnityEngine.Events;

public class ChildEventTrigger_WithArgs : MonoBehaviour
{
    [Header("UnityEvent設定(引数あり)")]
    // Triggerイベント用(引数:Collider)
    public ColliderEvent OnChildTrigger;
    // Collisionイベント用(引数:Collision)
    public CollisionEvent OnChildCollision;

    void OnTriggerEnter(Collider other)
    {
        Debug.Log($"{gameObject.name} で Trigger 発生:{other.gameObject.name}");
        OnChildTrigger?.Invoke(other);
    }

    void OnCollisionEnter(Collision collision)
    {
        Debug.Log($"{gameObject.name} で Collision 発生:{collision.gameObject.name}");
        OnChildCollision?.Invoke(collision);
    }
}
C#

インスペクター設定例

  1. 各子オブジェクトに ChildEventTrigger_WithArgs をアタッチ
  2. Inspector上の「On Child Trigger」や「On Child Collision」に、引数(ColliderまたはCollision型)を受け取る対象メソッドをドラッグ&ドロップで設定

メリット・デメリット(引数あり)

  • メリット
    • 引数付きイベントをエディタ上で柔軟に設定可能
    • 非プログラマーでもイベントの割り当てが直感的に行える
    • 再利用性、保守性が高い
  • デメリット
    • 大量に使用するとシリアライズのオーバーヘッドが増加する可能性
    • 複雑なロジックにはコードで直接制御したほうが明確な場合がある

まとめ

  • 個別スクリプト方式
    • 引数なし:シンプルな実装で各子ごとの固定処理に最適
    • 引数あり:イベント詳細を活用し、柔軟な個別処理を実装可能
  • 親通知方式
    • 引数なし:親で一元管理する際のシンプルな通知方法
    • 引数あり:カスタムデータクラスを用いて、詳細情報を親側で一括管理可能
  • UnityEvent方式
    • 引数なし:エディタ上で直感的に設定でき、シンプルな実装が可能
    • 引数あり:パラメータ付きUnityEventにより、柔軟かつ再利用性の高いイベント管理を実現

各手法は、プロジェクトの要件やチームの運用体制に合わせて選択してください。どの方式も、引数の有無を含めた柔軟なイベント処理が実現でき、シーン内の子オブジェクト毎の挙動を細かく制御することが可能です。

Unity,イベント

Posted by hidepon