Help us understand the problem. What is going on with this article?

【Unity】複数のColliderが接触している場合の判定

実際に困ったケース

Unityで当たり判定を設定する場合Coliderを利用するわけですが、細かいところに手が届かないことがあります。具体的には以下のような場合に困ったわけです。

  • カンタンなゲームのルール
    • 青い枠に接触すればゲームクリア
    • 赤い枠に接触するとゲーム失敗

図1.png

接触した瞬間に判定するだけなら大丈夫です。ただし、もしこれが接触した状態を「少しの間」待ってから判定する場合、以下のようなことを考える必要があります。

  • 接触から少し時間が経過してから判定処理をする場合
    • 青い枠と赤い枠が同時に接触する場合がある
    • 同時に接触した場合に優先する仕組みが必要

Unityにそういった機能はないの?

記事登校時の2019/05/16にはなかったように思います。接触してる状態を取得するならCollider.OnCollisionStayを使えばイイんですが、それぞれの接触ごとに検知します。上記のように少し時間が経過してから判定をしたいのですが、それぞれに接触したときのオブジェクトを検知します。したがって時間差でどちらのオブジェクトを優先などの処理は工夫する必要があります。

複数の接触をどうやって検知するか?

答えはカンタンでGameObjectListを使えば検知できます。もう少し細かくいうとColliderで検知したオブジェクトをListに格納していき、そのListに格納されている内容から判定を行えばよいです。青い枠に当たっていれば赤い枠に触れててもクリアする場合は以下のようにするとよいです。以前に投稿した記事のソースから説明用に削っています。
タブンですがそのままコピペしても動くかもしれません。アタッチしたオブジェクトについては、画面タップすると上にオラァ~って吹っ飛んでいきます。2Dのゲームなのでご注意をば・・・。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class C02_HandMove : MonoBehaviour {

    public float pushSpeed = 10.0f; //打ち出されるスピード

    bool isTouched = false; //タッチされたか

    private float stayTime = 0;

    List<GameObject> colList = new List<GameObject> ();

    void Update () {

        //タッチしたらアタッチされているオブジェクトを上に発射
        TouthPushHand ();

        if (isTouched) {
            stayTime += Time.deltaTime;
            //1秒後にcolListで判定を行う
            if (stayTime > 1.0f) {
                if (colList.Count == 1) {
                    foreach (GameObject checkObj in colList) {
                        if (checkObj.name == "PushHole") {
                            Debug.Log("クリアしました!");
                        } else {
                            Debug.Log("失敗しました!");
                        }
                    }
                }
                if (colList.Count > 1) {
                    Debug.Log("クリアしました!");
                }
                if (colList.Count == 0) {
                    Debug.Log("失敗しました!");
                }
            }
        }
    }

    void TouthPushHand () {
        Vector3 PushForse = new Vector3 (0, pushSpeed, 0);
        if (Input.GetMouseButtonDown (0)) {
            gameObject.GetComponent<Rigidbody2D> ().AddForce (PushForse, ForceMode2D.Impulse);
            isTouched = true;
        }
    }

    private void OnCollisionEnter2D (Collision2D col) {
        colList.Add (col.gameObject);
    }
}

OnCollisionEnter2Dで接触してきたオブジェクトをリストに格納します。ガチャガチャと書いていますが、リストの中のオブジェクトを以下のような形で判定しています。

  • リストの中のオブジェクトが1つだけの場合
    • 青い枠のオブジェクトに当たっていればクリアと判定
    • 赤い枠のオブジェクトに当たっていれば失敗と判定
  • リストの中のオブジェクトが2つ以上の場合、青い枠を優先するのでクリアと判定
  • リストの中のオブジェクトがない場合、失敗と判定

冗長的なカンジで拡張性もどうなのよ?っていうご意見はあるかもしれませんが、ifでの分岐でわかりやすいので初心者にはイイかなと思ってます。知っている人からすれば当たり前かもしれないんですが、こういうTIPSをわかりやすくまとめておきたいのです。

ちなみに上記のコードだとUpdateに記述されているため、接触後はコンソールにログが表示され続けます。解決したい場合は、別でゲームクリアフラグのようなモノで管理するか、判定後にゲームオブジェクトを生成する場合なら!GameObject.Findなどの条件で1つだけ生成するようにするとイイかもしれません。多重生成を防ぐ件については別の記事で投稿しようかなと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away