ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【Unity】オブジェクト同士の衝突判定、重なり判定

 今回はUnityでのオブジェクト同士の当たり判定についての備忘録です。

 オブジェクト同士の当たり判定には「オブジェクト同士の衝突判定」と「オブジェクト同士の重なり判定」の2種類があるので、それぞれについて説明していきます。(といっても、玉転がしのチュートリアルに書いてありますが整理のためにやります)


0:当たり判定に使用するオブジェクトを作成する
unity3d.com

「はじめてのUnity」にあるような壁をCubeで作成、地面をPlaneで作成、PlayerであるSphereを作成し、以下のように配置します。
f:id:rikoubou:20180621213711p:plain

 この状態でPlayerである「Sphere」を選択し、「Add Component」から「Rigidbody」を選択して追加します。
f:id:rikoubou:20180621213944p:plain

 追加した「Rigidbody」の「Use Gravity」のチェックを外します。
f:id:rikoubou:20180621221827p:plain

 次にSphereがちゃんと映るような位置にカメラを移動・回転させ、Sphereにドラッグ&ドロップしてSphereの子にします。
f:id:rikoubou:20180621221342p:plain

 ここまでできたら以下のスクリプトを「PlayerController」という名前で作成します。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

/**
 * PlayerController
 */
public class PlayerController : MonoBehaviour {

	private Transform _playerTrans;
	private float DIFF = 0.5f;

	// Use this for initialization
	void Start () {
		_playerTrans = this.transform;
	}
	
	// Update is called once per frame
	void Update () {
		if (Input.GetKey(KeyCode.Space)) {
			rollPlayer ();
		} else {
			movePlayer ();
		}
	}

	/**
	 * Playerを矢印キーで動かす関数
	 */
	private void movePlayer() {
		if (Input.GetKey(KeyCode.UpArrow)) {
			_playerTrans.Translate (Vector3.forward * DIFF);
		} else if (Input.GetKey(KeyCode.RightArrow)) {
			_playerTrans.Translate (Vector3.right * DIFF);
		} else if (Input.GetKey(KeyCode.LeftArrow)) {
			_playerTrans.Translate (Vector3.left * DIFF);
		} else if (Input.GetKey(KeyCode.DownArrow)) {
			_playerTrans.Translate (Vector3.back * DIFF);
		}
	}

	/**
	 * Playerを左右に回転させる関数
	 */
	private void rollPlayer() {
		Quaternion q = _playerTrans.localRotation;
		if (Input.GetKey(KeyCode.LeftArrow)) {
			q = q * Quaternion.Euler(0f, -DIFF, 0f);
		} else if (Input.GetKey(KeyCode.RightArrow)) {
			q = q * Quaternion.Euler(0f, DIFF, 0f);
		}
		_playerTrans.localRotation = q;
	}

	/* オブジェクト同士の衝突判定 */
	// オブジェクトが衝突したとき
	void OnCollisionEnter(Collision collision) {
		Debug.Log ("Collision Enter: " + collision.gameObject.name);
	}
	 
	// オブジェクトが離れた時
	void OnCollisionExit(Collision collision) {
		Debug.Log ("Collision Exit: " + collision.gameObject.name);
	}
	 
	// オブジェクトが触れている間
	void OnCollisionStay(Collision collision) {
		Debug.Log ("Collision Stay: " + collision.gameObject.name);
	}

	/* オブジェクト同士の重なり判定 */
	// オブジェクトが重なったとき
	void OnTriggerEnter(Collider other) {
		Debug.Log ("Trigger Enter: " + other.gameObject.name);
	}

	// オブジェクトが離れた時
	void OnTriggerExit(Collider other) {
		Debug.Log ("Trigger Exit: " + other.gameObject.name);
	}

	// オブジェクトが重なっている間
	void OnTriggerStay(Collider other) {
		Debug.Log ("Trigger Stay: " + other.gameObject.name);
	}

}

 作成したスクリプトSphereにドラッグ&ドロップして適用させます
