Unity チュートリアル:Sphere同士のすり抜けとPlaneとの衝突検知の実装

2025年3月30日

このチュートリアルでは、Unityシーン内に配置した2個のSphereと1個のPlaneを用い、Sphere同士はすり抜け(トリガー検知)ながらも、Planeとの衝突は物理的な衝突イベント(Collision検知)で処理する方法を学びます。さらに、分かりやすい命名規則や親子構造でのCollider分割も取り入れた実装例を紹介します。


1. 概要

  • Sphere同士のすり抜け
    Sphere同士の接触は、Trigger Collider(isTrigger=true)で検知し、物理的な反発は発生させません。
    ※注意:複数の子オブジェクトにColliderがあると、各Colliderからイベントが発生する可能性があるため、重複検知を防ぐ処理(フラグ管理など)を実装します。
  • Planeとの衝突検知
    Planeとの接触は、衝突用Collider(isTrigger=false)とCollisionイベント(OnCollisionEnter)を利用して検知します。
  • Rigidbodyの必要性
    物理演算によるイベント(衝突・トリガー検知)を正しく受け取るため、Sphere(親オブジェクト)にはRigidbodyを付与します。
    Planeは静的オブジェクトのため、通常はRigidbodyは不要です。

2. シーンの準備と命名規則

2.1. オブジェクトの配置

  1. Plane(Floor / GroundPlane)
    • 名前: Floor または GroundPlane
    • Tag: Plane
    • コンポーネント: Mesh Renderer、Mesh Filter、Mesh Collider(isTrigger=false)
    • ※Inspector上で、見た目と衝突判定が設定されていることを確認してください。
  2. Sphere(2個)
    Sphereは親子構造で作成し、以下のように分けます。

2.2. Sphereの親子構造と命名例

