spine-haxeランタイム ドキュメント

ライセンスについて

Spineランタイムをアプリケーションに組み込む前に、必ずSpine Runtimes Licenseを確認してください。

はじめに

spine-haxeランタイムは、レンダラーに依存しないSpineランタイム コアAPIのHaxe実装であるコア モジュールと、starling フレームワークを使用した特定のレンダラー実装で構成されています。spine-haxeはターゲットとしてHTML5を使用してテストされており、現在のところWebGLと互換性があります。

spine-haxeランタイムは、乗算済みアルファを使用しているアトラスと2色ティントを除くSpineのすべての機能をサポートしています。

インストール方法

spine-haxeのコアモジュールの依存関係はゼロです。Starlingを使ったレンダリング実装には openflとstarlingの2つの依存関係があります。spine-haxeを使うには、まず必要な依存関係をすべてインストールする必要があります:

haxelib install openfl
haxelib install starling

依存関係をインストールしたら、spine-haxeの最新版をダウンロードしてインストールしてください:

haxelib install spine-haxe-x.y.z.zip

spine-haxeライブラリはlib.haxe.orgからは利用できないことに注意してください。このため、ライブラリをダウンロードしてzipアーカイブからインストールする必要があります。

サンプル

spine-haxeランタイムには、利用できる機能セットを実演しているいくつかのサンプルが含まれています。

ローカルでサンプルを実行するには:

  1. あなたのオペレーティングシステム用にGitとHaxeをインストールします。
  2. haxelibを設定し、必要な依存関係をすべてインストールします。
haxelib setup
haxelib install openfl
haxelib run openfl setup
haxelib install starling
  1. spine-runtimes リポジトリをクローンし、spine-runtimes/spine-haxe フォルダに移動して、testコマンドでlimeを実行します:
git clone https://github.com/esotericsoftware/spine-runtimes
cd spine-runtimes/spine-haxe
haxelib dev spine-haxe .
lime test html5

これにより、spine-haxeランタイムがビルドされ、ブラウザが開いてウェブページにサンプルが表示されます。 フレームをクリックしてすべてのサンプルを見て、spine-runtimes/spine-haxe/example/src フォルダにあるコードをチェックしてみてください。

spine-haxeランタイムのアップデート

プロジェクトのspine-haxeランタイムをアップデートする前に、Spineエディターとランタイムのバージョン管理に関するガイドをよく確認してください。

spine-haxeランタイムをアップデートするには、https://esotericsoftware.com/files/spine-haxe/4.2/spine-haxe-x.y.z.zip から希望のバージョンをダウンロードしてください(x.y.z の部分は実際に使用したいバージョンに従って変更してください)。それから haxelib install spine-haxe:x.y.z.zip を実行してください。

注意: spine-haxeパッケージの major.minor バージョンを変更した場合、Spineスケルトンを同じ major.minor バージョンのSpineエディターで再エクスポートする必要があります。詳しくは「バージョンの同期」を参照してください。

spine-haxeを使用する

spine-haxeランタイムは、乗算済みアルファを使用しているアトラスと2色ティントを除くSpineのすべての機能をサポートしています。

spine-haxeはstarlingをフレームワークとして使用しています。HTML5をターゲットとしてテストされており、現在のところ WebGL でのレンダリングに対応しています。Canvas API によるレンダリングはサポートされていません。

アセットのマネージメント

spine-haxe用にエクスポートする

以下の実行方法については、Spineユーザーガイド内で紹介されています :

  1. スケルトン&アニメーションデータのエクスポート
  2. スケルトンの画像を含むテクスチャアトラスのエクスポート

スケルトンのデータとテクスチャアトラスをエクスポートすると、以下のファイルが得られます:

  1. skeleton-name.json または skeleton-name.skel: これはスケルトンとアニメーションのデータを含んでいます。
  2. skeleton-name.atlas: これはテクスチャアトラスの情報を含んでいます。
  3. 1つまたは複数の .png ファイル: これはテクスチャアトラスの各ページで、スケルトンが使用するイメージを含んでいます。

補足: JSONエクスポートよりもバイナリ形式でのスケルトンエクスポートの方がサイズが小さく、読み込みが速いので、基本的にはそちらを選択したほうが良いでしょう。

これらのファイルを提供する際には、サーバーが正しいMIMEタイプを出力することを確認してください。

Spineアセットの更新

開発中にスケルトンデータやテクスチャアトラスファイルを更新したい時は、単純にSpineエディターから再エクスポートを行なって、Haxeプロジェクト内の既存のファイル(.json.skel.atlas.png)を置き換えるだけで簡単にこれらのソースファイルを更新できます。

