オブジェクト同士が衝突しているかどうかを判定する (2D)

ページ更新日 :
ページ作成日 :

検証環境

Windows
  • Windows 11
Unity エディター
  • 2021.3.3f1
入力システムパッケージ
  • 1.3.0

この Tips の前提設定

この Tips の説明の前提として以下の設定を事前に行っています。

はじめに

本 Tips では2つのオブジェクトが接触しているかどうかを判定する方法について説明しています。 片方のオブジェクトはキーボードで動かします。

下準備

プロジェクトを作成したらオブジェクトとなるスプライトの画像を2つ用意します。 準備が面倒なら2つ同じスプライトを配置しても構いませんが今回は分かりやすく別々にしています。 名前はそれぞれ「UnityTips」「UnityTips_2」にしています。

2つの画像ファイルをビューにドロップして追加します。

左側のオブジェクトをキーボードで動かすためにスクリプトを追加します。スクリプト名は Player としています。 スクリプトの内容については「スプライトの移動、回転、拡大縮小を行う」の Tips とほぼ同じです。

using UnityEngine;
using UnityEngine.InputSystem;

public class Player : MonoBehaviour
{
  // 一定時間ごとに呼ばれます
  void FixedUpdate()
  {
    // キーボードの情報を取得
    var keyboard = Keyboard.current;
    if (keyboard == null)
    {
      Debug.Log("キーボードがありません。");
      return;
    }

    // スプライトの移動処理
    // Translate メソッドでスプライトの位置が移動します
    // Space.World を指定すると回転の影響をうけません
    if (keyboard.leftArrowKey.isPressed)
    {
      transform.Translate(-0.1f, 0, 0, Space.World);
    }
    if (keyboard.rightArrowKey.isPressed)
    {
      transform.Translate(0.1f, 0, 0, Space.World);
    }
    if (keyboard.upArrowKey.isPressed)
    {
      transform.Translate(0, 0.1f, 0, Space.World);
    }
    if (keyboard.downArrowKey.isPressed)
    {
      transform.Translate(0, -0.1f, 0, Space.World);
    }
  }
}

スクリプトを動かす方のオブジェクトにアタッチします。

ゲームを実行してキーボードのカーソルキーで動くか確認してください。

最後に衝突判定状態を表示するためのテキストオブジェクトを配置しておきます。 オブジェクト名は TextState としています。

衝突処理設定

ここから衝突に関連する設定を行います。

まずは動かす方のオブジェクト「UnityTips」を選択し、インスペクターから「コンポーネントを追加」をクリックします。 一覧から Physics 2D を選択します。 Physics は 3D 対象なので間違えないでください。

Physics 2D の中から Box Collider 2D を選択します。今回のスプライトは矩形なので Box を選択していますが、例えば円状であれば Circle など他の形を選択します。

Box Collider 2D コンポーネントが追加されるのでパラメーターから「トリガーにする」にチェックを入れます。 これにより「当たったかどうか」だけの情報を取得できるようになります。

続いて Rigidbody 2D のコンポーネントを追加します。「Physics 2D -> Rigidbody 2D」から追加できます。 Rigidbody はオブジェクトに物理演算の情報を与え、Collider を持っているオブジェクトとの衝突判定も行うことができるようになります。

ただ Rigidbody コンポーネントを持っていると -Y 方向への重力情報も含まれてしまうので、実行するとそのまま下に落ちてしまいます。 なのでパラメーターから「重力スケール」を 0 に設定しておきます。

ちなみに横スクロールアクションゲームであれば下に自由落下するという意味で重力スケールを使うと便利な場合はあります。

同様にもう一つのオブジェクト「UnityTips_2」にも Box Collider 2D コンポーネントを追加しておきます。 こちらは今回影響を受ける側なので設定は何も変更する必要はありません。

Player スクリプトを開いて以下のように using と各フィールド、メソッドを追加します。

using System;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;

public class Player : MonoBehaviour
{
  /// <summary>状態表示用テキストオブジェクト。</summary>
  [SerializeField] private Text TextState;

  // 中略

  /// <summary>衝突した瞬間に呼ばれます。</summary>
  /// <param name="partner">衝突した相手のコリジョン情報。</param>
  private void OnTriggerEnter2D(Collider2D partner)
  {
    TextState.text = $"OnTriggerEnter2D : {partner.tag} {DateTime.Now:HH:mm:ss.fff}{Environment.NewLine}{TextState.text}";
  }

