実際に困ったケース
Unityで当たり判定を設定する場合Colider
を利用するわけですが、細かいところに手が届かないことがあります。具体的には以下のような場合に困ったわけです。
- カンタンなゲームのルール
- 青い枠に接触すればゲームクリア
- 赤い枠に接触するとゲーム失敗
接触した瞬間に判定するだけなら大丈夫です。ただし、もしこれが接触した状態を「少しの間」待ってから判定する場合、以下のようなことを考える必要があります。
- 接触から少し時間が経過してから判定処理をする場合
- 青い枠と赤い枠が同時に接触する場合がある
- 同時に接触した場合に優先する仕組みが必要
Unityにそういった機能はないの?
記事登校時の2019/05/16にはなかったように思います。接触してる状態を取得するならCollider.OnCollisionStay
を使えばイイんですが、それぞれの接触ごとに検知します。上記のように少し時間が経過してから判定をしたいのですが、それぞれに接触したときのオブジェクトを検知します。したがって時間差でどちらのオブジェクトを優先などの処理は工夫する必要があります。
複数の接触をどうやって検知するか?
答えはカンタンでGameObject
のList
を使えば検知できます。もう少し細かくいうと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つだけ生成するようにするとイイかもしれません。多重生成を防ぐ件については別の記事で投稿しようかなと思います。