Toolkit for CreateJSでドラクエ風バトルシーン(簡易)を作ってみる

1ft-seabass.jp.MEMO – Edge Animateでドラクエ風バトルシーン(簡易)を作ってみるに引き続き、Toolkit for CreateJSでも、ドラクエ風バトルシーン(簡易)を作ってみました。

なるべく、要件定義は同じで、Edgeと似たような実装をしてみます。

できあがったもの

ということで、概ね1日程度で出来上がり。Edge Animateで設計を整えていたので見通しやすかった。また、手慣れたFLASHアプリケーションを使って制作ができたので、素早く出来ました。

別ページで開く 

作業メモ

  • フレームレートは30FPS
    • 後発で作ったので、やや動きにキレが出てしまったかも。
  • オブジェクト構造は、Edgeに近づけたものにしています。
    • FLASHオーサリングならではのオブジェクト構造はあるかもしれませんが、今回はなし。
  • スマートフォンでの動作
    • Android2.3、Android4.0以上、iPhone4以上(手持ちの実機ではiPodTouch iOS5.1想定)でちゃんと動いた。
  • フレームレートベースなので細かな動きがちゃんと表現される
    • Edge Animateでは機種の再生速度に依存していた一瞬の表現が比較的しっかり再生される
      • 最初のオープニングのあたりが特に
  • 再生がおかしくなるとき、アラートがFlashほど親切ではない。
    • これはCreateJSも同様。
    • Chromeデベロッパーツールでのコンソールでのエラーで追うのがやりやすかった
    • /* js / 内でJSのコメントアウト / */をしてしまうとエラーになるのが困った。
  • やはりFLASHでのオーサリングは強力
    • ムービークリップのシンボル化が楽にできるのはとてもやりやすい
    • ドットシンタックスで階層が追えるのでありがたい
  • わりとハマった制約
    • クラシックトゥイーンのレイヤーはインスタンスを1つしか持てないのが割とハマる
      • 敵パーツ配置時などのうっかり同レイヤーに2つのインスタンスを置いてしまったことがあった。
  • 通常のSWFと同じ感覚でスクリプトを書くと、JavaScriptの値空間の挙動でところどころハマる
    • これはもう慣れるしかない
    • イベント内はe.targetで辿ったり、グローバルの値はstage.に紐付けたりすると、それなりに追いやすくなる。
  • EaselJS v0.5.0 API Documentation まじ必須。
  • Toolkitで吐き出されるTweenJSのチェーンスコープは、トゥイーンを再現したものだが、気軽にいじって処理を加えられそうにない。

TIPS

初期化処理

メインタイムラインの1フレーム目に初期化処理を書きました。

実際には/* js */で囲われています。

// バトルデータ
// htmlからbattleDataとしてくる。
// ターン数
stage.turnNum = 0;  // 全体で使うので var しない。
// 連打防止フラグ
stage.isAnimate = false;  // 全体で使うので var しない。
// ステータス反映
for( var i = 0 ; i < 3 ; i++ ){
	var currentStatus = battleData.startStatus[ i ];
	var statusElement = this.TopIndex[ "OwnStatus" + i ];
	statusElement.NameText.text = currentStatus.Name;
	statusElement.HPText.text = currentStatus.HP;
	statusElement.SPText.text = currentStatus.SP;
	statusElement.LVText.text = currentStatus.LV;
}
// 初回処理 //////////////////////////////////////////////////
var currentTurn = battleData.turn[ stage.turnNum ];
// メッセージ反映
this.TopIndex.Message.text = currentTurn.message;
// ターンのカウントを増やす
stage.turnNum++;

クリックイベント

また、onClickも記述することができるので、1フレーム目にクリックアクションも記述しました。

