前回の投稿 SpineをStarling1.7で使う:2種類のTextureAtlasに注意 の巻 の続きです。 この投稿では、2種類のテクスチャアトラスのうち、Spine形式のデータを読み込んで初期化する流れを追います。
デモページは前回と同じやつです。
公式のデモではデータファイル3つをソースにEmbedして埋め込んでいますが、
-
raptor.json… キャラクター構造+モーションデータ
-
raptor.atras … Spine形式のテクスチャアトラス設定テキストデータ
-
raptor.png … テクスチャアトラスの画像データ
通常はデータファイルをソース外から動的に読み込むことが多いと思います。それぞれURLLoader
やLoader
で読み込みSkeleton初期化する事は可能ですが、面倒なので、starling.utils.AssetManager
を使ってファイルを読み込んでみます。
#AssetManager利用の実際のコード
コード例を交えながら、サクサク進めます。
// インスタンスを作ってファイル登録してロード
var assetManager:AssetManager = new AssetManager();
assetManager.enqueue("raptor.png");
assetManager.enqueue("raptor.atlas");
assetManager.enqueue("raptor.json");
ssetManager.loadQueue(function (ratio:Number):void {
if (ratio == 1.0) {
createRaptor();
}
});
↑ ここはいつも通りのコードです。
// ロード済みのファイルの取り出し
function createRaptor():void {
var texture:Texture = _assetManager.getTexture("raptor");
var json:Object = _assetManager.getObject("raptor");
var atlasData:Object = _assetManager.getByteArray("raptor");
↑ pngはTextureとして、jsonはObjectとして、atrasデータのテキストは(getString的なメソッドがないため)ByteArrayとして取り出します。
[Embed(source = "/goblins-mesh.atlas", mimeType = "application/octet-stream")]
static public const GoblinsAtlas:Class;
↑ Spine公式のデモでもapplication/octet-stream
(なんかバイナリデータ)としてEmbedしています。これで大丈夫です。
// atlasDataとテクスチャからアトラスを作成して
var spineAtlas:Atlas = new Atlas(atlasData, new StarlingTextureLoader(texture));
// アトラスを元にAttachmentLoaderとやらを作って
var attachmentLoader:AttachmentLoader = new AtlasAttachmentLoader(spineAtlas);
// AttachmentLoaderを元にSkeletonJsonクラスを作って(Jsonそのものではないです)
var json:SkeletonJson = new SkeletonJson(attachmentLoader);
// SkeletonJsonに読み込んだjsonを解釈させて
var skeletonData:SkeletonData = json.readSkeletonData(json);
// skeletonDataからSkeletonAnimationを(やっと)作ります
var raptor:SkeletonAnimation = new SkeletonAnimation(skeletonData, true);
addChild(raptor);
↑ めんどくさい手続きで画面に表示できるSkeletonAnimation(=DisplayObject)を作ります。ほぼ公式のサンプルのままのコードです。お決まり処理になるので、関数かなにかにまとめましょう。。継承ツリーは、SkeletonAnimation extends SkeletonSprite extends DisplayObject
と、なっています。
面倒臭いながらも、SkeletonAnimationを生成できたので、これでOK!、と言いたいのですが、実は上記のコードにはエラーがあり、コンパイルは通るものの実行するとランタイムエラーになります。問題は下記の部分です。new StarlingTextureLoader()
がエラーとなります。
var spineAtlas:Atlas = new Atlas(atlasData, new StarlingTextureLoader(texture));
引数の型としてはObject型なので隠蔽されますが、StarlingTextureLoaderはコンスタラクタの引数にBitmapかBitmapDataかBitmap、もしくは(アトラス画像が複数枚の場合)パスをキー名、BitmapかBitmapDataをValueに持つObjectを取ります。Texture型は引数にとりません。
//`A Bitmap or BitmapData for an atlas that has only one page, or for a multi page atlas an object where the key is the image path and the value is the Bitmap or BitmapData.`
{
"hoge1.png":hoge1BitmapData,
"hoge2.png":hoge2BitmapData
}
と、いう事でraptor.pngをBitmap型としてStarlingTextureLoaderに引き渡したいのですが、、
#AssetManagerからBitmapDataの参照は取れない
のです。AssetManagerは画像を読み込むと、その画像を元にすみやかにTextureインスタンスを生成し、画像すなわちBitmapDataを破棄してしまいます。使用メモリを少なくするための処理なのですが、(Spineを使う場合にに限らず)Bitmapを利用したい場合に困ってしまいます。AssetManagerを改造してBitmapを保持するようにしている人もいるようですが、自分はコアなライブラリであるStaling側に変更を極力加えたくないので、下記のような解決策を取りました。
#専用のTextureLoaderを作ってしまおう
Textureインスタンスを引数に取るharayoki.spine.starling.MyStarlingTextureLoaderなるものを作りました。implements TextureLoaderです。クラス名はそのうち変えそうですが。。ソースはここにあります。中身はStarlingTextureLoaderをベースに改造して作られたものです)
new MyStarlingTextureLoader(texture);
//もしくは
new MyStarlingTextureLoader({path1:texture1, path2:texture2});
↑ このようにインスタンス化が可能です。こちらのクラスを使って書き出したのが、前回と今回のデモページとなります。とりあえず、問題なく動いていますね。やあ、よかった、よかった。
#まとめ
今回は専用TextureLoaderを作って問題を解決しましたが、Starling形式のatlasXMLを読み込んでSpineを使う場合はAssetManagerとの相性は抜群です。今回の投稿はSpine形式のatlasデータテキストと画像をAssetManagerで読み込んで使いたい場合のノウハウとなります。自分でパッキングまで行う人には必要ないノウハウですね。でも、他の作業者からデータをもらうような場合って、Spine形式のデータをもらうことになるほうが多いと思うんですよね。
そういうニーズがありそうなので公式でもTextureを引数にできるTextureLoaderを作ってよ、という要望をSpineのフォーラムに投げておきました。そのうち実装されると良いなあ。。
http://ja.esotericsoftware.com/forum/Wish-StarlingTextureLoader-can-be-instantiated-with-starling-6412