SpineをStarling1.7で使う:フィルタを使えるようにする


別の記事にも書きましたが、Spine-StarlingのSkeletonAnimation(SkeletonSprite)にはデフォルトの状態でフィルタが正しく適用できません。せっかくの表現力が下がってもったいないので、フィルタが適用できるSkeletonAnimationクラスを作ってみました。

デモ


http://harayoki.github.io/qiita_demo/SpineAndFilter/
ドロップシャドウフィルタを適用してみました。いい感じですね。もちろん発光でもブラーでもカラーマトリックスでもディスプレースメントでも適用できます。ちなみに背景やキャラをタッチすると動きます。

使い方

SkeletonAnimationFilterApplicableというクラスが、SkeletonAnimationにフィルタが適用できるように拡張したクラスです。
SkeletonAnimationFilterApplicable.as

sample.as
// skeletonDataを引数にインスタンス化(引数はSkeletonAnimationクラスと同じです。)
var skeletonAnimation:SkeletonAnimationFilterApplicable = new SkeletonAnimationFilterApplicable(skeletonData);

// 適用なフィルタ適用して配置
skeletonAnimation.filter = BlurFilter.createDropShadow();
addChild(skeletonAnimation);

これだけでも動きますが、アニメーションの切り替わり時などに、下記の手順を追加する事を推奨します。意味合いは後述。

sample.as

// アニメーションの再生指示後など、大きさが著しくかわるタイミングで、
animationState.addAnimationByName(0, "guruguru", true, 0);

// updateBoundsを呼び出す。もしくは、
skeletonAnimation.updateBounds(20, 20);

// skeletonAnimationのローカル座標で大きさを直接指定する。
skeletonAnimation.setBoundsDirectly(new Rectangle(-160, -160, 320, 320));

(updateBoundsとsetBoundsDirectlyはどちらかを呼べば良いです。)


あまり試していませんが、Sprite3Dでの3D表示にも対応してあります。

仕組み

SkeletonSpriteはなぜかgetBounds()メソッドが座標は返すものの大きさを返しません。(おそらくSkeletonSpriteの内部構造が複雑なため、真面目にgetBounds()を実装するとパフォーマンスに問題がでてしまうので、このような実装になっているのではないか?)そして、StarlingのフィルタはgetBounds()が返す大きさを元に描画エリアを決定するので、SkeletonSpriteにはフィルタが適用できない状態になってしまっています。

ここを適当な固定の大きさを返すようにすればフィルタが描画されるわけですが、getBoundsは引数で指定された座標空間上でのbounds(座標と大きさ)を返す仕様なため、固定の値でなく、座標変換処理をやってやらないといけません。というところで、直接Bounds値をsetBoundsDirectlyメソッドで指定できて(内部で大きさを計算させず、固定の大きさをユーザが与える)、getBoundsメソッドではその値を元に座標変換を行う実装をしました。

もちろん固定の大きさを返すよりも、表示中のSkeletonSpriteの大きさをきちんと計算して返したほうが良いのですが、前述のように都度まじめに計算させるとパフォーマンス低下の問題が出そうです。

この問題を解決するために、指定したタイミングで大きさを計算して、再度の計算指示があるまではその固定値を元に大きさを返すような仕様の実装を追加しました。通常はアニメーションを変更するたびに1回だけupdateBoundsメソッドを呼んでやると良いでしょう。その際、引数で縦横のマージン幅を指定できるので、アニメーション中に大きさが変わったり、フィルタ描画でSkeletonSpriteの大きさが大きくなる場合は、多めにマージンを指定して下さい。(さもないとはみ出た部分のフィルタが描画されません。)

Spineのデータ側に編集を入れていいなら、透明なパーツなどをダミーで置いておき、明示的にアニメーション中の最大描画領域の大きさを指定するのも良いかもしれません。

いろいろ他とまざってますが、デモのソースはここにあります。
https://github.com/harayoki/SpineTest1/blob/master/src/demos/FilterDemo1.as

終わりに

一番Spine-Starlingランタイムで気になっていた部分をなんとかできました。このあたりも制作される予定のStarling2対応ランタイムで直っていると良いですね。では。