Unity 2D:旗に触れたとき「ゴール」が2回出る原因と対処
目次
TL;DR
- 原因:同一 Rigidbody2D 配下の**複数 Collider(例:Box+Circle)**が同じ旗 Trigger に当たり、各コライダー分だけ OnTriggerEnter2D が呼ばれる。
- 解決(どれか一つ/併用可)
- CompositeCollider2D で統合(Used by Composite=Merge) → 発火1回
- レイヤーで片方を無視(片方を子に分離し、Collision Matrix で無効化)
- コードでワンショット制御(goalHit で一度だけ処理)
- CapsuleCollider2D を1枚に統一(Box+Circle を廃止して根本的に1ヒット化)
現象
- 旗(Goal, IsTrigger=ON)に猫(Player)が触れると、「ゴール」ログが2回出る。
- 猫の落下は発生していない。
なぜ2回出るのか
Unity 2D の Trigger は、同じ Rigidbody2D にぶら下がる複数 Collider が同じ Trigger と重なれば、各コライダー分だけコールバックが届く仕様。
つまり Box と Circle の両方が旗に触れていると 2回呼ばれます。
対処法
1) CompositeCollider2D で統合(推奨)
効果:統合形状でイベントが1回だけ発生。設計もシンプル。
手順
- Player に Rigidbody2D と CompositeCollider2D を追加。
- 統合したい Box / Circle の Composite Operation を Merge(= Used by Composite) に。
- 子コライダー側の IsTrigger は無視され、Composite 側の設定が使われます。
- CompositeCollider2D 推奨設定
- Geometry: Outlines
- Vertex Distance: 0.002〜0.005(細かすぎると頂点過多で重くなる)
- Edge Radius: 0.01〜0.03(角の“噛み”緩和)
注意
- どれか1枚でも Merge が外れていると、その1枚が独立に当たり二重再発。
- 別オブジェクトのコライダーが触れていないかも確認。
2) レイヤーで片方のコライダーを無視(設計分離)
効果:複数コライダーを残したまま、旗と接触するのは片方だけにできる。
ポイント
- レイヤーは GameObject 単位。同一オブジェクト内でコライダーごとに分けられないため、無視したいコライダーを子オブジェクトへ分離する前準備が必要。
手順(概要)
- Add Layer… で例:PlayerMain(反応させる側)、PlayerSub(無視側)、Goal(旗)。
- Player の子に空オブジェクト SubCollider を作り、無視したい Collider を移設(Rigidbody2D は親だけ)。
- レイヤー割当:親=PlayerMain、子=PlayerSub、旗=Goal。
- Edit > Project Settings > Physics 2D > Layer Collision Matrix で PlayerSub × Goal を無効化。→ 旗に触れるのは PlayerMain だけ → 発火は1回。
3) コードでワンショット制御(最小変更の保険)
設計をいじらず重複ログ/重複処理を防ぐ。将来の変更にも強い。
bool goalHit;
void OnTriggerEnter2D(Collider2D other)
{
if (goalHit) return;
if (!other.CompareTag("Goal")) return;
goalHit = true; // 二重発火の保険
Debug.Log("ゴール");
UnityEngine.SceneManagement.SceneManager.LoadScene("ClearScene");
}
4) CapsuleCollider2D を1枚に統一(シンプル&軽量)
効果:発火源が1つになるため二重発火が原理的になくなる。設定も軽く保守性が高い。
手順
- Player の BoxCollider2D / CircleCollider2D を削除。
- CapsuleCollider2D を1枚追加(Direction=Vertical, IsTrigger=OFF)。
- サイズ調整:幅は胴体に合わせ少し細め(スプライト幅の70〜85%目安)、高さは足元〜頭頂を覆う。
- 任意で PhysicsMaterial2D(Friction=0, Combine=Minimum)を付与。
- Rigidbody2D.constraints = FreezeRotation(姿勢安定)。
- 旗は IsTrigger=ON + Tag=“Goal”。OnTriggerEnter2D で判定。
メリット
- 設計が単純・最も軽量・設定漏れが少ない
- 丸端形状で段差に噛みにくい
デメリット
- 胴体基準で当たりがやや太めに感じる場面あり(幅を細めに/補助 Trigger を追加)
目的
- 物理の安定用“太め”コライダー(Capsule 1枚など)はそのまま残し、判定だけは“細く・局所的”に行うための専用センサーを持たせるためです。
- これにより、
- 旗やアイテムの**誤判定(早すぎ・遅すぎ・当たりすぎ)**を減らす
- 地面用/頭上用/前方用など用途別に、検出対象を限定できる(Layer で分離)
- 物理安定(段差・坂・押し出し)と精密判定をトレードオフせず両立できる
旗判定での実装パターン
「メイン当たり=物理(太め)」「補助=判定(細め)」の二枚看板にします。
- 子オブジェクトに補助トリガーを追加
- 子 GoalSensor を作成
- BoxCollider2D(小さめ)を付与し Is Trigger = ON
- 位置はプレイヤー前方に少しオフセット(例:offset.x = +0.15〜0.25)
- レイヤーを分ける(重要:重複発火を避ける)
- 例:
- メイン本体(Capsule)= PlayerBody
- 補助トリガー(子)= PlayerSensor
- 旗(Goal)= Goal
- Physics 2D > Layer Collision Matrix
- PlayerBody × Goal = OFF(無効)
- PlayerSensor × Goal = ON(有効)こうすると、旗と接触するのは補助トリガーだけになり、発火は1回に統一されます。
- 例:
- コード(親から処理する例)
// 子オブジェクト側(GoalSensor.cs)
using UnityEngine;
public class GoalSensor : MonoBehaviour
{
PlayerController owner;
void Awake()
{
owner = GetComponentInParent<PlayerController>();
}
void OnTriggerEnter2D(Collider2D other)
{
if (other.CompareTag("Goal"))
{
owner.OnGoalTriggered(); // 親の処理を呼ぶ
}
}
}
// 親(PlayerController.cs)
using UnityEngine;
using UnityEngine.SceneManagement;
public class PlayerController : MonoBehaviour
{
bool goalHit;
public void OnGoalTriggered()
{
if (goalHit) return;
goalHit = true;
Debug.Log("ゴール");
SceneManager.LoadScene("ClearScene");
}
}
ポイント
- メイン Capsule は物理専用(Is Trigger=OFF)。
- 旗との接触は補助トリガーだけが担当(行列で厳密化)。
- これで「太めで安定な当たり」を維持しつつ、ゴール判定は細く正確にできます。
補助トリガーの典型パターン(応用)
- 足元センサー:接地判定(IsGrounded)、坂・段差での安定
- 頭上センサー:天井ヒット/ジャンプ抑止
- 前方センサー:ゴール・スイッチ・会話開始・梯子/壁登り切替
- 取得センサー:コイン/アイテムだけを拾う(敵や地形は無視)
どれも 対象レイヤーを限定(Matrix)し、不要なものとは衝突しないようにするのがコツです。
チェックリスト
- 旗の Collider:IsTrigger=ON, Tag=“Goal”
- (方式1)Composite:すべての統合対象で Merge=ON
- (方式2)レイヤー:PlayerSub × Goal を無効化(子分離済み)
- (方式3)goalHit でワンショット制御
- (方式4)CapsuleCollider2D 1枚に統一(IsTrigger=OFF)
- 余計なコライダーの混在なし(別子や別オブジェクトが触れていない)
まとめ
- 二重発火は「複数コライダーが同じ Trigger に当たる」のが原因。
- Composite 統合・レイヤー無効化・ワンショット制御・Capsule 1枚化のいずれかで1ヒット化すれば解消。
- プロジェクト方針や保守性に合わせて選び、必要なら複数を併用してください。
訪問数 14 回, 今日の訪問数 14回
ディスカッション
コメント一覧
まだ、コメントがありません