その際、spine-haxeの major.minor バージョンとエクスポートを行ったSpineエディターの major.minor が一致していることを確認してください。詳しくは「バージョンの同期」を参照してください。

コアクラス

spine-haxeのAPIはstarlingやopenflに依存しない汎用Haxeランタイムの上に構築されています。プラットフォームに依存しないコアクラスと、Spineスケルトンのロード、クエリ、修正、アニメーションを行うアルゴリズムを提供します。

ここではspine-haxeを使用する際によく見ることになる最も重要なコアクラスについてのみ簡単に説明しています。Spineランタイムのアーキテクチャ、コアクラス、APIの使用法の詳細については、Spineランタイムガイドを参照してください。

TextureAtlas クラスは、.atlas ファイルとそれに対応する .png 画像ファイルからロードしたデータを保管します。

SkeletonData クラスは、.json または .skel ファイルからロードされたデータを保管します。このスケルトンデータには、ボーン階層、スロット、アタッチメント、コンストレイント、スキン、アニメーションに関する情報が含まれます。SkeletonData インスタンスは、通常、それが表すスケルトンで使用されるイメージをソースとする Atlas(アトラス) も一緒に提供することによってロードされます。これは、Skeletonインスタンスを作成するための設計図として機能します。複数のスケルトンを同じアトラスとスケルトンデータからインスタンス化し、ロードされたデータを共有することで、ロード時間と実行時のメモリ消費を最小限に抑えることができます。

Skeleton クラスは、SkeletonData インスタンスから作成されたスケルトンのインスタンスを格納します。スケルトンは現在のポーズを保管します。つまり、ボーンの位置、スロット、アタッチメント、アクティブなスキンの現在の構成を保管します。現在のポーズは、手動でボーンのトランスフォームを変更するか、より一般的には、AnimationState を介してアニメーションを適用することで計算されます。

AnimationState クラスは、スケルトンに適用する(単数または複数の)アニメーションを追跡し、最後のレンダリングフレームと現在のレンダリングフレームの間の経過時間に基づいてそれらのアニメーションを進め、ミックスを行い、スケルトンインスタンスにアニメーションを適用して現在のポーズを設定します。AnimationStateAnimationStateData インスタンスに問い合わせ(処理要求)をして、アニメーション間のミキシング時間を取得します。特定のアニメーション間に使用するミキシング時間が無ければデフォルトミックスタイムを取得します。

spine-haxeランタイムはこれらのコアクラスの上に構築されています。

starling用のSpine Haxeランタイム

starling用のspine-haxeでは2つのクラスを公開しています: StarlingTextureLoaderSkeletonSprite です。

StarlingTextureLoader は、starlingで画像を作成および破棄するための、spine TextureLoader の実装です。

SkeletonSprite は starling DisplayObject の拡張で、Spineのアニメーションを再生します。

Spineアセットのロード

スケルトンデータファイル(.json/.skel) や .atlas ファイルのようなSpineアセットは、openfl Assets クラスを通じてロードされます。

SkeletonSprite のインスタンスを作成する前に、それぞれのスケルトンファイルやアトラスファイルをロードする必要があります。具体的には以下を使用することができます:

  • Assets.getText(string): は、.json.atlas ファイルのようなテキストベースのアセットをロードします。
  • Assets.getBytes(string): は、.skel ファイルのようなバイナリベースのアセットをロードします。

スケルトンデータを skeleton.skel というバイナリのスケルトンファイルにエクスポートし、アトラスを skeleton.atlas というファイルに、対応する1つの skeleton.png ファイルと一緒にエクスポートしたと仮定すると、次のようにアセットをロードすることができます:

haxe
var atlasFile = Assets.getText("skeleton.atlas");
var skeletonFile = Assets.getBytes("skeleton.skel");

アセットがロードされたら、TextureAtlasSkeletonData のインスタンスを取得する必要があります:

haxe
var atlas = new TextureAtlas(atlasFile, new StarlingTextureLoader("skeleton.atlas"));
var skeletondata = SkeletonData.from(skeletonFile, atlas);

TextureAtlas コンストラクタが StarlingTextureLoader のインスタンスを必要とすることに注意してください。StarlingTextureLoader にアトラスファイルの名前を提供する必要があります。個々のテクスチャアトラスページ画像は、明示的にロードする必要なく、透過的にロードされます。

スケルトンのraw(生の)データとアトラスは、それだけではアニメーションやレンダリングができません。その代わりに、SkeletonSprite がそれらから構築されます。同じアセットでインスタンス化された SkeletonSprite は、同じスケルトンデータとアトラスを共有します。

