Haxe + CreateJS 開発メモ#2 Toolkit for CreateJS連携

前回、Haxe で CreateJS を使用する環境を作りました。引き続き今回は、Flash で作成したシンボルを Toolkit for CreateJS で書きだして、Haxe で使えるようにしてみます。もちろん、Dynamic 型ではなく、クラスを定義してオートコンプリートが効くようにする事を目指します。これができるようになると、いよいよ Flash っぽくなってきますね。

Haxe で JavaScript のライブラリを使用する

Toolkit for CreateJS(長いので以下 TfCJS)に取り掛かる前に、少し回り道になりますが Haxe で JavaScript のライブラリを使用する方法について説明しておきます。

前回、Haxe 内で CreateJS や jQuery を使えるように、既存の Haxe 向けライブラリを haxelib コマンドでインストールしました。これらの有名な JavaScript ライブラリは、Haxe 向けのライブラリも用意されているため、感謝しつつそれらをインストールするだけで済んでしまいます。しかし、いつでもそのような Haxe 向けのライブラリが用意されているわけではありません。もし自分で作成した JavaScript ライブラリを Haxe で使用する場合、Haxe へ対応させる作業は自分で行うことになります。

仮に、以下の様な JavaScript があったとします。

utils.Tracer.js

if (typeof console === "undefined" || console === null) {
	console = {};
}
if (typeof console.log !== "function") {
	console.log = function () {};
}
if(typeof utils === "undefined" || utils === null) {
	utils = {};
}
utils.Tracer = {
	trace: function(str){
		console.log(str);
	}
};

console.log() をラップしただけの、ライブラリと呼ぶのも躊躇われる js ですが、これを Haxe に対応させてみます。と言っても手順は簡単で、JavaScript ライブラリが持つメソッドを記述した extern クラスファイルを作るだけです。

まず JavaScript の名前空間と同じ utils パッケージを作り、その中に JavaScript のオブジェクト名と同じ Tracer クラスを作ります。Tracer クラスの中身には、以下のように記述します。

/src/utils/Tracer.hx

package utils;

extern class Tracer {
	public static function trace(str:String):Void;
}

"extern" を付けることがが重要です。このクラスが、実行時に使用可能になるオブジェクトのインターフェイス定義である事を示します。これだけで、Tracer クラスが使用可能になりました。
試しに Main.hx 等で "Tra" とタイプすると補完が出てこなくて焦りますが、"utils" からタイプすると出てきます。

余談ですが、コンソール出力してくれる機能は haxe.Log クラスの trace で実装されているので、自作する必要はありません。

Toolkit for CreateJSで作ったライブラリを使用する

ではこの応用で、 TfCJS から書きだしたライブラリを Haxe に取り込んでみます。まず Flash で、こんなアニメーションを作ってみました。

MovieClipAssets.swf

[SWF]https://cdn-ssl-devio-img.classmethod.jp/wp-content/uploads/2013/01/MovieClipAssets.swf, 100, 100[/SWF]

これを TfCJS で書きだすと、こんな感じのjsが出力されます。

MovieClipAssets.js

(function (lib, img, cjs) {

var p; // shortcut to reference prototypes

// stage content:
(lib.MovieClipAssets = function() {
	this.initialize();

	// レイヤー 1
	this.instance = new lib.SampleMovieClip();
	this.instance.setTransform(50,50);

	this.shape = new cjs.Shape();
	this.shape.graphics.f("#999999").s().p("AH0nzIAAPnIvnAAIAAvnIPnAA").cp();
	this.shape.setTransform(50,50);

	this.addChild(this.shape,this.instance);
}).prototype = p = new cjs.Container();
p.nominalBounds = new cjs.Rectangle(0,0,100,100);


// symbols:
(lib.Tween1 = function() {
	this.initialize();

	// レイヤー 1
	this.shape = new cjs.Shape();
	this.shape.graphics.f("#000000").s().p("AhjhjIDHAAIAADHIjHAAIAAjH").cp();

	this.addChild(this.shape);
}).prototype = p = new cjs.Container();
p.nominalBounds = new cjs.Rectangle(-9.9,-9.9,20,20);


(lib.SampleMovieClip = function(mode,startPosition,loop) {
	this.initialize(mode,startPosition,loop,{},true);

	// レイヤー 1
	this.instance = new lib.Tween1("synched",0);

	this.timeline.addTween(cjs.Tween.get(this.instance).to({rotation:360},49).wait(1));

}).prototype = p = new cjs.MovieClip();
p.nominalBounds = new cjs.Rectangle(-9.9,-9.9,20,20);

})(lib = lib||{}, images = images||{}, createjs = createjs||{});
var lib, images, createjs;