f:id:rikoubou:20180621222302p:plain

 この状態でゲームを再生させると以下のように矢印キーで前後左右に動き、スペースキーを押しながら矢印の左右のキーで視点を回転させることができます。
f:id:rikoubou:20180621222628g:plain

 これで準備ができました。


1:オブジェクト同士の衝突判定
 オブジェクト同士の衝突判定の場合の説明です。

 実際に存在するものの当たり判定(オブジェクトの中を通り過ぎたりしないようにしたい時など)に使用します。

 使い方としては0の準備ができていれば、特に変更することなくこの当たり判定を使用できます。

 衝突する自身のオブジェクトのスクリプト(ここではSphereに適用したPlayerControllerスクリプト)にある以下の記述で、それぞれの場合において衝突先のオブジェクトを取得することができます。

// オブジェクトが衝突したとき
void OnCollisionEnter(Collision collision) {
	Debug.Log ("Enter: " + collision.gameObject.name); // 衝突先のオブジェクト名を取得
}
	 
// オブジェクトが離れた時
void OnCollisionExit(Collision collision) {
	Debug.Log ("Exit: " + collision.gameObject.name); // 衝突していたオブジェクト名を取得
}
	 
// オブジェクトが触れている間
void OnCollisionStay(Collision collision) {
	Debug.Log ("Stay: " + collision.gameObject.name); // 触れているオブジェクト名を取得
}

 実行すると、以下のようになります。
f:id:rikoubou:20180621225458g:plain

 Sphereが回転しているのは反発係数などがデフォルトで設定されているためです。

 これを防ぐためには、以下のように「Physic Material」を作成します。
f:id:rikoubou:20180621225800p:plain

 名前はなんでもよいですが「NoPhysic」という名前にし、各種値全て0にします。
f:id:rikoubou:20180621230051p:plain

 衝突対象の全てのオブジェクト(ここではSphereとfloorとWallの全部)に「NoPhysic」をドラッグ&ドロップして適用させます。
f:id:rikoubou:20180621230422p:plain

 この状態で実行すると以下のようになります。
f:id:rikoubou:20180621231209g:plain

 PlaneにSphereが接しているため最後は無重力のようになってしまっています。

 Sphereを接していない部分まで高さを与えてやると以下のように反発することなく止まるようになります。
f:id:rikoubou:20180621231333g:plain


2:オブジェクト同士の重なり判定
 オブジェクト同士の重なり判定の場合の説明です。

 実際に存在しないものの当たり判定(ゴールエリアなど実際には存在しない範囲などの当たり判定など)に使用します。

 実際に存在しないものとして扱われるので、オブジェクトがあってもすり抜けます。

 0までの準備が済んだ状態で以下のようにColliderの「Is Trigger」にチェックを入れれば使用できます。
f:id:rikoubou:20180621224620p:plain

※このチェックが入っているか入っていないかでオブジェクトの衝突判定になるか、重なり判定になるかの切り替えになります。

 重なる自身のオブジェクトのスクリプト(ここではSphereに適用したPlayerControllerスクリプト)にある以下の記述で、それぞれの場合において重なった先のオブジェクトを取得することができます。

// オブジェクトが重なったとき
void OnTriggerEnter(Collider other) {
	Debug.Log ("Trigger Enter: " + other.gameObject.name);
}

// オブジェクトが離れた時
void OnTriggerExit(Collider other) {
	Debug.Log ("Trigger Exit: " + other.gameObject.name);
}

// オブジェクトが重なっている間
void OnTriggerStay(Collider other) {
	Debug.Log ("Trigger Stay: " + other.gameObject.name);
}

 実行すると、以下のようにオブジェクトをすり抜けて移動します。
f:id:rikoubou:20180621224504g:plain


 以上がオブジェクト同士の衝突判定と重なり判定です。

 基本的なことですが、Playerの移動範囲の制限やゴール地点などに使うことになると思います。


・参考資料