SkeletonSpriteインスタンスの作成

スケルトンのrawデータと対応するアトラスが読み込まれたら、SkeletonSprite をインスタンス化する前に、AnimationStateData をインスタンス化する必要があります:

haxe
var animationStateData = new AnimationStateData(skeletondata);

これで SkeletonSprite をインスタンス化することができます:

haxe
// SkeletonSpriteをインスタンス化
var skeletonSprite = new SkeletonSprite(skeletondata, animationStateData);

// SkeletonSpriteをステージのDisplayObjectの子として追加
addChild(spineboy);

SkeletonSprite コンストラクタは、SkeletonDataAnimationStateData を受け取ってインスタンスを生成します。

スケルトンの境界を取得するには、スケルトンで getBounds() 関数を呼び出します。また、SkeletonSpritegetAnimationBounds(string, bool) 関数を呼び出すと、最初のパラメータとして指定された名前のアニメーションを含む Rectangle を取得できます。2番目のパラメータは、境界の計算中にクリッピング・アタッチメントを考慮するためのブール値です。SkeletonSpritegetBounds(DisplayObject) は常にサイズ0, 0の Rectangle を返すので、使わないでください。

SkeletonSprite

SkeletonSprite は、Starling DisplayObjectの拡張で、Skeleton とそれに関連する AnimationState の保存、更新、レンダリングをバンドルしています。SkeletonSprite インスタンスは、前のセクションで説明したように、スケルトンデータとアトラスから作成されます。SkeletonAnimationState には、それぞれ skeleton フィールドと state フィールドからアクセスできます。

毎フレーム、SkeletonSprite コンテナは以下を行います:

  • AnimationState の更新
  • AnimationStateSkeleton に適用します。
  • Skeleton のワールド変換を更新し、新しいポーズを生成します。
  • Skeleton を現在のポーズでレンダリングします。

アニメーションの適用

Spine コンテナで表示されるスケルトンにアニメーションを適用するには、AnimationState を使用します。

注意: アニメーショントラックやアニメーションのキューイングなど、より詳しい情報については、Spineランタイムガイドの「アニメーションの適用」を参照してください。

トラック0に特定のアニメーションを設定するには、AnimationState setAnimation を呼び出します:

haxe
spineObject.state.setAnimation(0, "walk", true);

最初のパラメーターはトラック、2番目のパラメーターはアニメーションの名前、3番目のパラメーターはアニメーションをループさせるかどうかを指定します。

addAnimation を使って複数のアニメーションをキューに入れることもできます:

haxe
spineObject.state.setAnimation(0, "walk", true);
spineObject.state.addAnimation(0, "jump", 2, false);
spineObject.state.addAnimation(0, "run", 0, true);

addAnimation の最初のパラメーターはトラックです。2番目のパラメーターはアニメーションの名前です。3番目のパラメーターは、このアニメーションが同じトラック上の前のアニメーションと置き換わるまでの時間(ディレイ)を秒単位で指定します。最後のパラメーターはアニメーションをループさせるかどうかを指定します。

上の例では、まず "walk" アニメーションが再生されます。その2秒後に "jump" アニメーションが一度再生され、続いて "run" アニメーションに切り替わり、ループします。

あるアニメーションから別のアニメーションに遷移するとき、 AnimationState は特定の時間(デュレーション)だけアニメーションをミックス(クロスフェード)します。これらのミックスデュレーションは AnimationStateData インスタンスで定義され、 AnimationState はそこからミックスデュレーションを取得します。

AnimationStateData インスタンスは AnimationState.data プロパティからも利用できます。以下のようにしてデフォルトのミックスデュレーションや、特定のアニメーションのペアのミックスデュレーションを設定することができます:

spineObject.state.data.setDefaultMix = 0.2;
spineObject.state.data.setMixByName("walk", "jump", 0.1);

アニメーションを設定または追加すると、TrackEntry オブジェクトが返されます。これによりアニメーションの再生をさらに変更できます。例えば、アニメーションを逆再生するようにTrackEntryを設定することができます:

const entry = spineObject.state.setAnimation(0, "walk", true);
entry.reverse = true;

利用できるオプションについて詳しくは TrackEntry クラスのドキュメントをご覧ください。

注意: TrackEntry インスタンスを使用している関数の外部で保持する場合には注意医してください。TrackEntryは内部で再利用されるため、TrackEntryがdisposeイベントを発生させた後は無効になります。

