unity入門　その5 　当たり判定処理

# 前回までのおさらい

その３では、フィールドの自動生成を行いました。

![test.gif](https://qiita-image-store.s3.amazonaws.com/0/98558/0151a947-9b9c-97a5-17e0-c236da04957c.gif)

その４では、ゲームには必須の画面遷移の方法を記述

続いて、物体同士の当たり判定を作っていきたいと思います。

## 本題に入る前にちょっと脱線。
過去３回で作ってた、サンプルからちょっと動きを変更しています。
床がグリグリ動くと、見にくくなってきたから、
ユーザのアクションに応じて、プレイヤーボールだけの
重力場を変更するようにしました。

こんな感じ。
![test.gif](https://qiita-image-store.s3.amazonaws.com/0/98558/d2bbe2cf-a9a1-7199-eed0-28d7ef703219.gif)

fieldにつけていたスクリプトをプレイヤーボールの方につけかえて、
下に書いたように、transform.rotationをphysics.gravityに変更しました。

```js:fieldControl.js
	// ターゲットの回転を実行
	transform.rotation = Quaternion.Slerp(transform.rotation, target,  Time.deltaTime * smooth);
```

```js:boalControl.js
// 画面は傾けないで球に与える重力方向を変更
	Physics.gravity = new Vector3(tiltAroundZ, 0, tiltAroundX);
```
これで、フィールドは動かずボールの重力場だけが更新されるので、
ボールが重力に応じて動くようになりました。

# 今回の本題
## 今回の目標
　ゲームを作る上ではやっぱり絶対に必要になってくると思われる
　当たり判定部分を書いていく

　やりたい事
・何か、ゴールオブジェクトを作って触れたらクリア！
・特定オブジェクトに触れたらワープするギミック
　
とかそういうのを作るために、ぶつかった判定を受け取る仕組みを見ていきます。

------------------------------------------
目標イメージ
ゴールオブジェクトとして、円筒の物体を生成し、
プレイヤー（球）が触れたら、数秒後タイトル画面に戻る。
ワープするギミックとしても参考にできるように、
ゴールオブジェクトに触れた瞬間、画面右下にワープするようにしてみた。

![test.gif](https://qiita-image-store.s3.amazonaws.com/0/98558/8130c5b8-8f65-ed32-0335-80b15220813a.gif)

------------------------------------------

# やるべき事　概要
 ・判定を行う同士のレイヤーを作成する
 ・どのレイヤーとどのレイヤーがぶつかるのかを設定する
 ・当たり判定のColliderを作成
 ・当たった後に、画面遷移などのイベント発行に必要なスクリプト作成


## 判定を行う同士のレイヤーを作成する
 今回は、ゴール用に作成するオブジェクトとプレイヤーの当たり判定を行うので、
　goalレイヤーと、playerレイヤーを作成します。

Edit -> Project Settings -> Tags and Layers
![スクリーンショット 2016-02-20 16.15.20.png](https://qiita-image-store.s3.amazonaws.com/0/98558/e959a524-f3a3-c694-98f2-86f100aa73f5.png)

inspectorにレイヤー設定画面が出てくるので、
User Layer 8,9にgoalとplayerを追加
![スクリーンショット 2016-02-20 16.16.12.png](https://qiita-image-store.s3.amazonaws.com/0/98558/4bc20335-da22-8c18-7932-b47cb67e53a8.png)

## どのレイヤーとどのレイヤーがぶつかるのかを設定する
Edit -> Project Settings -> Physics
![スクリーンショット 2016-02-20 16.32.46.png](https://qiita-image-store.s3.amazonaws.com/0/98558/94933f8f-d167-b960-a43a-396d64916d8d.png)

physicsManagerが表示されます。
この画面で、"どのレイヤー"と"どのレイヤー"が物理演算対象かを設定します。
Playerは球にする予定なので、
壁とgoalオブジェクトに対して当たり判定が必要です。
壁は、レイヤーはDefaultなので、
player -> [Default][goal]にチェックします。

goalは、動かないオブジェクトになるので、
球と当たり判定が分かればいいということで
goal -> [player]にチェックを入れます。

![スクリーンショット 2016-02-20 16.33.15.png](https://qiita-image-store.s3.amazonaws.com/0/98558/bfa2e5e6-1724-7a64-55f4-9e84c5245646.png)

---------------------
脱線
 例えば、playerとDefaultのチェックを外すと
壁の当たり判定がなくなり、素通りできるようになります。
この仕組みを使えば、隠し扉の仕組みも簡単に作れそうです。

壁がなかったことになりました。
![test.gif](https://qiita-image-store.s3.amazonaws.com/0/98558/9915c24f-deb9-bef9-dd77-cb717a96ec35.gif)

------------------------

## 当たり判定のColliderを作成

ゴール用のオブジェクトを作ります。
円筒のcylinderを使います。
![スクリーンショット 2016-02-20 16.43.42.png](https://qiita-image-store.s3.amazonaws.com/0/98558/3d22b898-62cd-c663-cac8-57872e3a1cd9.png)

オブジェクト名を[goal]に変更（任意）
![スクリーンショット 2016-02-20 16.44.41.png](https://qiita-image-store.s3.amazonaws.com/0/98558/bf19349d-3c51-9d5e-3392-fa9e1476cae3.png)


goalオブジェクトのレイヤーをgoalにします。
![スクリーンショット 2016-02-20 16.52.32.png](https://qiita-image-store.s3.amazonaws.com/0/98558/73339e04-d038-ac9f-4656-67915b373d0f.png)

今回のゴールオブジェクトはIs Triggerにします。

![スクリーンショット 2016-02-20 16.54.50.png](https://qiita-image-store.s3.amazonaws.com/0/98558/a2f436df-2b1b-bb31-ba5e-ac87b543ef94.png)
Capsule Collider の Is Triggerにチェック
物理演算処理は行わず、当たり判定のTriggerにだけ使うという記し。


goalをPrefab化してください。
![スクリーンショット 2016-02-20 17.02.36.png](https://qiita-image-store.s3.amazonaws.com/0/98558/88474647-bc8b-ffea-49d5-77316a5f916c.png)

球の方もデフォルトで、Sphere Colliderが入っているので、
そのままで問題ないです。

![スクリーンショット 2016-02-20 17.04.03.png](https://qiita-image-store.s3.amazonaws.com/0/98558/dca17b0a-c2e5-680b-e144-ed4574a6a4bf.png)
前に作った、球のPrefabsを選択して、Inspectorを確認

## 当たった後に、画面遷移などのイベント発行に必要なスクリプト作成

最後に、ぶつかった時のイベントをスクリプトで受け取る方法
３つのオブジェクト間でやり取りを行う方法を記載します。

今回作成のメッセージのやり取り概要

---------------------
プレイヤーがゴールオブジェクトに衝突
↓
ゴールオブジェクトが衝突イベント取得
↓
ゲーム監視用スクリプトにクリアを通知（ゴールギミック）
↓
プレイヤーを特定の場所へ移動するイベントを通知（ワープギミック）

----------------------

最初にも書いた、ゴール用とワープをいっぺんに書きます。
ゴールオブジェクトに触れたら、
ゲームクリアとして、一定時間後にタイトルに戻り、かつ
適当な場所にボールを飛ばす

空のGameObjectに設定

```csharp:GameCtrl.cs
using UnityEngine;
using System.Collections;

public class GameCtrl : MonoBehaviour {
	// 20秒以内にゴールにつけない場合ゲームオーバー
	public float deadlinetime = 20.0f * 60.0f;
	// ゲームオーバーフラグ
	public bool gameOver = false;
	// ゲームクリア
    public bool gameClear = false;
	//
	public float sceneDelay = 3.0f;

	void Update()
	{
		// ゲーム終了条件成立後、シーン遷移
		if( gameOver || gameClear ){
			sceneDelay -= Time.deltaTime;
			if( sceneDelay <= 0.0f ){
				Application.LoadLevel("TitleScene");
			}
			return;
		}
		//前回フレームからの時間差を取得
		deadlinetime -= Time.deltaTime;
		// 残り時間が無くなったらゲームオーバー
		if(deadlinetime<= 0.0f ){
			GameOver();
		}

	}

	// ゲームオーバー
	public void GameOver()
	{
		gameOver = true;
	}

	// ゲームクリア
	// Goalオブジェクトからイベントメッセージとして
	// 呼び出してもらうためのメソッド
	public void GameClear()
	{
		gameClear = true;
	}
}
```
![スクリーンショット 2016-02-20 17.21.02.png](https://qiita-image-store.s3.amazonaws.com/0/98558/aea78943-2ae4-cfe9-4f36-0e6a358a9fa6.png)

プレイヤーにつけるオブジェクト
重力場を捜査している元々つけてたスクリプトのどっかに
以下のメソッドを追加
ポジションを無理やり、(10,-10)に変えます。

```js:boalControl.js
// goalレイヤーの物体と衝突した時
function Goal(){
	this.transform.position.x = 10;
	this.transform.position.z = -10;
}
```



ゴールオブジェクトに付けるスクリプト
衝突イベントは以下のイベントで受け取ります。

```csharp:Csharp
// 衝突した瞬間
void OnTriggerEnter(Collider other) {
}
// 衝突している間
void OnTriggerStay(Collider other) {
}
// 衝突から離れた瞬間
void OnTriggerExit(Collider other) {
}
```


```csharp:goalScript.cs
using UnityEngine;
using System.Collections;

public class goalScript : MonoBehaviour {
	GameCtrl gameRuleCtrl;
	
	// 衝突した瞬間に呼ばれる
	void OnTriggerEnter(Collider other) {
		// GameCtrlクラスを探して、取得する
		gameRuleCtrl = GameObject.FindObjectOfType(typeof(GameCtrl)) as GameCtrl;
		// GameCtrlクラスのGameClearメソッドを呼ぶ
		gameRuleCtrl.GameClear ();

		// 衝突してきた物体のGoalメソッドを呼ぶ
		// boalControl.jsに追加
		other.SendMessage ("Goal");
	}
}
```


今更だけど、
C#に統一すればよかったです。。。


コードベースの動作の概要

Layer(player)がLayer(goal)に当たった時
goalScript:: OnTriggerEnter　が発行される。

その中で、以下の２つの処理を実行
- 「GameCtrl」クラスを探索して、そのクラスのGameClearメソッドを呼ぶ
- ゴールオブジェクトに衝突したオブジェクトが持っているGoal()を呼ぶ

GameCtrlはGameclearの状態が一定期間続いた場合、
TitleSceneへ画面遷移
衝突した球にAddされているboalControl.jsのGoalメソッドが呼ばれて、
ポジションが移動する。


![test.gif](https://qiita-image-store.s3.amazonaws.com/0/98558/6b8b6bd4-d5cb-b6db-8be0-2a9824829548.gif)

こんな感じになりました。
ゴールオブジェクトに触れると、右下に飛ばされて、
ちょっと経過したらタイトルに戻る。

-------------------------------

追加
[unity入門　その３](http://qiita.com/_Nanagin/items/10bb71f4ae3ca57dc4c0)で作成した、自動フィールド生成のところで、
FileReadTestの中に[g]を作って、
creatStageを拡張する。
![スクリーンショット 2016-02-20 19.50.41.png](https://qiita-image-store.s3.amazonaws.com/0/98558/fd6f3e15-e389-0877-08af-59c18ff229e1.png)
Goalの定義に作成したゴールオブジェクトのPrefabsを登録すれば、
ゴールオブジェクトも自動生成に対応。

```sharp:creatStage.cs
public class creatStage : MonoBehaviour {

	-- 略 --
	// ゴールオブジェクトの定義
	public GameObject goal;
	-- 略 --

void CreateStage(Vector3 pos){
Vector3 originPos = pos;
		string stageTextData = textAsset.text;
		pos.y = 0;

		foreach(char c in stageTextData){

			GameObject obj = null;

			if(c == '#'){
				obj = Instantiate(block, pos, Quaternion.identity) as GameObject;
				obj.name = block.name;
				pos.x += obj.transform.lossyScale.x;

				obj.transform.parent = parents.transform;

			--　略 --
			}else if(c == 'g'){
				// [g]の時はゴールオブジェクトを生成
				obj = Instantiate(goal, pos, Quaternion.identity) as GameObject;
				obj.name = goal.name;
				pos.x += spaceScale.x;

				obj.transform.parent = parents.transform;
			-- 略 --
			}
		}
	}
}



```