Unity4.6オープンβでチュートリアルのShoothing Game作ってみる その弐


##やる事
[前回](http://qiita.com/MrSmart/items/b7e4eeebd1435fe8861b "Qiita")で公式のチュートリアル"ゲーム制作編"は一通り終わったので、
今回はそれを改良しつつ[モバイル編](http://japan.unity3d.com/developer/document/tutorial/2d-shooting-game/mobile/ "Qiita")に移行する予定だったんですが、今回は寄り道回になります...

##モバイル編入る前に...
###Warning問題
今回もUnity4.6.Ob17を使用するんだけど、前回までのプロジェクトを再生するとwave2が出現した時に必ず

```
The referenced script on this Behaviour is missing!
UnityEngine.Object:Instantiate(Object, Vector3, Quaternion)
<Start>c__Iterator0:MoveNext() (at Assets/Scripts/Emitter.cs:34)
```

ってWarningが大量に表示るのが非常に気になったので、色々調べた結果、
どうやら[第12回](http://japan.unity3d.com/developer/document/tutorial/2d-shooting-game/game/12.html "Unity Tutorial")で入れたWave.unitypackageがプロジェクトと噛み合って無いようで、
Wave2を自分で作り直してみたところ、Wave2出現時のWarningが消えた。全Waveを自作すれば問題なくなりますが、全部のWave作り直しとなると時間かかりそう...

###Inspector入力の向上
スクリプトファイルのパラメータでアクセス修飾子をpublicにすると
Inspector上で数値を入力出来るようになるけど、
![スクリーンショット 2014-09-10 14.56.31.png](https://qiita-image-store.s3.amazonaws.com/0/45511/74824667-a7f0-3e09-2909-930130955bdc.png "スクリーンショット 2014-09-10 14.56.31.png")
なんか味気ないというか、これじゃどんな値も入れ放題で見ていて危なっかしいので、入力値に上限入れたり、選択方式にしたい。
調べた所、enumやPropertyDrawerという機能を使えば行けるっぽいので早速実装してみた。

```c#:Spaceship.cs
using UnityEngine;

// Rigidbody2Dコンポーネントを必須にする
[RequireComponent(typeof(Rigidbody2D))]
public abstract class Spaceship : MonoBehaviour
{
	// 移動スピード
	[Range(0.0f, 10.0f)]
	public float speed = 1.0f;
	
	// 弾を撃つ間隔
	[Range(0.0f,  1.0f)]
	public float shotDelay = 1.0f;
	
	// 弾のPrefab
	public GameObject bullet;
	
	// 爆発のPrefab
	public GameObject explosion;
	
	// 爆発の作成
	public void Explosion ()
	{
		Instantiate (explosion, transform.position, transform.rotation);
	}
	
	// 弾の作成
	public void Shot (Transform origin)
	{
		Instantiate (bullet, origin.position, origin.rotation);
	}

	protected abstract void Move (Vector2 direction);
}
```

```c#:Enemy.cs
using UnityEngine;
using System.Collections;

public class Enemy : Spaceship {

	// ヒットポイント
	[Range(10, 200)]
	public int hp = 10;

	// 弾を撃つかどうか
	public bool canShot;

	public enum EnemyScore
	{
		ENEMY_SCORE_WEAK = 100,
		ENEMY_SCORE_NORMAL = 200,
		ENEMY_SCORE_STRONG = 300,
		ENEMY_SCORE_BOSS = 2000
	}
	// スコアのポイント
	public EnemyScore score;

	IEnumerator Start ()
	{
		// ローカル座標のY軸のマイナス方向に移動する
		Move (transform.up * -1);
		
		// canShotがfalseの場合、ここでコルーチンを終了させる
		if (canShot == false) {
			yield break;
		}
		
		while (true) {
			
			// 子要素を全て取得する
			for (int i = 0; i < transform.childCount; i++) {
				
				Transform shotPosition = transform.GetChild (i);
				
				// ShotPositionの位置/角度で弾を撃つ
				Shot (shotPosition);
			}
			
			// shotDelay秒待つ
			yield return new WaitForSeconds (shotDelay);
		}
	}
	
	// 機体の移動
	protected override void Move (Vector2 direction)
	{
		rigidbody2D.velocity = direction * speed;
	}
	
	void OnTriggerEnter2D (Collider2D c)
	{
		// レイヤー名を取得
		string layerName = LayerMask.LayerToName (c.gameObject.layer);
		
		// レイヤー名がBullet (Player)以外の時は何も行わない
		if (layerName != "Bullet (Player)") return;
		
		// PlayerBulletのTransformを取得
		Transform playerBulletTransform = c.transform.parent;
		
		// Bulletコンポーネントを取得
		Bullet bullet =  playerBulletTransform.GetComponent<Bullet>();
		
		// ヒットポイントを減らす
		hp = hp - bullet.power;
		
		// 弾の削除
		Destroy(c.gameObject);
		
		// ヒットポイントが0以下であれば
		if(hp <= 0 )
		{
			// スコアコンポーネントを取得してポイントを追加
			FindObjectOfType<Score>().AddPoint((int)score);
			
			// 爆発
			Explosion ();
			
			// エネミーの削除
			Destroy (gameObject);
			
		}else{
			
			GetComponent<Animator> ().SetTrigger("Damage");
			
		}
	}
}
```

試しにSpaceshot.csとEnemy.csを修正したところ、こんな感じになった。

![スクリーンショット 2014-09-10 15.00.00.png](https://qiita-image-store.s3.amazonaws.com/0/45511/e89565c8-b722-99c9-1c65-096b195ee5f4.png "スクリーンショット 2014-09-10 15.00.00.png")
制限を入れたいpublicパラメータの直上に

```c#
[Range( /*最小値*/ , /*最大値*/ )]
```
を入れてあげればUnity側で自動的にスライダー入力形式に変換してくれる。便利！！
もう一つはenumで入力数値を制限するパターン

元々

```c#:Enemy.cs
	// スコアのポイント
	public int point = 100;
```

を

```c#:Enemy.cs
	public enum EnemyScore
	{
		ENEMY_SCORE_WEAK = 100,
		ENEMY_SCORE_NORMAL = 200,
		ENEMY_SCORE_STRONG = 300,
		ENEMY_SCORE_BOSS = 2000
	}
	// スコアのポイント
	public EnemyScore score;
```

と書き換えた。こうすると、scoreの中には100,200,300,2000の何れかの値しか入力出来なくなるので、
入力値を複数固定値で管理したい場合は便利！！

![スクリーンショット 2014-09-10 15.15.45.png](https://qiita-image-store.s3.amazonaws.com/0/45511/c8d1e533-3b29-f3e0-f264-56d15760cdab.png "スクリーンショット 2014-09-10 15.15.45.png")

##Tips
###ネットワーク通信
これはチュートリアルとは全く関係ないですが、
やはりここは押さえておきたかったので、localhost接続で色々試してみました。

現在時刻を取得してログに出力するスクリプトをMain Cameraに設置してみた

```c#:Initialize.cs
using UnityEngine;
using System.Collections;

public class Initialize : MonoBehaviour {

	// Use this for initialization
	void Start () {
		StartCoroutine (Connect("http://localhost/"));
	}
	
	// Update is called once per frame
	void Update () {
	
	}

	private IEnumerator Connect(string url) {
		WWW www = new WWW (url);
		yield return www;
		if (www.error == null) {
			Debug.Log("Success : " + www.text);
		} else {
			Debug.Log("Failure : " + www.error);
		}
	}

}
```

...


```
WWW Error: Rejected because no crossdomain.xml policy file was found
UnityEngine.Debug:Log(Object)
<WaitForRequest>c__Iterator2:MoveNext() (at Assets/Scripts/Initialize.cs:39)
```
crossdomain.xmlってナニ( ´・д・)？

という事で調べた結果、ネットワーク接続するには
**Edit > Project Settings > Editor**
にある
![スクリーンショット 2014-09-10 18.29.02.png](https://qiita-image-store.s3.amazonaws.com/0/45511/72d9290d-06f6-9ae4-cab4-b8968bc5201b.png "スクリーンショット 2014-09-10 18.29.02.png")
を接続先のドメインに変えてあげないといけないらしい。
この値をhttp://localhost/と変更したら...
![スクリーンショット 2014-09-10 19.07.37.png](https://qiita-image-store.s3.amazonaws.com/0/45511/1e61bc4d-a827-56d7-d2ff-4b4089c5864f.png "スクリーンショット 2014-09-10 19.07.37.png")
ちゃんと取れました。

...なぜあんなところに...