スケルトンをスムーズにセットアップポーズに戻したい場合は、アニメーショントラックに空のアニメーションをセットまたはキューに追加します:

spineObject.state.setEmptyAnimation(0, 0);
spineObject.state.addAnimation(0, "walk", 0).mixDuration = 0.5;
spineObject.state.addEmptyAnimation(0, 0.5, 6);

setAnimation と同様に、setEmptyAnimation() の最初のパラメーターはトラックを指定します。2番目のパラメーターは、前のアニメーションをミックスアウトし、"空の"アニメーションをミックスするために使用するミックスデュレーションを秒単位で指定します。

addAnimation と同様に、addEmptyAnimation() の最初のパラメーターはトラックを指定します。2番目のパラメーターはミックスデュレーションを指定します。3番目のパラメーターはディレイ(秒単位)で、このディレイの後に空のアニメーションがミキシングされてトラック上の前のアニメーションと置き換わります。

AnimationState.clearTrack() を使えばトラック上のすべてのアニメーションを即座にクリアすることができます。すべてのトラックを一度にクリアするには AnimationState.clearTracks() を使います。しかしこれはスケルトンを最後に適用されたポーズのままにする点に注意してください。通常はこれを使うよりも空のアニメーションを使ってセットアップポーズにスムーズに戻す方が良いでしょう。

スケルトンのポーズをセットアップポーズに戻すには、Skeleton.setToSetupPose() を使います:

spineObject.skeleton.setToSetupPose();

これはボーンとスロットの両方をセットアップポーズの設定にリセットします。ボーンだけをリセットしたい場合はSkeleton.setBonesToSetupPose() を、スロットだけをリセットしたい場合は Skeleton.setSlotsToSetupPose() を使用してください。

AnimationStateイベント

AnimationState は、再生中のアニメーションのライフサイクル中に様々なイベントを発行します。必要に応じてこのイベントをリッスンすることで、それらに反応させることができます。SpineランタイムのAPIでは、以下のイベントタイプを定義しています:

  • start: アニメーションが開始された時に発されます。
  • interrupt: アニメーションのトラックがクリアされた、または新しいアニメーションが設定されたなどにより中断された時に発されます。
  • end: アニメーションが二度と適用されない時に発されます。
  • dispose: アニメーションのTrackEntryが破棄された時に発されます。
  • complete: アニメーションが1ループを完了するごとに発されます。
  • event: ユーザーが定義したイベントが発生した時に発されます。

イベントを受け取るには、AnimationStateListener コールバックを、すべてのアニメーションでイベントを受信するAnimationState か、キューされた特定のアニメーションの TrackEntry に登録します:

haxe
// コールバックをAnimationStateに追加する
spineObject.state.onStart.add(entry -> trace('Started animation ${entry.animation.name}'));
spineObject.state.onInterrupt.add(entry -> trace('Interrupted animation ${entry.animation.name}'));
spineObject.state.onEnd.add(entry -> trace('Ended animation ${entry.animation.name}'));
spineObject.state.onDispose.add(entry -> trace('Disposed animation ${entry.animation.name}'));
spineObject.state.onComplete.add(entry -> trace('Completed animation ${entry.animation.name}'));
spineObject.state.onEvent.add((event entry) -> trace('Custom event for ${entry.animation.name}: ${event.data.name}'));

// コールバックをTrackEntryに追加する
var trackEntry = spineObject.state.setAnimationByName(0, "walk", true);
trackEntry.onEvent.add((entry, event) => trace('Custom event for ${entry.animation.name}: ${event.data.name}'));

詳しくは EventsExample.hx をご覧ください。

スキン

多くのアプリケーションやゲームでは、髪や目、ズボン、イヤリングやバッグなどのアクセサリーなど、さまざまなアイテムを組み合わせてカスタムアバターを作ることができます。Spineでは、複数スキンを組み合わせることでこれを実現することができます。

以下のようにして、他のスキンからカスタムスキンを作成することができます:

haxe
const skeletonData = spineObject.skeleton.data;
const skin = new spine.Skin("custom");
skin.addSkin(skeletonData.findSkin("skin-base"));
skin.addSkin(skeletonData.findSkin("nose/short"));
skin.addSkin(skeletonData.findSkin("eyelids/girly"));
skin.addSkin(skeletonData.findSkin("eyes/violet"));
skin.addSkin(skeletonData.findSkin("hair/brown"));
skin.addSkin(skeletonData.findSkin("clothes/hoodie-orange"));
skin.addSkin(skeletonData.findSkin("legs/pants-jeans"));
skin.addSkin(skeletonData.findSkin("accessories/bag"));
skin.addSkin(skeletonData.findSkin("accessories/hat-red-yellow"));      
spineObject.skeletonskin = skin;
spineObject.skeleton.setToSetupPose();