// クリックイベントの設定(view全体に)
this.TopIndex.onClick = function( e ){
	if( stage.isAnimate ){
		// アニメ中は動作しない。
	} else {
		// 現在のターン
		var currentTurn = battleData.turn[ stage.turnNum ];
		var nextTurn = battleData.turn[ stage.turnNum + 1 ];
		// modeによって表示分岐
		if( currentTurn.mode == "attack" ) {
			e.target.Message.text = stage.multiLineConvert( currentTurn.message );
			e.target[ "Enemy" + currentTurn.effect.eid ].gotoAndPlay( "defence" );			
			e.target[ "Slash" + currentTurn.effect.eid ].gotoAndPlay( "attack" );
			e.target.parent.gotoAndPlay( "attack" );
			// アニメ中フラグ ON
			stage.isAnimate = true;
		} else if( currentTurn.mode == "defence" ) {
			e.target.Message.text = stage.multiLineConvert( currentTurn.message );
			e.target[ "Enemy" + currentTurn.effect.eid ].gotoAndPlay( "attack" );		
			e.target.parent.gotoAndPlay( "defence" );
			// ダメージ反映
			var currentStatus = battleData.startStatus[ currentTurn.effect.id ];
			var statusElement = e.target[ "OwnStatus" + currentTurn.effect.id ];
			var afterHP = currentStatus.HP - currentTurn.effect.damage;
			statusElement.HPText.text = afterHP;
			// アニメ中フラグ ON
			stage.isAnimate = true;
		} else if( currentTurn.mode == "finish" ) {
			// アニメ中フラグ ON
			stage.isAnimate = true;
			// URLジャンプ
			window.open("createjs_battle_sample_viewport.html", "_self");
		}
	
		// ターンのカウントを増やす
		stage.turnNum++;
	}
}

Edge Animateとは記述は若干違えど、基本同じような命令で書かれています。onClick内からTopIndexを示すe.targetより内部のオブジェクトたどって、ドットで階層が追えるのがいいですね。

全体で使う値はstage.に集約させた

正規のやり方かどうかはわかりませんが、表示オブジェクト(DisplayObject)であればstageを持っているので、ターン数を管理するturnNumや連打防止用のisAnimateは全体で使う値ので、stage.に集約させました。

ソースがパブリッシュごとに上書きされてしまうので~_viewport.htmlで分離

Toolkit for CreateJSはソースがパブリッシュごとに上書きされてしまうのでEdgeでやっていたような、viewportやcssの追記がやりにくかった。

そのため、元ネタcreatejs_battle_sample.htmlをコピーして、viewportやcssの追記用の~_viewport.htmlで分離した。閲覧・検証時はこちらを見ています。

データのやり取りはcreatejs_battle_sample_viewport.htmlのJavaScriptから

createjs_battle_sample_viewport.htmlをみていただくと、データのやり取りはここのJavaScriptから行なっています。

Edge Animateとの違いは、メッセージテキスト用の文字列の改行がBRでなく\nになっています。