  /// <summary>衝突している間呼ばれます。ただしスリープモードになった場合は呼ばれません。</summary>
  /// <param name="partner">衝突した相手のコリジョン情報。</param>
  private void OnTriggerStay2D(Collider2D partner)
  {
    TextState.text = $"OnTriggerStay2D : {partner.tag} {DateTime.Now:HH:mm:ss.fff}{Environment.NewLine}{TextState.text}";
  }

  /// <summary>衝突状態でなくなったタイミングで呼ばれます。</summary>
  /// <param name="partner">衝突した相手のコリジョン情報。</param>
  private void OnTriggerExit2D(Collider2D partner)
  {
    TextState.text = $"OnTriggerExit2D : {partner.tag} {DateTime.Now:HH:mm:ss.fff}{Environment.NewLine}{TextState.text}";
  }
}

当たったかどうかの情報を表示したいので以下のようにテキストオブジェクトに表示できるようにしておきます。

/// <summary>状態表示用テキストオブジェクト。</summary>
[SerializeField] private Text TextState;

衝突判定の処理を以下のように記述します。

/// <summary>衝突した瞬間に呼ばれます。</summary>
/// <param name="partner">衝突した相手のコリジョン情報。</param>
private void OnTriggerEnter2D(Collider2D partner)
{
  TextState.text = $"OnTriggerEnter2D : {partner.tag} {DateTime.Now:HH:mm:ss.fff}{Environment.NewLine}{TextState.text}";
}

/// <summary>衝突している間呼ばれます。ただしスリープモードになった場合は呼ばれません。</summary>
/// <param name="partner">衝突した相手のコリジョン情報。</param>
private void OnTriggerStay2D(Collider2D partner)
{
  TextState.text = $"OnTriggerStay2D : {partner.tag} {DateTime.Now:HH:mm:ss.fff}{Environment.NewLine}{TextState.text}";
}

/// <summary>衝突状態でなくなったタイミングで呼ばれます。</summary>
/// <param name="partner">衝突した相手のコリジョン情報。</param>
private void OnTriggerExit2D(Collider2D partner)
{
  TextState.text = $"OnTriggerExit2D : {partner.tag} {DateTime.Now:HH:mm:ss.fff}{Environment.NewLine}{TextState.text}";
}

Rigidbody 2D を持っているオブジェクトが Collider 2D を持っている他のオブジェクトに衝突すると上記の各イベントが発生します。 これにより当たっているかどうかの判別と処理ができるようになります。

イベントは3つありますがそれぞれ以下のタイミングで呼ばれます。

OnTriggerEnter2D オブジェクト同士が当たったタイミング
OnTriggerStay2D オブジェクト同士が当たっている間
OnTriggerExit2D オブジェクト同士が衝突状態から離れたタイミング

いずれも相手オブジェクトがもつ Collider2D を引数で受け取るので Collider2D.tag で当たった対象の種類(敵とかアイテムとか)を調べたり Collider2D.gameObject で相手のオブジェクトを取得して処理することができます。

上記3つのイベントのうち OnTriggerEnter2DOnTriggerExit2D は一回の衝突ごとに一回呼ばれます。

OnTriggerStay2D は衝突している間毎フレーム呼ばれますが、両オブジェクトが停止してから一定時間が経過するとイベントが呼ばれなくなります。 これはスリープ状態に入ったとみなされ、動いていないものに毎フレーム衝突計算するのは処理の無駄であると判断されるためです。

スリープ状態になってほしくない場合は Rigidbody 2D のパラメータのスリープモードを「スリープしない」に変更してください。

コードを変更したら情報表示用のテキストをセットします。

ゲームを実行しキーボードでオブジェクトを動かしてもう一つのスプライトに接触してみてください。 それぞれ3つの関数が呼ばれる事を確認できると思います。

ちなみにオブジェクトを接触させてから移動を停止すると、少しの間は OnTriggerStay2D メソッドが呼ばれますが、 一定時間経過後に OnTriggerStay2D メソッドが呼ばれなくなることを確認できると思います。

これがスリープ状態に入ったことを示すのですが、このスリープに入るまでの時間を変更したい場合はプロジェクトの設定から「2D 物理」を選択して「スリープ時間」パラメータを秒で指定することにより変えることができます。 0 に設定することは出来ませんが、限りなく小さい値にすると OnTriggerStay2D メソッド自体呼ばれなくなります。