はじめに
前回の続きです。
今回は、ステージデータをWeb上にUploadしておいて、そこから読み込むようにしておきます。
こうすると、アプリケーション本体とは別にステージデータを増やすことができるようになります。
Web上のデータを取得する
どこかのURL (stageUrl) 以下に、ステージ番号別にテキストデータを配置するものとします。
とりあえず <stageUrl>/0001.txt
というフォーマットとします。
StageModel.cs
最終的には、ステージデータが何個あるか、とかも管理したいので、その辺を扱うClassをStageModel
として作ろうと思います。適切かどうかわかりませんが、とりあえず Singleton にしておきます。
Web上のデータを取得するには、 WWW
という便利Classがあるのでそれを使います。
以下のようになりました。
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#の好きなところです。
var model = StageModel.GetInstance();
StartCoroutine(model.LoadStageData(Global.stage, data => Build(data)));
Start()
から実際にStageを構築する部分はBuild(string)
メソッドとして分割しました。
変更点全体では以下のようになりました。
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
というエラーが出ています。
crossdomain.xml
調べてみると、http://docs.unity3d.com/Manual/SecuritySandbox.html に書いてありました。
WebPlayerでは Webplayerが配布されているDomainと同じではないURLに WWWクラスでアクセスした場合エラーになります。
これを回避するには、 http://<Access Host>/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"
を追加する必要があります。 参考
データ置き場を変更
このファイルは、 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できるようになりました。