<script>
battleData = {
	startStatus:[
		{ Name:"主人公" , HP:500 , SP:50 , LV:20 },
		{ Name:"仲間A" , HP:400 , SP:80 , LV:22 },
		{ Name:"仲間B" , HP:200 , SP:200 , LV:18 }
	]
	,
	turn:[
		{ mode:"info" , message:"ウィスプ 3ひきがあらわれた!\n\n →つぎへ" },
		{ mode:"attack" , message:"主人公のこうげき!ウィスプAに30のダメージをあたえた!" , effect:{ eid:0 , damage:30 } },
		{ mode:"attack" , message:"仲間Aのこうげき!ウィスプBに25のダメージをあたえた!" , effect:{ eid:1 , damage:25 } },
		{ mode:"attack" , message:"仲間Bのこうげき!ウィスプCに8のダメージをあたえた!" , effect:{ eid:2 , damage:18 } },
		{ mode:"defence" , message:"ウィスプAのこうげき!主人公は5のダメージをうけた!" , effect:{ eid:0 , id:0 , damage:5 } },
		{ mode:"defence" , message:"ウィスプBのこうげき!主人公は12のダメージをうけた!" , effect:{ eid:1 , id:0 , damage:12 } },
		{ mode:"defence" , message:"ウィスプCのこうげき!仲間Bは8のダメージをうけた!" , effect:{ eid:2 , id:2 , damage:8 } },
		{ mode:"attack" , message:"主人公のこうげき!ウィスプAに28のダメージをあたえた!\nウィスプAをたおした!!" , effect:{ eid:0 , damage:28 } , dead:{ eid:0 } },
		{ mode:"attack" , message:"仲間Aのこうげき!ウィスプBに20のダメージをあたえた!" , effect:{ eid:1 , damage:20 } },
		{ mode:"attack" , message:"仲間Bのこうげき!ウィスプBに18のダメージをあたえた!\nウィスプBをたおした!!" , effect:{ eid:1 , damage:18 } , dead:{ eid:1 } },
		{ mode:"defence" , message:"ウィスプCのこうげき!仲間Aは8のダメージをうけた!" , effect:{ eid:2 , id:1 , damage:8 } },
		{ mode:"attack" , message:"主人公のこうげき!ウィスプCに22のダメージをあたえた!" , effect:{ eid:2 , damage:22 } },
		{ mode:"attack" , message:"仲間Aのこうげき!ウィスプCに11のダメージをあたえた!" , effect:{ eid:2 , damage:11 } },
		{ mode:"attack" , message:"仲間Bのこうげき!ウィスプCに15のダメージをあたえた!\nウィスプCをたおした!!" , effect:{ eid:2 , damage:15 } , dead:{ eid:2 } },
		{ mode:"finish" , message:"ウィスプ 3ひきをたおした!\n\n →タッチするともう一度再生" }
	]
}
</script>

複数行のテキストフィールドが単一行になってしまうので調整

下部のメッセージの部分、複数行のテキストフィールドが単一行になってしまうので簡易的にですが一定文字数で折り返すように調整しました。本当はマルチバイトのときと半角英数字のときでカウントを区別したりしないといけないが、ひとまずはよし。正直、これがかなり手間で心が折れかけた。次回バージョンで改善されるといいな。

// 複数行時のテキスト整形
stage.multiLineConvert = function(str){
	var ret = "";
	var lineNum = 15;
	var count = 0;
	while( count < str.length - 1 ){
		var cutStr = str.substr(count,lineNum);
		var turnIndex = cutStr.indexOf("\n");
		if( turnIndex > -1 ){
			turnIndex += 1;
			ret += cutStr.substr(0,turnIndex);
			count += turnIndex;
		} else {
			ret += cutStr + "\n";
			count += lineNum;
		}
		
	}
	return ret;
}

うーん、テキスト周りはまだネックですね。

GoogleChromeで検証しているとローカルでのJS実行に制約

GoogleChromeで検証しているとローカルでのJS実行に制約があります。そのため、うまく動かないケースがあるので注意。

この回避方法は–allow-file-access-from-filesオプションを指定して起動したら問題なく動きました。いちいち、サーバーにアップして見たりは面倒なので、助かる。

参考:Google Chromeでは(Ajaxで)ローカルファイルにアクセスしようとするとエラーになることについて、他 – エンジニアのソフトウェア的愛情

おわりに

いかがでしたでしょうか。

Toolkit for ※1 CreateJSもまだまだ1.0にはなっておらず発展途上ですが、このレベルの仕様であれば受け止められそうな仕上がりになって来ました。しかしながら、テキスト周りはもう少し頑張ってくれたら!

今回のソースコードも以下においておきます。

Edge Animateデータダウンロード:createjs_battle_sample.zip

それでは、よき CreateJS Life を!

※1 Toolkit for CreateJSはExtensionとしてすでに1.0越えていますが、CreateJS単体ではまだ0.5あたりという状況なので修正しました。