SpineをStarling1.7で使う:AssetManager経由の初期化に苦労する の巻

前回の投稿 [SpineをStarling1.7で使う:２種類のTextureAtlasに注意 の巻](http://qiita.com/harayoki/items/1f19000c9bf135d0ca85) の続きです。 この投稿では、２種類のテクスチャアトラスのうち、Spine形式のデータを読み込んで初期化する流れを追います。
[デモページ](http://harayoki.github.io/qiita_demo/SpineTest1/)は前回と同じやつです。

公式のデモではデータファイル３つをソースにEmbedして埋め込んでいますが、

* [raptor.json](https://github.com/harayoki/SpineTest1/blob/RaptorDemo/assets/assets/raptor.json)… キャラクター構造＋モーションデータ
* [raptor.atras](https://github.com/harayoki/SpineTest1/blob/RaptorDemo/assets/assets/raptor.atlas) … Spine形式のテクスチャアトラス設定テキストデータ

* [raptor.png](https://github.com/harayoki/SpineTest1/blob/RaptorDemo/assets/assets/raptor.png) …  テクスチャアトラスの画像データ 
<img src="https://raw.githubusercontent.com/harayoki/SpineTest1/RaptorDemo/assets/assets/raptor.png" width="512" height="512" />

通常はデータファイルをソース外から動的に読み込むことが多いと思います。それぞれ`URLLoader`や`Loader`で読み込みSkeleton初期化する事は可能ですが、面倒なので、`starling.utils.AssetManager`を使ってファイルを読み込んでみます。

#AssetManager利用の実際のコード
コード例を交えながら、サクサク進めます。

```sampple.as
// インスタンスを作ってファイル登録してロード
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();
    }
});
```

↑ ここはいつも通りのコードです。

```sample.as
// ロード済みのファイルの取り出し
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として取り出します。

```GoblinsExample.as
[Embed(source = "/goblins-mesh.atlas", mimeType = "application/octet-stream")]
	static public const GoblinsAtlas:Class;
```

↑ Spine公式のデモでも`application/octet-stream`(なんかバイナリデータ)としてEmbedしています。これで大丈夫です。

```sample.as
// 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()`がエラーとなります。

```error.as
var spineAtlas:Atlas = new Atlas(atlasData, new StarlingTextureLoader(texture));
```

引数の型としてはObject型なので隠蔽されますが、StarlingTextureLoaderはコンスタラクタの引数にBitmapかBitmapDataかBitmap、もしくは(アトラス画像が複数枚の場合)パスをキー名、BitmapかBitmapDataをValueに持つObjectを取ります。Texture型は引数にとりません。

```sample.as
//`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です。クラス名はそのうち変えそうですが。。ソースは[ここ](https://github.com/harayoki/SpineTest1/blob/RaptorDemo/src/harayoki/spine/starling/MyStarlingTextureLoader.as)にあります。中身はStarlingTextureLoaderをベースに改造して作られたものです）

```sample.as
new MyStarlingTextureLoader(texture);
//もしくは
new MyStarlingTextureLoader({path1:texture1, path2:texture2});
```

↑ このようにインスタンス化が可能です。こちらのクラスを使って書き出したのが、前回と今回の[デモページ](http://harayoki.github.io/qiita_demo/SpineTest1/)となります。とりあえず、問題なく動いていますね。やあ、よかった、よかった。

<img src="https://qiita-image-store.s3.amazonaws.com/0/8302/e93afc5c-e795-98b2-16e2-9f7c219cbdc1.jpeg" width="535" height="399"/>

#まとめ
今回は専用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