U
09 / 22 · 9 分
FAQ

衝突とトリガー:なぜイベントが発火しないのか

短い答え

OnCollisionEnter / OnTriggerEnter が呼ばれないのはなぜ?

両方のオブジェクトに collider が必要で、片方には Rigidbody も必要です。OnCollisionEnter なら kinematic 以外、OnTriggerEnter なら kinematic でも可。続けてメソッド名と引数、スクリプトが collider を持つオブジェクトに付いているか、二つの層が Layer Collision Matrix で今も相互に有効かを確かめてください。

画面の中で二つのオブジェクトが触れ合っているのに、あなたのメソッドは実行されない。Console には何も出ず、エラーも警告もなし——Unity は単に一度も呼び出さなかっただけです。原因はたったの六つで、全部確かめるのに一分もかかりません。

まず最初:二つのイベントのうち、どちらが必要か

✓ OnCollisionEnter

solid hits solid

二つのオブジェクトは互いを通り抜けられません。物理的な衝突が起きる場で使います:突き倒される木箱、壁に跳ね返るボールなど。渡されるのは Collision で、接触点と衝突時の速度が入っています。

✓ OnTriggerEnter

something enters an area

二つのオブジェクトはそのまま互いを通り抜け、その事実だけが通知されます。コイン、出入口、ダメージゾーン、剣の届く範囲などに使いましょう。渡されるのは Collider:何が入ってきたかだけで、衝突の情報はありません。

六つのチェック、この順番で

  1. 1

    どちらかの側に Rigidbody。物理が二つのうち一方を監視しているときだけ、Unity は接触を報告します。衝突イベントには少なくとも片側に kinematic でない Rigidbody が必要。トリガーイベントなら片側に Rigidbody があればよく、kinematic でも構いません。二つとも静かに置かれているだけなら、何も報告されません。

  2. 2

    メソッド名と引数。OnTriggerEnter(Collider other) と OnCollisionEnter(Collision collision)——一字一句このとおりに書いたものだけが有効です。Unity は名前でメソッドを探すため、onTriggerEnter や、3D シーンでの Collider2D はコンパイルは通っても決して呼ばれません。

  3. 3

    スクリプトが正しいオブジェクトに付いているか。メッセージは、コライダーを持つ GameObject と、そのコライダーが属する Rigidbody を持つ GameObject の両方に送られます。その二つより親側のスクリプトには何も届きません。

  4. 4

    両方のコライダーが存在し、有効であるか。Inspector を見るのが最速です:Play を押してそのモノに歩いて近づけ、Scene ウィンドウで二つのコライダーのアウトラインが本当に重なっているか確かめます。既定サイズのままのコライダーは、モデルにすっぽり収まっていることがよくあります。

  5. 5

    二つのレイヤーは出会いを許可されているか。Edit▸Project Settings▸Physics に Layer Collision Matrix があります。そこでチェックが外れているマスが一つでもあると、二つのオブジェクトは完全に互いを無視します——衝突もトリガーもメッセージもなし。

  6. 6

    2D と 3D は決して交わらない。Collider2D と Collider は同じシーンを共有する別々の物理エンジンです。2D コライダーが 3D コライダーに触れることはなく、2D のイベントは必ず末尾が 2D の方です。

ちゃんと動くトリガーと、ちゃんと動く衝突の両方

Coin.cs — on the coin: a trigger collider, no Rigidbody needed on the coin itself
using UnityEngine;

// The coin sits still, so the Rigidbody lives on the player instead.
[RequireComponent(typeof(Collider))]
public class Coin : MonoBehaviour
{
    public int value = 1;

    void Reset()
    {
        // Runs when the component is added: tick Is Trigger for whoever forgets.
        GetComponent<Collider>().isTrigger = true;
    }

    void OnTriggerEnter(Collider other)
    {
        // Anything can walk in here — check who it was before scoring.
        if (!other.CompareTag("Player")) return;

        // Wire this into whatever keeps your score.
        Debug.Log($"+{value}");
        Destroy(gameObject);
    }
}
Crate.cs — on the crate: a solid collider and a Rigidbody that physics may push
using UnityEngine;

[RequireComponent(typeof(Rigidbody), typeof(Collider))]
public class Crate : MonoBehaviour
{
    public AudioSource thud;

    void OnCollisionEnter(Collision collision)
    {
        // Collision carries the impact: a light nudge should not sound like a crash.
        if (collision.relativeVelocity.magnitude < 2f) return;

        thud.Play();
    }
}

見落としがちな、まだ二つある

  • CharacterController は Rigidbody ではありません。OnCollisionEnter は決して呼ばれず、代わりに OnControllerColliderHit が呼ばれます——ただし移動中にトリガーを起動するのは普通にするので、箱を押せないキャラでもコインは拾える、という現象が起きます。

  • 十分な速さのあるオブジェクトは、二つのフレームの間に薄い壁をまっすぐ突き抜けてワープします。その Rigidbody の Collision Detection を Continuous に設定し、ついでに壁にも少し厚みを持たせましょう。

  • transform.position を直接書き換えて Rigidbody を動かすのはテレポートで、物理が判定できる経路が存在しません。Rigidbody.MovePosition か速度で動かせば、接触の報告は戻ってきます。

これを作るレッスン

弾、ダメージ、エフェクト

この記事はそれだけで完結するレシピです。コースでは同じものを、5 つのタブを貫くひとつのプロジェクトの一部として作ります — キャラクターと戦闘 のレッスン 05。