UnityでHTTPでWeb上にあるステージデータを読み込んで表示する


はじめに
====

[前回](http://qiita.com/mokemokechicken/items/29676ee458f83424a217)の続きです。

今回は、ステージデータをWeb上にUploadしておいて、そこから読み込むようにしておきます。
こうすると、アプリケーション本体とは別にステージデータを増やすことができるようになります。

Web上のデータを取得する
====

どこかのURL (stageUrl) 以下に、ステージ番号別にテキストデータを配置するものとします。
とりあえず `<stageUrl>/0001.txt` というフォーマットとします。

StageModel.cs
---
最終的には、ステージデータが何個あるか、とかも管理したいので、その辺を扱うClassを`StageModel`として作ろうと思います。適切かどうかわかりませんが、とりあえず Singleton にしておきます。

Web上のデータを取得するには、 `WWW`という便利Classがあるのでそれを使います。
以下のようになりました。

```csharp:StageModel.cs
public class StageModel
{
	//// Singleton
	private static StageModel instance = new StageModel(Global.stageUrl);

	private StageModel(string baseUrl) {
		this.baseUrl = baseUrl;
	}

	static public StageModel GetInstance() {
		return instance;
	}

	private string baseUrl;

	public IEnumerator LoadStageData(int stageNum, Action<string> callback) {
		var url = String.Format("{0}/{1:D4}.txt", baseUrl, stageNum);
		var www = new WWW(url);
		Debug.Log(url);
		yield return www;
		callback(www.text);
	}
}
```

`LoadStageData` にStage番号とデータが取得出来た際のCallback Actionを指定する形にしておきます。

`yield return www` のように書くと非同期的な処理になって、 www が規定の処理（この場合はデータを取得する）が終わるまでこのメソッド内の処理の実行はStopして、終わったら続きから呼び出してくれます。その間別の処理は動くことができるので全体をBlockすることはありません。
また、戻り値の型を `IEnumerator` にしておく必要があります。

エラー処理とか何もしていませんが、おいおい追加しようと思います。


StageController.cs
-------

呼び出し側も少し変更する必要があります。

Unityでは、単に`LoadStageData`を呼ぶだけでなく、その戻り値を更に `StartCoroutine()` に渡す必要があります。
callback Action は `data => Build(data)` みたいに書けるので楽でいいですね。C#の好きなところです。

```csharp:Start()
		var model = StageModel.GetInstance();
		StartCoroutine(model.LoadStageData(Global.stage, data => Build(data)));
```

`Start()`から実際にStageを構築する部分は`Build(string)`メソッドとして分割しました。
変更点全体では以下のようになりました。

```csharp:StageController.cs
	void Start () {
		Global.stageControler = this;

		prefabMap = new Dictionary<char, GameObject> {
			{BLOCK, blockPrefab},
			{LADDER, ladderPrefab},
			{HIDDEN_LADDER, ladderPrefab},
			{BAR, barPrefab},
			{PLAYER, playerPrefab},
			{TREASURE, treasurePrefab},
			{GOAL, goalPrefab},
			{ENEMY, enemyPrefab},
		};

		var model = StageModel.GetInstance();
		StartCoroutine(model.LoadStageData(Global.stage, data => Build(data)));
	}

	void Build(string data) {
		BuildStage(data);
		
		if (playerObject) {
			// Camera
			cameraObject = (GameObject)Instantiate(cameraPrefab);
			var cameraScript = cameraObject.GetComponent<Camera>();
			cameraScript.target = playerObject.transform;
			
			// set target to EnemyControl
			foreach (var enemyObject in enemyObjects) {
				var enemyControl = enemyObject.GetComponent<EnemyControl>();
				enemyControl.target = playerObject.transform;
			}
		}
		
		Global.paused = false;
	}
```

Cross Domain問題への対応
======

今回StageDataをgithubにPushして管理したいと思って、最初は単に Githubの raw なDataを参照させようとしていました。
しかし、Editor内で実行してみると、データがLoadされてきません。

Consoleを見てみると、

`SecurityException: No valid crossdomain policy available to allow access`

![Stage_unity_-_RoadRunner_-_Web_Player.png](https://qiita-image-store.s3.amazonaws.com/0/11124/641e2e30-255d-6935-4570-60c6c13fc47e.png "Stage_unity_-_RoadRunner_-_Web_Player.png")

というエラーが出ています。

crossdomain.xml
---
調べてみると、http://docs.unity3d.com/Manual/SecuritySandbox.html に書いてありました。

WebPlayerでは Webplayerが配布されているDomainと同じではないURLに WWWクラスでアクセスした場合エラーになります。
これを回避するには、 `http://<Access Host>/crossdomain.xml` というファイルを置いて、

```xml:crossdomain.xml
<?xml version="1.0"?>
<cross-domain-policy>
    <allow-access-from domain="*" />
</cross-domain-policy>
```

のように書く必要があります。ここでは、任意のDomainからのアクセスを許可しています（緩い設定）。

また、Web PlayerがHTTPSで配布してあり、WWW ClassのアクセスURLがHTTPの場合、
`<allow-access-from domain="*" secure="false" />`
と`secure="false"`を追加する必要があります。 [参考](http://forum.unity3d.com/threads/123171-Crossdomain-xml-not-found-when-using-HTTPS)


データ置き場を変更
-------
このファイルは、 URLのルートディレクトリに置かないといけないのですが、
https://raw.githubusercontent.com/crossdomain.xml のようなURLにファイルを置くことはできません。

仕方がないので、Github Pagesを作って、そこからStageDataを配布するようにしました。
結局以下のようなURLで配布するようにします。

* crossdomain.xml: https://mokemokechicken.github.io/crossdomain.xml
* stage 1: https://mokemokechicken.github.io/UnityChanRoadRunnerStageData/0001.txt


Github Pagesの作り方は http://blogger.weblix.net/2014/01/github-pages-create-web-site.html などが参考になると思います。

おわりに
=====

思ったより手間が掛かりましたが、WebからデータをLoadできるようになりました。

* 今回のバージョンのWebPlayer版
	* https://dl.dropboxusercontent.com/u/21300511/UnityRoadRunner/demo1l/demo1l.html
* ソースコード
	* https://github.com/mokemokechicken/UnityChanRoadRunner/tree/demo1l