親オブジェクト(Sphere本体)

  • 名前: Sphere_A(もう1つは Sphere_B
  • Tag: Sphere
  • Layer: Sphere専用レイヤー(例:SphereLayer
  • コンポーネント:
    • Rigidbody(※必要に応じてisKinematic設定可能)
    • Mesh Renderer / Mesh Filter(見た目)
    • イベント処理用スクリプト(後述の SphereEventController)

子オブジェクト(Collider管理用)

各Sphereには以下の子オブジェクトを追加します。

  1. CollisionCollider
    • 名前: CollisionCollider
    • コンポーネント: Sphere Collider(isTrigger = false
    • ※物理的な衝突(Planeとの接触)用
  2. TriggerCollider
    • 名前: TriggerCollider
    • コンポーネント: Sphere Collider(isTrigger = true
    • ※Sphere同士の接触時にトリガーイベントを発生させる用

2.3. 階層構造例

SampleScene
├─ Main Camera
├─ Directional Light
├─ Floor         (Tag: Plane)
├─ Sphere_A      (Tag: Sphere, Layer: SphereLayer, Rigidbody付き)
│   ├─ CollisionCollider  (Sphere Collider, isTrigger = false)
│   └─ TriggerCollider    (Sphere Collider, isTrigger = true)
└─ Sphere_B      (Tag: Sphere, Layer: SphereLayer, Rigidbody付き)
    ├─ CollisionCollider  (Sphere Collider, isTrigger = false)
    └─ TriggerCollider    (Sphere Collider, isTrigger = true)

3. 実装方法

以下、2つのアプローチでSphere同士のすり抜けとPlaneとの衝突検知を実現する方法を紹介します。

アプローチ1:レイヤー衝突マトリックスの利用

  1. レイヤー設定
    • Sphereに専用レイヤー(例:SphereLayer)を設定し、PlaneはDefaultまたは別レイヤーに設定。
  2. レイヤー衝突マトリックスの調整
    • Unityエディタの Edit > Project Settings > Physics を開き、「Layer Collision Matrix」でSphereLayer同士の衝突を無効にします。
  3. → これにより、Sphereの衝突用Colliderが他のSphereと物理的に干渉せず、すり抜け状態となります。

アプローチ2:Physics.IgnoreCollisionの利用

  1. スクリプトでSphere同士の衝突無視
    • シーン内の全Sphereオブジェクト(Tag: Sphere)について、各Sphereの非トリガーCollider同士の衝突をコードで無視する方法です。レイヤーの対応が不要になります
    • この処理は、各Sphereの親オブジェクトにアタッチすることで実現します。

4. サンプルコード

以下は、Sphereの親オブジェクトにアタッチする【SphereEventController】スクリプトの例です。
このコードは、PlaneとのCollisionイベント、Sphere同士のTriggerイベントを処理し、さらに同一接触で複数回イベントが発生しないようフラグ管理も行っています。

using UnityEngine;

public class SphereEventController : MonoBehaviour
{
    // Sphere同士のトリガー接触処理が重複しないように管理するフラグ
    private bool triggerProcessed = false;

    // Planeとの物理衝突を検知
    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Plane"))
        {
            Debug.Log($"{name} が Planeとの衝突を検知");
            // Planeとの衝突時の処理
        }
    }

    // Sphere同士のトリガーイベントを検知
    void OnTriggerEnter(Collider other)
    {
        // 既に処理済みならスキップ(重複検知対策)
        if (triggerProcessed) return;

        if (other.gameObject.CompareTag("Sphere"))
        {
            // ここでは、Colliderが子オブジェクトの場合、親名を参照する例
            string otherParentName = other.transform.parent != null ? other.transform.parent.name : other.name;
            Debug.Log($"{name} が、{otherParentName} とトリガー接触を検知");
            // Sphere同士の接触時の処理

            // フラグを立てることで、同一接触で複数回処理しないようにする
            triggerProcessed = true;
        }
    }

    // 接触が離れたタイミングでフラグをリセット
    void OnTriggerExit(Collider other)
    {
        if (other.gameObject.CompareTag("Sphere"))
        {
            triggerProcessed = false;
        }
    }
}

移動用コード

using UnityEngine;

public class Move : MonoBehaviour
{
    public float speed = 10f;
    private Rigidbody rb;

    void Start()
    {
        // Rigidbodyを取得
        rb = GetComponent<Rigidbody>();
    }

    void FixedUpdate()  // 物理演算は FixedUpdate で実行する
    {
        float x = Input.GetAxis("Horizontal");
        float z = Input.GetAxis("Vertical");

        Vector3 movement = new Vector3(x, 0, z);
        rb.AddForce(movement * speed);
    }
}

※ Physics.IgnoreCollision を利用する場合(アプローチ2)

Sphere同士の非トリガーCollider間の衝突を無視する処理を追加する場合、以下のようなスクリプトを各Sphereに付与します。
この例は、Start()でScene内のSphereオブジェクトを取得し、非トリガーCollider同士の衝突を無視します。

using UnityEngine;

public class SphereCollisionManager : MonoBehaviour
{
    private Collider collisionCollider; // 衝突用Collider(isTrigger=false)

    void Start()
    {
        // 自身のColliderの中から、非トリガーColliderを選択
        Collider[] colliders = GetComponentsInChildren<Collider>();
        foreach (Collider col in colliders)
        {
            if (!col.isTrigger)
            {
                collisionCollider = col;
                break;
            }
        }
        if (collisionCollider == null)
        {
            Debug.LogWarning("非トリガーColliderが見つかりません");
            return;
        }

        // シーン内の全Sphereについて、非自分自身の非トリガーCollider同士の衝突を無視する
        GameObject[] spheres = GameObject.FindGameObjectsWithTag("Sphere");
        foreach (GameObject sphere in spheres)
        {
            if (sphere == gameObject) continue;
            Collider[] otherColliders = sphere.GetComponentsInChildren<Collider>();
            foreach (Collider other in otherColliders)
            {
                if (!other.isTrigger)
                {
                    Physics.IgnoreCollision(collisionCollider, other);
                }
            }
        }
    }

    // 以降は SphereEventController と同様に、TriggerとCollisionのイベントを実装可能
}

5. テストと確認

  1. シーン再生
    • Sphere同士が重なっても、物理的な衝突は発生せず、OnTriggerEnterが1回だけ呼ばれることを確認します。
    • PlaneにSphereが接触した際、OnCollisionEnterが呼ばれるかコンソールで確認してください。
  2. デバッグ
    • 複数のColliderがある場合、各Colliderからイベントが発生しないか注意し、必要ならフラグ管理やレイヤー設定(またはPhysics.IgnoreCollision)を活用してください。

6. まとめ

このチュートリアルでは以下の点を学びました:

  • シーンの準備とオブジェクトの命名規則
    → Sphere(親:Sphere_ASphere_B、子:CollisionColliderTriggerCollider)やPlane(Floor)といった名称で一目で役割を把握できる構成
  • イベント処理の実装
    → Sphere同士のすり抜けはトリガーColliderとOnTriggerEnterで検知し、Planeとの衝突はCollisionColliderとOnCollisionEnterで検知
  • Rigidbodyの必要性
    → Sphereの親オブジェクトにRigidbodyを付与することで、子のColliderからの物理イベントを一元管理
  • 重複イベント対策
    → フラグ管理やレイヤー衝突マトリックス、またはPhysics.IgnoreCollisionを活用して、1回の接触につき1回のみ処理する実装方法

この内容をもとにプロジェクトに適した設定・実装を行い、期待通りの挙動を確認してください。


このチュートリアル資料を参考に、より分かりやすく管理しやすいUnityプロジェクトの構築を進めてください。

参考:親オブジェクトでななく子オブジェクトにスクリプトをアタッチする方法

Unity

Posted by hidepon