【Unity】ブロックをスクリプトで配置：「はじめてのUnity」のブロック崩しを改造しながら学ぶ

# はじめに
JunShimuraさんの「[[超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で](http://qiita.com/JunShimura/items/cbb0db8087a5cc75735e) ::(1)ステージ配置～(6)ブロックを手作業で並べる)」をよりゲームらしく改造する。

作成する記事は下記の通りです。

 * [スコアの表示](http://qiita.com/yaju/items/615b31e9f17e310642d6)
 * [ゲームマネージャークラスを作る](http://qiita.com/yaju/items/5a3b46104f4f0a767c7f)
 * ブロックをスクリプトで配置(この記事)
 * [音を鳴らす](http://qiita.com/yaju/items/32a6960a2e61267f8731)

# 前回の状態
前回の記事「[ゲームマネージャークラスを作る](http://qiita.com/yaju/items/5a3b46104f4f0a767c7f)」では、ゲームクリアー後にブロックが全て消えた状態でゲームスタートとなってしまいました。これはブロックの初期化(並べる)処理が入っていないためです。
何故かというとブロックが全て消えてしまうため、ブロックを複製することが出来なく無くなってしまうからです。

ブロックを複製するためには、ブロックのプレハブ(Prefab)化を行います。

# プレハブ化とは
プレハブ(Prefab)とはオブジェクトの作り置きが出来る仕組みのことです。テンプレートとして使用できます。

## BlockをPrefab化する
現在、Hierarchyに`Block`を複数ある方は、1つのみ残して削除してください。
不要な`Block`を複数選択するには`Shift`キーを押しながら`Block`を選択します。その後に右クリックメニューから`Delete`を選択します。

Hierarchyから、`Block`をAssetsにドラッグすると、`Block`プレハブが出来上がります。
![Unity_PrefabAdd.gif](https://qiita-image-store.s3.amazonaws.com/0/14053/993631d5-4acb-12cc-633a-5759457e1c0b.gif)

**`Block`プレハブができたら、Hierarchyの`Block`を選択して右クリックメニューから`Delete`で削除してください。**

Prefabのメリットなどは下記記事を参照してください。
[PrefabとInstantiateの基本（1）ブロックをScriptで配置：「はじめてのUnity」のブロック崩しを改造しながら学ぶ](http://qiita.com/JunShimura/items/7e45fc6236cf97914041)

## Blockの色を変える準備
白一色だったブロックですが、複数の色のブロックにして色ごとに得点を分けるようにしたいと思っています。

「[[超初心者向け]Unityチュートリアル「はじめてのUnity」のブロック崩しと同等をC#で::(2)色を変える](http://qiita.com/JunShimura/items/1b2b4842e983022075de)」で壁の色を変える際に、`Wall Material`を作成しました。
今回はブロックの色を変える`Block Material`を作成します。

下に在るAssetsの左にある[Create]>[Material]をクリックして下さい。
名前を`Block Material`に変えます。
名前を間違えた場合、名前のところをクリックすれば編集できるようになります。
![Unity_MaterialAdd.gif](https://qiita-image-store.s3.amazonaws.com/0/14053/53991b3f-8574-fdc7-5d4c-5682d4b28bf1.gif)

Assetsにある`Block`を選択して、`Inspector`を表示します。ちなみにプレハブ化された場合、水色の箱アイコンになります。
次に`Materials`の`Element 0`のところにある○をクリックし、開いたリスト画面から`Block Material`を選択します。
![Unity_BlockMaterial.png](https://qiita-image-store.s3.amazonaws.com/0/14053/3ee9eb0f-4ed8-da14-95fd-5cb4056423c6.png)

色はスクリプトによって複数の色に変更するので、ここでは何もしません。

# スクリプトの作成
先に空のゲームオブジェクトを作成します。
Hierarchyに下にある[Create]>[Create Empty]を選択して下さい。
![Unity_EmptyAdd.png](https://qiita-image-store.s3.amazonaws.com/0/14053/dd60a5f8-0d4e-f932-b70f-d177f08a6920.png)

名前を`BlockMap`とします。`Position`はXYZを`0`にしておきます。
![Unity_BlockMap.png](https://qiita-image-store.s3.amazonaws.com/0/14053/fb6d0439-e398-b381-1f32-89f986608d38.png)

`BlockMap`の[Inspector]タブの一番下にある[Add Compornent]ボタンをクリックして、Scriptアセットを作成、追加します。ファイル形式はcsを選びます。
![Unity_BlockMapScriptAdd.png](https://qiita-image-store.s3.amazonaws.com/0/14053/2c6eb48e-a9fe-9467-2231-717ba183ed61.png)

ファイル名を`BlockMap`とします。そうすると、Assetsの中に「BlockMap.cs」が出来ています。
※BとMは英大文字です。間違えた場合、ファイル名と中身のクラス名の両方を修正してください。
![Unity_BlockMapScript.png](https://qiita-image-store.s3.amazonaws.com/0/14053/8f66219f-25e8-c442-745a-c2c76f4181c4.png)

## BlockMap.csを編集する
Assets内の`BlockMap`スクリプト(C#のアイコン）をダブルクリックすると、エディタが開きます。
優先的に開かれるエディタは、多くの場合はMonoDevelopというソフトが起動されます。

少し長いのでコピー＆ペーストで入力してしまいましょう。
※MonoDevelopでペーストが効かないに時はMonoDevelopを閉じ直してください。

``` chsarp:BlockMap.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BlockMap : MonoBehaviour {

	public GameObject blockPrefab;
	public Material material;

	Color[] itemColor = { Color.clear, Color.red, Color.blue, Color.green, Color.yellow, Color.magenta };

	// 0:None 1:Red 2:Blue 3:Green 4:Yellow 5:Magenta
	int[,,] stageMap = {
		{
			{ 3, 3, 3, 3, 3},
			{ 2, 0, 2, 0, 2},
			{ 1, 1, 0, 1, 1},
			{ 0, 0, 0, 0, 0}
		},
		{
			{ 4, 4, 0, 4, 4},
			{ 3, 0, 3, 0, 3},
			{ 2, 2, 2, 2, 2},
			{ 0, 1, 1, 1, 0},
		},
		{
			{ 4, 5, 4, 5, 4},
			{ 5, 3, 3, 3, 5},
			{ 2, 5, 2, 5, 2},
			{ 1, 1, 5, 1, 1},
		}
	};

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		
	}

	// ステージ最大数
	public int GetStageMax(){
		return stageMap.GetLength(0);
	}

	// ステージ作成
	public void CreateStage(int stage){
		var parent = this.transform;

		// 配置する座標を設定
		Vector3 placePosition = new Vector3(-4,0,9);
		// 初期化する座標を設定
		Vector3 initPosition = placePosition;

		// 配置する回転角を設定
		Quaternion q = new Quaternion();
		q = Quaternion.identity;

		// ブロック全削除
		var clones = GameObject.FindGameObjectsWithTag ("Block");
		foreach (var clone in clones){
			Destroy(clone);
		}

		// 配置
		for (int i = 0; i < stageMap.GetLength(1); i++) {
			placePosition.x = initPosition.x;
			for (int j = 0; j < stageMap.GetLength(2); j++) {
				int item = stageMap[stage-1, i, j];
				if(item != 0){
					// ブロックの複製
					GameObject block = Instantiate(blockPrefab, placePosition, q, parent);
					// ステージのブロック番号により色変更
					Renderer r = block.GetComponent<Renderer>();
					r.material.color = itemColor[item];
					// ステージのブロック番号で名前を生成
					block.name = "Block_" + item.ToString();
				}
				placePosition.x += blockPrefab.transform.localScale.x;
			}
			placePosition.z -= blockPrefab.transform.localScale.z * 2;
		}
	}
}
```

少し説明します。

* 3次元配列を用意し、ステージごとに2次元配列(5x4)をブロック番号をセットしています。
* ステージマップのブロック番号が1以上ならブロックのプレハブを複製し、各番号の色を変更しています。
* 名前にブロック番号を付与して、スコア処理で番号の判定をします。

## Block PrefabとMaterialを設定する
スクリプト内でpublicにした型はInspectorに表示されます。
ここにBlockとBlock Materialをセットします。
![unity_BlockMapScript.png](https://qiita-image-store.s3.amazonaws.com/0/14053/2f586c4f-4bea-74d1-cdb7-391d7db924a5.png)

Block Prefabの右横にある○をクリックし、開いたリスト画面から`Block`を選択します。
![unity_BlockPrefabSelect.png](https://qiita-image-store.s3.amazonaws.com/0/14053/cbae0fef-416d-66b7-7116-da3c48cdf01f.png)
Materialの右横にある○をクリックし、開いたリスト画面から`Block Material`を選択します。
![unity_BlockMaterialSelect.png](https://qiita-image-store.s3.amazonaws.com/0/14053/2ae33130-63d6-98d5-ae60-5ee1e95823fa.png)


# Manager.csを編集する
`GameOpening`処理には、ステージ作成の処理を追加しています。
`GameClear`処理には、3ステージまではステージクリアとして処理を追加しています。

``` chsarp:Manager.cs
	// オープニング処理
	void GameOpening ()
	{
		currentState = GameState.Opening;

		// タイトル名のセット
		SetTitle ("Game Start", Color.green);

		// ボールの動作停止
		Time.timeScale = 0;

		// ラケットの初期位置をセット
		racket.transform.position = new Vector3 (0.0f, 0.0f, -8.5f);
		// ボールの初期位置をセット
		ball.transform.position = new Vector3 (0.0f, 0.0f, -7.5f);

		// ステージ作成
		FindObjectOfType<BlockMap>().CreateStage(stage);
	}

	// ゲームクリアー処理
	void GameClear ()
	{
		// ステージ最大数の取得
		int stageMax = FindObjectOfType<BlockMap> ().GetStageMax();

		// 次のステージへ
		stage++;
		if (stage > stageMax) {
			// タイトル名のセット
			SetTitle ("Game Clear", Color.yellow);
			// ステージ初期値
			stage = 1;
		} 
		else {
			// タイトル名のセット
			SetTitle (string.Format("Stage {0} Clear", stage -1) , Color.yellow);
		}
		//3秒後にオープニング処理を呼び出す
		Invoke ("GameOpening", 3f);
	}
```

# Block.csを編集する
これまで一律`10`点だった得点をブロックの色ごとに得点を分けるようにします。
ブロックの名前の末尾を取得して、`10`倍したのを得点とします。

``` chsarp:Block.cs
	void OnCollisionEnter(Collision collision) {
		//衝突判定
		if (collision.gameObject.tag == "Ball") {
			if (FindObjectOfType<Manager> ().currentState == Manager.GameState.Playing) {
				//スコア処理を追加
				string name = this.name;
				int score = int.Parse (name.Substring (name.Length - 1));
				FindObjectOfType<Score> ().AddPoint (score * 10);
			}

			//相手のタグがBallならば、自分を消す
			Destroy(this.gameObject);
		}
	}
```

# 改善
テスト用に一時的に、`Ball.cs`のゲームオーバー判定をコメントアウト。

## ボールが壁を突き抜ける
たまにボールが壁を突き抜けてしまうことが判明、下記の記事を参考にボールにも適用してみます。
[衝突判定の再考、壁抜けを止める：「はじめてのUnity」のブロック崩しを改造しながら学ぶ](http://qiita.com/JunShimura/items/942c97d9c133e56f71a7)

`Ball`を選択し、[Inspector]タブで`Rigidbody`の`Collision Detection`を`Continuos`に変更します。
![Unity_BallCollision.png](https://qiita-image-store.s3.amazonaws.com/0/14053/1c4424d1-c68b-ac9a-e656-dacd11897b9c.png)

## ラケットの形状を変更
ボールが垂直方向のままで方向変更ができないことがあるます。
これはラケットが`Cube`になっているので、末端を丸みがあるように`Capsule`に変更します。
`Mesh`のところにある○をクリックし、開いたリスト画面から`Capsule`を選択します。
![Unity_RacketShape.png](https://qiita-image-store.s3.amazonaws.com/0/14053/e3925371-e26f-8401-d687-ffdfa9ebd220.png)

このままだと`Box Collider`のままなので、`Capsule Collider`に変更します。
一旦、`Box Collider`コンポーネントを削除します。
`Box Collider`コンポーネントの右横にある歯車を`Remove Compornent`を選択します。
次に[Inspector]タブの一番下にある[Add Compornent]ボタンをクリックして、[Physics]の[Capsule Collider]を追加します。
![Unity_RacketCollider.gif](https://qiita-image-store.s3.amazonaws.com/0/14053/667e01fd-3631-f4b8-26fe-b17794b1d577.gif)

`Height`を`1`、`Direction`を`X-Axis`にします。
![Unity_RacketCollider2.png](https://qiita-image-store.s3.amazonaws.com/0/14053/0aa823dd-89e2-dd4d-aa1c-6d2f98c33bc9.png)

## ラケットのサイズを変更
ついでにラケットのサイズを大きくします。`Scale`の`X`を`5`にします。
![Unity_RacketScaleX.png](https://qiita-image-store.s3.amazonaws.com/0/14053/159f75cd-7d5c-571b-d32e-08cf77163f81.png)

# 実装を確認する
エディタ画面中央の実行ボタンを押してみましょう。
オープニング画面でスペースキーで押すとゲームスタートとなります。
ブロックが全て無くなるとステージクリアーになり、次のステージとなります。
現在は、3ステージまでしか用意していません。
![Unity_GameStart3.png](https://qiita-image-store.s3.amazonaws.com/0/14053/5de3b5b1-ec55-3ae5-d03f-f7e880275611.png)

# 学んだこと
* プレハブ化することでテンプレートとして使用できること
* スクリプトによりブロックを自由に配置できること
* スクリプトによりブロックの色を変更できること

# 参照
* [UnityとBlenderハンズオン第7章](https://www.slideshare.net/yaju88/unityblender7)
* [UnityとBlenderハンズオン第8章](https://www.slideshare.net/yaju88/unityblender8)
* [UnityとBlenderハンズオン第9章](https://www.slideshare.net/yaju88/unityblender9)
* [UnityとBlenderハンズオン第10章](https://www.slideshare.net/yaju88/unityblender10-55356833)