まず、コンストラクタ Skin() で新しい空のスキンを作成します。

次に、スケルトンから SkeletonData を取得します。これは SkeletonData.findSkin() でスキンを名前から探すのに使用します。

Skin.addSkin() で新しいスキンにまとめたいスキンをすべて追加します。

最後に、Skeleton に出来上がった新しいスキンをセットし、Skeleton.setSlotsToSetupPose() を呼び出して、以前のスキンやアニメーションのアタッチメントが残らないようにします。

完全なコード例は MixAndMatchExample.hx で確認できます。

ボーンのトランスフォームの設定

Spineエディター内でスケルトンを構築する際、スケルトンは、スケルトンのワールド座標系または「スケルトン座標系」と呼ばれるもので定義されます。この座標系は、Haxeの座標系と一致しない場合があります。そのため、例えば、ユーザーがタッチでボーンを動かせるようにする場合などは SkeletonSprite に相対するマウス座標やタッチ座標をスケルトン座標系に変換する必要があります。

SkeletonSpritehaxeWorldCoordinatesToBone(point, bone) というメソッドを提供しています。このメソッドは SkeletonSprite からの相対点を受け取り、指定されたボーンからの相対点をスケルトンの座標系に変換します。

逆にスケルトン座標系からHaxe座標系へ変換したい場合は、SkeletonSprite.skeletonToHaxeWorldCoordinates(point) で実現できます。

完全なコード例は ControlBonesExample.hx で確認できます。

SpineランタイムのAPI呼び出し

starling用のspine-haxeは、SkeletonSprite プロパティの skeletonstatestate.data を通してコアAPI全体を公開しています。一般的なSpineランタイムガイドと同様に、これらのクラスのJSドキュメントを参照してください。

最小限のプロジェクトセットアップ

Starlingを使用してHaxeでSpineアニメーションを実行したいだけの場合や、Spineアニメーションを含むプロジェクトを開始したい場合は、以下の手順に従ってください。

必要な依存関係をすべてインストールしたら、以下のようにして新しいプロジェクトを作成します:

openfl create starling:project MySpineProject

これで MySpineProject という名前のフォルダが作成され、OpenFLで空のプロジェクトを実行するのに必要なすべてのファイルが格納されるので、MySpineProject をお好みのhaxe IDEで開いてください。Assets フォルダにアセットをコピーします。project.xmlファイルを編集し、以下のように依存関係としてspine-haxeを追加します:

<haxelib name="spine-haxe" />

もし .skel バイナリスケルトンをロードしたい場合は、以下の行を:

<assets path="Assets" rename="assets" />

以下に置換してください。(そうしないとopenflは skel ファイルをテキストとしてロードしてしまい、ファイルが壊れてしまいます):

<assets path="Assets" rename="assets" exclude="*.skel"/>
<assets path="Assets" rename="assets" include="*.skel" type="binary" />

次に Source/Game.hx ファイルを開き、コンストラクタを以下のものに変更してください:

haxe
public function new () {
   super ();
   var atlas = new TextureAtlas(
      Assets.getText("assets/raptor.atlas"),
      new StarlingTextureLoader("assets/raptor-pro.atlas"));
   var skeletondata = SkeletonData.from(Assets.getText("assets/raptor-pro.json"), atlas, .5);
   var animationStateData = new AnimationStateData(skeletondata);
   var skeletonSprite = new SkeletonSprite(skeletondata, animationStateData);
   skeletonSprite.x = Starling.current.stage.stageWidth / 2;
   skeletonSprite.y = Starling.current.stage.stageHeight * .75;
   skeletonSprite.state.setAnimationByName(0, "walk", true);
   addChild(skeletonSprite);
   Starling.current.juggler.add(skeletonSprite);
}

必要なクラスをインポートすることを忘れないように注意してください。IDEからコードを実行するか、プロジェクトフォルダでターミナルから以下のコマンドを実行してください:

lime test html5

VS Code セットアップ

IDEとしては、以下の拡張機能を持つVisual Studio Codeを使用することをお勧めします:

  1. Haxe
  2. HXCPP デバッガー
  3. Lime

これらの拡張機能は、自動補完、デバッグ、ビルドサポートなどのIDE機能を提供します。

ビルドをデバッグするには、VS Codeの下部にあるステータスバーの対応するLimeターゲットを HTML5 / Debug などに設定します。F5 キーを押して、lime の実行設定を実行します。