spine-flutterランタイム ドキュメント
ライセンスについて
Spineランタイムをアプリケーションに組み込む前に、必ずSpine Runtimes Licenseを確認してください。
はじめに
spine-flutterランタイムは、spine-cppの上にFlutter FFIプラグインとして実装されています。Flutterがサポートするすべてのプラットフォーム(デスクトップ、Android、iOS、ウェブ)をサポートし、ティントブラックを除くすべてのSpineの機能をサポートしています。
インストール方法
spine-flutterはFlutter 3.10.5以降でサポートされています。Flutterプロジェクトでspine-flutterを使うには、プロジェクトの pubspec.yaml
ファイルに以下の依存関係を追加してください:
...
spine_flutter: ^4.1.1
spine-flutterの major.minor
バージョンが、エクスポートを行ったSpineエディターの major.minor
と一致していることを確認してください。詳しくは「バージョンの同期」を参照してください。
次に、main()
関数の最初に以下の2行を追加して、spine-flutterランタイムを初期化します:
WidgetsFlutterBinding.ensureInitialized();
await initSpineFlutter(enableMemoryDebugging: false);
...
}
注意:
main()
メソッドは必ずasync
である必要があります。
WebデプロイメントにはCanvasKitが必要で、この記事を書いている時点では、Webデプロイメントに約2MBの依存関係が追加されます。Canvaskitを使ってWeb用にアプリをコンパイルするには、次のようにします:
Webレンダラーについての詳細はFlutterのドキュメントを参照してください。
サンプル
spine-flutterランタイムには、利用できる機能セットを実演しているサンプルが多数含まれています。
pub.devから spine_flutter
パッケージを直接ダウンロードした場合は、以下の手順で example/
フォルダにあるサンプルプロジェクトを実行できます:
cd example
flutter run
または、以下の手順でサンプルプロジェクトを実行することもできます:
- Flutter SDKをインストールし、
flutter doctor
を実行します。これは他にインストールすべき依存関係を指示してくれます。 - spine-runtimesリポジトリをクローンします:
git clone https://github.com/esotericsoftware/spine-runtimes
spine-flutter/
フォルダ内のsetup.sh
スクリプトを実行します。 WindowsではGit for Windowsに含まれるGit Bashを使ってこのBashスクリプトsetup.sh
を実行できます。
その後、IntelliJ IDEA/Android StudioやVisual Studio Codeなど、Flutterをサポートするお好みのIDEやエディターで spine-flutter
を開き、サンプルを確認したり実行したりできます。
そのほか、コマンドラインからサンプルを実行することもできます。
サンプルプロジェクトには、以下の例が含まれています:
example/lib/simple_animation.dart
: これはSpineWidget
とSpineWidgetController
を使用して、エクスポートしたSpineスケルトンをロードして、ウィジェットに表示し、特定のアニメーションを再生するまでの基本的な使い方を実演しています。example/lib/pause_play_animation.dart
: アニメーションの一時停止と再開の方法を実演しています。example/lib/animation_state_events
: スロットの色を設定する方法、複数のアニメーションをキューに入れる方法、AnimationStateのイベントをリッスンする方法を実演しています。example/lib/debug_rendering.dart
:SpineWidgetController
のonAfterPaint
コールバックを使用して、レンダリングされたスケルトンの上にカスタム描画を実行する方法を実演しています。example/lib/dress_up.dart
: Spineのスキン機能と、キャラクター作成UIで使用するスケルトンをサムネイルにレンダリングする方法を実演しています。example/lib/ik_following.dart
: マウスまたはタッチ入力でスケルトンのボーンをドラッグする方法を実演しています。example/lib/flame_example.dart
: Flameゲームエンジンでspine-flutterを使用するための簡単なFlameコンポーネントの書き方を実演しています。
spine-flutterランタイムのアップデート
プロジェクトのspine-flutterランタイムをアップデートする前に、Spineエディターとランタイムのバージョン管理に関するガイドをよく確認してください。
spine-flutterランタイムをアップデートするには、pubspec.yaml
の spine_flutter
パッケージのバージョン文字列を変更してください。
注意:
spine_flutter
パッケージのmajor.minor
バージョンを変更した場合、使用しているSpineスケルトンを同じmajor.minor
バ ージョンのSpineエディターを使って再エクスポートする必要があることに注意してください!
spine-flutterを使用する
spine-flutter ランタイムは、Spine で作成されたアニメーションのロード、再生、操作をサポートする汎用 spine-cpp のイディオム Dart FFI ラッパー です。spine-flutter ランタイムは spine-cpp APIのほとんどすべてをDartのイディオムとして公開し、FlutterおよびFlame固有のクラスを提供して、Spineスケルトンを簡単に表示したり操作できるようにします。
spine-flutterランタイムは、ティントブラックを除き、Spineのすべての機能をサポートしています。
アセットのマネージメント
spine-flutter用にエクスポートする
以下の実行方法については、Spineユーザーガイド内で紹介されています :
スケルトンのデータとテクスチャアトラスをエクスポートすると、以下のファイルが得られます:
skeleton-name.json
またはskeleton-name.skel
: これはスケルトンとアニメーションのデータを含んでいます。skeleton-name.atlas
: これはテクスチャアトラスの情報を含んでいます。- 1つまたは複数の
.png
ファイル: これはテクスチャアトラスの各ページで、スケルトンが使用するイメージを含んでいます。
補足: JSONエクスポートよりもバイナリ形式でのスケルトンエクスポートの方がサイズが小さく、読み込みが速いので、基本的にはそちらを選択したほうが良いでしょう。
これらのファイルを、Atlas
、SkeletonData
、SkeletonDrawable
、SpineWidget
などのspine-flutterクラスを使って読み込むことができます。
注意: spine-flutterランタイムは現在、Flutterの技術的制限により、乗算済みアルファ(Premultiplied alpha)を使用してエクスポートされたアトラスをサポートしていません。Flutterのレンダリングエンジンは、よくある非乗算済みアルファのアーティファクトを確実に回避します。
Spineアセットの更新
開発中にスケルトンデータやテクスチャアトラスファイルを更新したい時は、単純にSpineエディターから再エクスポートを行なって、Flutterプロジェクト内の既存のファイル(.json
、.skel
、.atlas
、.png
)を置き換えるだけで簡単にこれらのソースファイルを更新できます。
その際、spine-flutterの major.minor
バージョンとエクスポートを行ったSpineエディターの major.minor
が一致していることを確認してください。詳しくは「バージョンの同期」を参照してください。
コアクラス
spine-flutterのAPIは汎用spine-cppランタイムの上に構築されており、プラットフォームに依存しないコアクラスとSpineスケルトンのロード、クエリ、変更、アニメーションを行うアルゴリズムを提供します。コアクラスはDart FFIでラップされ、Dartのイディオムクラスとして公開されます。
ここではspine-flutterを使用する際によく見ることになる最も重要なコアクラスについてのみ簡単に説明しています。Spineランタイムのアーキテクチャ、コアクラス、APIの使用法の詳細については、Spineランタイムガイドを参照してください。
Atlas
クラスは、.atlas
ファイルとそれに対応する .png
画像ファイルからロードしたデータを保管します。
SkeletonData
クラスは、.json
または .skel
ファイルからロードされたデータを保管します。このスケルトンデータには、ボーン階層、スロット、アタッチメント、コンストレイント、スキン、アニメーションに関する情報が含まれます。SkeletonData
インスタンスは、通常、それが表すスケルトンで使用されるイメージをソースとする Atlas
(アトラス) も一緒に提供することによってロードされます。これは、Skeleton
インスタンスを作成するための設計図として機能します。複数のスケルトンを同じアトラスとスケルトンデータからインスタンス化し、ロードされたデータを共有することで、ロード時間と実行時のメモリ消費を最小限に抑えることができます。
Skeleton
クラスは、SkeletonData
インスタンスから作成されたスケルトンのインスタンスを格納します。スケルトンは現在のポーズを保管します。つまり、ボーンの位置、スロット、アタッチメント、アクティブなスキンの現在の構成を保管します。現在のポーズは、手動でボーンのトランスフォームを変更するか、より一般的には、AnimationState
を介してアニメーションを適用することで計算されます。
AnimationState
クラスは、 スケルトンに適用する(単数または複数の)アニメーションを追跡し、最後のレンダリングフレームと現在のレンダリングフレームの間の経過時間に基づいてそれらのアニメーションを進め、ミックスを行い、スケルトンインスタンスにアニメーションを適用して現在のポーズを設定します。AnimationState
は AnimationStateData
インスタンスに問い合わせ(処理要求)をして、アニメーション間のミキシング時間を取得します。特定のアニメーション間に使用するミキシング時間が無ければデフォルトミックスタイムを取得します。
spine-flutterランタイムはこれらのコアクラスの上に構築されています。
SpineWidget
SpineWidget
は、Spineスケルトンのロードと表示を担当する StatefulWidget です。最低限、ウィジェットはスケルトンとアトラスファイルをロードする場所を知る必要があり、アニメーションの設定やスケルトンのスキンの変更など、ウィジェットの状態を変更する SpineWidgetController
インスタンスを受け取る必要があります。
最も単純なケースでは、SpineWidget
は次のように他のウィジェットの build()
メソッド内でインスタンス化できます:
Widget build(BuildContext context) {
final controller = SpineWidgetController(onInitialized: (controller) {
// トラック0にwalkアニメーションをセットし、それをループさせます
controller.animationState.setAnimationByName(0, "walk", true);
});
return Scaffold(
appBar: AppBar(title: const Text('Simple Animation')),
body: SpineWidget.fromAsset("assets/spineboy.atlas", "assets/spineboy-pro.skel", controller)
);
}
インスタンス化すると、SpineWidget
は指定されたファイルを非同期にロードし、それらのファイルから基礎となるコアクラスのインスタンス(Atlas
、SkeletonData
、Skeleton
、AnimationStateData
、AnimationState
のインスタンス)を構築します。
ロードが完了すると SpineWidgetController
が呼び出され、(単数または複数の)アニメーションの設定、ボーン階層の操作、スケルトンのスキンの変更など、ウィジェットの状態を変更できるようになります。詳しくは後述の SpineWidgetController
セクションを参照してください。
SpineWidget
クラスは、異なるソースからスケルトンとアトラスファイルをロードするための複数の静的ファクトリーメソッドを提供します:
SpineWidget.fromAsset()
は、ルートバンドルまたは提供されたバンドルからファイルをロードします。SpineWidget.fromFile()
は、ファイルシステムからファイルをロードします。SpineWidget.fromHttp()
は、URLからファイルをロードします。SpineWidget.fromDrawable()
は、SkeletonDrawable
からウィジェットを構築します。これは、スケルトンデータをプリロード、キャッシュ、および/またはSpineWidget
インスタンス間で共有したい場合に便利です。詳しくは後述の「スケルトンデータのプリロードと共有」のセクションを参照してください。
すべてのファクトリーメソッドにはオプションの引数があり、Spineスケルトンをウィジェット内にどのようにフィットさせ、整列させるか、またウィジェットのサイズをどのように設定するかを定義できます。
fit
: ウィジェット内のスケルトンをフィットするために使用するBoxFit。alignment
: ウィジェット内のスケルトンの位置合わせに使用するAlignment。BoundsProvider
: fitとalignmentを計算するときにスケルトンに使用するバウンディングボックスのピクセルサイズを計算するために使用します。デフォルトでは、スケルトンのセットアップポーズのバウンディングボックスが使用されます。詳細は、SetupPoseBounds
、RawBounds
、SkinAndAnimationBounds
のクラスドキュメントを参照してください。sizedByBounds
:BoundsProvider
によって計算された境界によってウィジェットのサイズを決めるか、親ウィジェットによってサイズを決めるかを定義します。
スケルトンデータのプリロードと共有
複数の SpineWidget
インスタンス間でアトラスとスケルトンデータを共有したい場合は、アセットを手動で事前にロードすることができます:
final skeletonData = await SkeletonData.fromAsset("assets/test.json", atlas);
同じデータから1つ以上の SpineWidget
インスタンスをインスタンス化できるので、ロード時間とメモリの節約になります:
ただしそれらを参照する SpineWidget
(または SkeletonDrawable
) が存在しなくなったら、アトラスとスケルトンデータを破棄する必要があるので注意してください。
atlas.dispose();
SpineWidgetController
SpineWidgetController
は、SpineWidget
のスケルトンのアニメーションとレンダリングを制御します。このコントローラーはコンストラクタの引数としてオプションのコールバックセットを提供し、SpineWidget
のライフタイム中に特定のタイミングで呼び出されます。
このコントローラーは、Atlas
、SkeletonData
、Skeleton
、AnimationState
などの Spine Runtimes API オブジェクトを返すゲッターを通して、スケルトンの状態を公開します。詳しくはは、Spineランタイムガイドおよびクラスのドキュメントを参照してください。
SpineWidget
の初期化時に、コントローラーの onInitialized()
コールバックメソッドが一度だけ呼び出されます。このメソッドは、再生する(単数または複数の)アニメーションの初期設定や、スケルトンのスキン設定などに使用できます。
初期化が完了すると、SpineWidget
は画面のリフレッシュレートで連続的にレンダリングされます。毎フレーム AnimationState
が現在キューに入っているアニメーションに基づいて更新され、Skeleton
に適用されます。
次に、オプションの onBeforeUpdateWorldTransforms()
コールバックが呼び出され、Skeleton.updateWorldTransform()
を使用して現在のポーズが計算される前にスケルトンを変更できます。
現在のポーズが計算された後、オプションの onAfterUpdateWorldTransforms()
コールバックが呼び出され、スケルトンがレンダリングされる前に、現在のポーズをさらに修正することができます。手動でボーンの位置を変更したい場合にはこれを利用すると良いでしょう。
SpineWidget
によってスケルトンがレンダリングされる前に、オプションの onBeforePaint()
コールバックが呼び出されます。これを利用して Canvas
上でスケルトンの後ろにあるべき背景や他のオブジェクトをレンダリングすることができます。
SpineWidget
が現在のスケルトンのポーズを Canvas
にレンダリングした後、オプションの onAfterPaint()
コールバックが呼び出されます。これを利用してスケルトンの上に追加のオブジェクトをレンダリングすることができます。
デフォルトでは、ウィジェットはスケルトンを毎フレーム更新してレンダリングします。スケルトンの更新とレンダリングを一時停止したい場合は、SpineWidgetController.pause()
メソッドを使用します。スケルトンの更新とレンダリングを再開するには、SpineWidgetController.resume()
メソッドを使用します。SpineWidgetController.isPlaying()
ゲッターは、現在の再生状態を報告します。詳しくは、サンプル example/lib/animation_state_events.dart
を参照してください。
SkeletonDrawable
SkeletonDrawable
は、Skeleton
のロード、保存、更新、およびレンダリングと、それに関連する AnimationState
を、単一の使いやすいクラスにまとめたものです。このクラスは、カスタムウィジェットの実装の基礎として使用できます。SpineWidget
は、SkeletonDrawable
のインスタンスを介して、表示するスケルトンの状態をカプセル化します。
ファイルアセットから SkeletonDrawable
を構築するには、fromAsset()
、fromFile()
、fromHttp()
メソッドのいずれかを使用します。複数の SkeletonDrawable
インスタンス間で Atlas
と SkeletonData
共有するには、コンストラクタで drawable をインスタンス化し、それぞれに同じアトラスとスケルトン データを渡します。
SkeletonDrawable
は、スケルトンのクエリ、変更、アニメーションを行うために、Skeleton
と AnimationState
を公開します。また、そのスケルトンとAnimationStateを構築するのに使った Atlas
と SkeletonData
も公開します。
スケルトンをアニメーションさせるには、AnimationState.setAnimation()
や AnimationState.addAnimation()
などの AnimationState
のAPIを使用して、単数または複数のトラックにアニメーションをキューに入れます。
アニメーションの状態を更新し、それをスケルトンに適用し、現在のスケルトンのポーズを更新するには、SkeletonDrawable.update()
メソッドを呼び出し、アニメーションを進めるための秒単位のデルタ時間を与えます。
スケルトンの現在のポーズをレンダリングするには、レンダリングメソッド SkeletonDrawable.render()
、SkeletonDrawable.renderToCanvas()
、SkeletonDrawable.renderToPictureRecorder()
、SkeletonDrawable.renderToPng()
、SkeletonDrawable.renderToRawImageData()
のいずれかを使用します。
SkeletonDrawable
は、ネイティブヒープに割り当てられたオブジェクトを格納します。そのため、SkeletonDrawable
が不要になったら、SkeletonDrawable.dispose()
を呼び出して、ネイティブオブジェクトを手動で破棄する必要があります。これを行わないと、ネイティブ メモリリークが発生してしまいます。
補足:
SpineWidget
を使用する場合は、ウィジェットが使用するSkeletonDrawable
を手動で破棄する必要はありません。SkeletonDrawable
は、ウィジェット自身が破棄されるときにウィジェットが破棄します。
アニメーションの適用
SpineWidget
で表示されるスケルトンにアニメーションを適用するには、SpineWidgetController
の コールバックで AnimationState
を使用します。
注意: アニメーショントラックやアニメーションのキューイングなど、より詳しい情報については、Spineランタイムガイドのアニメーションの適用を参照してください。
トラック0に特定のアニメーションを設定するには、AnimationState.setAnimation()
を呼び出します:
// トラック0にwalkアニメーションをセットし、それをループさせます
controller.animationState.setAnimationByName(0, "walk", true);
});
最初のパラメーターはトラック、2番目のパラメーターはアニメーションの名前、3番目のパラメーターはアニメーションをループさせるかどうかを指定します。
複数のアニメーションをキューに入れることもできます:
controller.animationState.addAnimationByName(0, "jump", false, 2);
controller.animationState.addAnimationByName(0, "run", true, 0);
addAnimationByName()
の最初のパラメーターはトラックです。2番目のパラメーターはアニメーションの名前です。3番目のパラメーターは、このアニメーションが同じトラック上の前のアニメーションと置き換わるまでの時間(ディレイ)を秒単位で指定します。最後のパラメーターはアニメーションをループさせるかどうかを指定します。
上の例では、まず "walk"
アニメーションが再生されます。その2秒後に "jump"
アニメーションが一度再生され、続いて "run"
アニメーションに切り替わり、ループします。
あるアニメーションから別のアニメーションに遷移するとき、AnimationState
はミックスタイムと呼ばれる特定の時間だけアニメーションをミックスします。これらのミックスタイムは AnimationStateData
インスタンスで定義され、AnimationState
はそこからミックスタイムを取得します。
AnimationStateData
インスタンスはコントローラーを介して利用することもできます。デフォルトのミックスタイムや、特定のアニメーションのペアのミックスタイムを設定することができます:
controller.animationStateData.setMixByName("walk", "jump", 0.1);
アニメーションを設定または追加すると、TrackEntry
オブジェクトが返されます。これを利用してアニメーションの再生をさらに変更できます。例えば、アニメーションを逆再生するようにTrackEntryを設定することができます:
entry.setReverse(true);
利用できるオプションについて詳しくは TrackEntry
クラスのドキュメントをご覧ください。
注意:
TrackEntry
インスタンスを使用している関数の外部で保持しないでください。TrackEntryは内部で再利用されるため、それが表すアニメーションが完了すると無効になります。
スケルトンをスムーズにセットアップポーズに戻したい場合は、アニメーショントラックに空のアニメーションをセットまたはキューに追加します:
controller.animationState.addEmptyAnimation(0, 0.5, 0.5);
setEmptyAnimation()
の最初のパラメーターはトラックを指定します。2番目のパラメーターは、前のアニメーションをミックスアウトし、"空の"アニメーションをミックスするために使用するミックスタイムを秒単位で指定します。
addEmptyAnimation()
の最初のパラメーターはトラックを指定します。2番目のパラメーターはミックス時間を指定します。3番目のパラメーターはディレイ(秒単位)で、このディレイの後に空のアニメーションがミキシングされてトラック上の前のアニメーションと置き換わります。
AnimationState.clearTrack()
を使えばトラック上のすべてのアニメーションを即座にクリアすることができます。すべてのトラックを一度にクリアするには AnimationState.clearTracks()
を使います。しかしこれはスケルトンを最後に適用されたポーズのままにする点に注意してください。
スケルトンのポーズをセットアップポーズに戻すには、Skeleton.setToSetupPose()
を使います:
これはボーンとスロットの両方をセットアップポーズの設定にリセットします。スロットだけをセットアップポーズの設定にリセットしたい場合は Skeleton.setSlotsToSetupPose()
を使用してください。
AnimationStateイベント
AnimationState
は、再生中のアニメーションのライフサイクル中に様々なイベントを発行します。必要に応じてこのイベントをリッスンすることで、それらに反応させることができます。SpineランタイムのAPIでは、以下のイベントタイプを定義しています:
start
: アニメーションが開始された時に発されます。interrupt
: アニメーションのトラックがクリアされた、または新しいアニメーションが設定されたなどにより中断された時に発されます。end
: アニメーションが二度と適用されない時に発されます。dispose
: アニメーションのTrackEntryが破棄された時に発されます。complete
: アニメーションが1ループを完了するごとに発されます。event
: ユーザーが定義したイベントが発生した時に発されます。
イベントを受け取るには、AnimationStateListener
コールバックを、すべてのアニメーションでイベントを受信するAnimationState
か、キューされた特定のアニメーションのTrackEntryに登録します:
entry.setListener((type, trackEntry, event) {
if (type == EventType.event) {
print("User defined event: ${event?.getData().getName()}");
}
});
controller.animationState.setListener((type, trackEntry, event) {
print("Animation state event $type");
});
詳しくは example/lib/animation_state_events.dart
をご覧ください。
スキン
多くのアプリケーションやゲームでは、髪や目、ズボン、イヤリングやバッグなどのアクセサリーなど、さまざまなアイテムを組み合わせてカスタムアバターを作ることができます。Spineでは、複数スキンを組み合わせることでこれを実現することができます。
以下のようにして、他のスキンからカスタムスキンを作成することができます:
final skeleton = controller.skeleton;
final customSkin = Skin("custom-skin");
customSkin.addSkin(data.findSkin("skin-base")!);
customSkin.addSkin(data.findSkin("nose/short")!);
customSkin.addSkin(data.findSkin("eyelids/girly")!);
customSkin.addSkin(data.findSkin("eyes/violet")!);
customSkin.addSkin(data.findSkin("hair/brown")!);
customSkin.addSkin(data.findSkin("clothes/hoodie-orange")!);
customSkin.addSkin(data.findSkin("legs/pants-jeans")!);
customSkin.addSkin(data.findSkin("accessories/bag")!);
customSkin.addSkin(data.findSkin("accessories/hat-red-yellow")!);
skeleton.setSkin(customSkin);
skeleton.setSlotsToSetupPose();
まず、コンストラクタ Skin()
でカスタムスキンを作成します。
次に、スケルトンから SkeletonData
を取得します。これは SkeletonData.findSkin()
でスキンを名前から探すのに使用します。
Skin.addSkin()
で新しいカスタムスキンにまとめたいスキンをすべて追加します。
最後に、Skeleton
に出来上がった新しいスキンをセットし、Skeleton.setSlotsToSetupPose()
を呼び出して、以前のスキンやアニメーションのアタッチメントが残らないようにします。
注意:
Skin
はC++オブジェクトをラップします。不要になったらSkin.dispose()
を呼び出して手動で破棄する必要があります。
詳しくは example/lib/dress_up.dart
をご覧ください。これは SkeletonDrawable
を使用してスキンのサムネイルプレビューをレンダリングする方法も実演しています。
ボーンのトランスフォームの設定
Spineエディター内でスケルトンを構築する際、スケルトンは、スケルトンのワールド座標系または「スケルトン座標系」と呼ばれるもので定義されます。この座標系は、スケルトンがレンダリングされる SpineWidget
の座標系と一致しない場合があります。そのため、例えば、ユーザーがタッチでボーンを動かせるようにする場合などは SpineWidget
に相対するマウス座標やタッチ座標をスケルトン座標系に変換する必要があります。
SpineWidgetController
は toSkeletonCoordinates()
というメソッドを提供しており、関連する SpineWidget
からの相対的な Offset
を取得し、それをスケルトン座標系に変換します。
詳しくは example/lib/ik_following.dart
をご覧ください。
Flame Integration
spine-flutterには、Flame EngineでSpineスケルトンをロードしてレンダリングする方法を実演しているサンプルが含まれています。詳しくはソースファイル example/lib/flame_example.dart
を参照してください。
この例では、Flameの PositionComponent
を継承したシンプルな SpineComponent
を使用しています。SpineComponent
は、静的な SpineComponent.fromAsset()
メソッド、またはコンストラクタからインスタンス化できます。
この静的メソッドは、スケルトンとアトラスのデータを他のコンポーネントと共有する必要がない場合に、迅速な1回限りのロードメカニズムとして使用できます。このサンプルには SimpleFlameExample
という FlameGame
の実装が含まれており、Flameゲームの一部として画面上にSpineスケルトンを表示するシンプルな方法を実演しています。
コンストラクタで SpineComponent
を作成すると、SkeletonDrawable
を受け取ることで、データのロードと共有をより細かく管理できます。例えば、スケルトンデータとアトラスを事前にロードし、それを複数の SpineComponent
インスタンスで共有できます。これにより、データが共有され、レンダリングがバッチ処理されるため、メモリ使用量とレンダリングパフォーマンスの両方が改善されます。詳しくは PreloadAndShareSpineDataExample
という FlameGame
の実装例を参照してください。
設計上、Flame はコンポーネントがいつ寿命に達したかを知ることができません。しかし、SpineComponent
は寿命が来たときに解放する必要があるネイティブリソースを扱います。そのため、SpineComponent
が使用されなくなった場合は、SpineComponent.dispose()
を呼び出す必要があります。SpineComponent
が SkeletonDrawable
から構築された場合は、PreloadAndShareSpineDataExample
の例のように、構築元の SkeletonData
と Atlas
も手動で破棄する必要があります。
SpineランタイムのAPI呼び出し
spine-flutterは、SpineランタイムAPIのほとんどすべてをDartにマッピングしています。例えば Skeleton
や AnimationState
のように、SpineWidgetController
や SkeletonDrawable
が返すオブジェクトは、spine-cppのAPIをDartに1:1変換したものです。そのため、一般的なSpineランタイムガイドにある資料のほとんどすべてをDartコードに適用できます。
しかしながら、spine-cppからDart FFIへのブリッジの性質上、以下のような制限があります:
- 返される配列やマップはすべて内部配列のコピーです。それらを変更しても影響はありません。しかし、返される
Float32List
やInt32List
のインスタンスはネイティブメモリのラッパーであり、ネイティブデータを変更するために使用することができます。 - ボーンやスロット、その他のSpineオブジェクトを直接、作成、追加、削除することはできません。
- タイムラインのC++クラス階層はDartでは公開されていません。