この js では、Flash のステージコンテンツに対応している MovieClipAssets オブジェクトと、作成したシンボル "SampleMovieClip" に対応した SampleMovieClip オブジェクト、また、その中で使われている Tween1 オブジェクトが、名前空間 lib の中に定義されています。

では、この js ファイルをプロジェクトの bin ディレクトリにコピーした後、先ほどの JavaScript ライブラリをHaxeに対応させる手順に則って、これらに対応する extern クラスを lib パッケージ内に作成していきます。

/src/lib/MovieClipAssets.hx

package lib;
import createjs.easeljs.Container;

extern class MovieClipAssets extends Container {
}

/src/lib/SampleMovieClip.hx

package lib;
import createjs.easeljs.MovieClip;

extern class SampleMovieClip extends MovieClip {
}

シンボル独自のメソッドやプロパティは持たないため、それぞれに対応する CreateJS のクラスの継承だけで事足ります。
ただし、現在 haxelib に登録されている CreateJS-Haxe(v1.1) の MovieClip クラスにはバグがあるようで、このままコンパイルするとエラーになってしまいます。Github からバグ修正済みの MovieClip.hx をダウンロードして差し替える必要があります。

https://github.com/nickalie/CreateJS-Haxe/blob/master/createjs/easeljs/MovieClip.hx

差し替える対象のファイルは、Haxe インストールディレクトリの下の \lib\createjs\1,1\createjs\easeljs に入っています。

それでは早速、実際に作ったクラスを使ってみます。

/bin/index.html

<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="utf-8"/>
	<title>CreateJSTest</title>
	<meta name="description" content="" />
	<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
	<script src="http://code.createjs.com/easeljs-0.5.0.min.js"></script>
	<script src="http://code.createjs.com/tweenjs-0.3.0.min.js"></script>
	<script src="http://code.createjs.com/movieclip-0.5.0.min.js"></script>
</head>
<body>
	<script src="NewProject.js"></script>
	<script src="MovieClipAssets.js"></script>
	<canvas id="canvas" width="250" height="250"></canvas>
</body>
</html>

/src/Main.hx

package ;

import createjs.easeljs.Stage;
import createjs.easeljs.Ticker;
import js.JQuery;
import js.Lib;
import lib.MovieClipAssets;
import lib.SampleMovieClip;

class Main {
	
	private var stage:Stage;

	//エントリポイント
	public static function main():Void{
		new Main();
	}
 
 	//コンストラクタ
	public function new() {
		new JQuery(Lib.document).ready(function(e:JqEvent):Void {
			stage = new Stage(cast Lib.document.getElementById("canvas"));
			
			//MovieClipAssetsをステージに追加
			var mca:MovieClipAssets = new MovieClipAssets();
			stage.addChild(mca);
			
			//SampleMovieClipをステージに追加
			for (i in 0...20) {
				var smc:SampleMovieClip = new SampleMovieClip("independent", 0, true, null);
				stage.addChild(smc);
				smc.x = 250 * Math.random();
				smc.y = 250 * Math.random();
			}
			
			//24fpsでstageを描画更新する
			Ticker.setFPS(24);
			Ticker.addListener(stage);
		});
	}
}

実行結果

Flash で作ったステージコンテンツ(グレーの四角)をまるごと使うことも、シンボル(回転する黒い四角)ごとに個別に使う事もできます。Flash コンテンツを TfCJS で書き出す際には色々と制限があり、Flash で作ったものを何でも書き出せるわけではありませんが、静止画像やシンプルなアニメーション程度なら全く問題はありません。

注意点

TfCJS は、デフォルトで lib, images, createjs の各名前空間を使用するため、もし Haxe 側でこれらと競合するパッケージに extern ではないクラスを配置すると、Haxe が作るレキシカルスコープ内のパッケージオブジェクトに、TfCJS のグローバルスコープのオブジェクトが隠蔽されてしまい、コンパイルは通りますが実行時エラーが発生してしまいます。entern クラスのパッケージの最上位を window にする事でも回避可能ですが、あまりスマートなやり方ではない気がします。
TfCJS の設定で、出力される js で使用する名前空間を変更する事が可能ですので、もし競合する場合は必要に応じて設定を変えましょう。個人的には、TfCJS が使用する名前空間 lib と、Haxe で JavaScript のオブジェクトにアクセスするためのクラス js.Lib がオートコンプリート時に紛らわしいので、別の名前空間に変更したほうが良いと思います。

今回参考にさせて頂いたサイト様

CreateJS & Haxe : MovieClip.hx, Timeline.hx, Point.hx 修正・処理追加
http://www.dango-itimi.com/blog/archives/2013/001151.html