spine-android ランタイム ドキュメント
ライセンスについて
Spineランタイムをアプリケーションに組み込む前に、必ずSpine Runtimes Licenseを確認してください。
はじめに
spine-androidランタイムはspine-libgdxの上に実装されており、 Spineで作成されたアニメーションのロード、再生、操作をサポートしています。
インストール方法
このランタイムは最低限のSDKバージョン24(Android 7.0、Nougat)をサポートしています。 Spineの機能を利用するには、プロジェクトのターゲットSDKバージョンをこのバージョン以上に設定してください。
Androidアプリプロジェクトでspine-androidを使用するには、まず、mavenCentralとSonaTypeのスナップショットリポジトリをGradleプロジェクトに追加します:
mavenCentral()
maven {
url = uri("https://oss.sonatype.org/content/repositories/snapshots")
}
}
次に、アプリの build.gradle ファイルに spine-android の依存関係を追加します:
implementation(""com.esotericsoftware.spine:spine-android:4.2.+"")
}
これにより、Spineエディターバージョン4.2と互換性のある最新のspine-androidバージョンがダウンロードされます。一般的に、これはspine-runtimesリポジトリの最新コミットに基づくスナップショットバージョンとなります。当社のスナップショットリリースは安定しており、頻繁に公開されています。それでもスナップショットバージョンではなくリリースバージョンに依存したい場合、または依存する必要がある場合は、ご使用のSpineエディターバージョン用の最新リリース版をMaven Centralで見つけることができます。
spine-androidのmajor.minorバージョンとエクスポートを行ったSpineエディターのmajor.minorが一致していることを確認してください。詳しくは「バージョンの同期」を参照してください。
サンプル
spine-androidランタイムには、利用できる機能セットを実演しているサンプルがいくつか含まれています。サンプルプロジェクトを実行するには、以下の手順に従ってください:
- Android Studioをインストールします
- spine-runtimesリポジトリをクローンします:
git clone https://github.com/esotericsoftware/spine-runtimes - Android Studio内で
spine-android/フォルダを開きます app構成を実行します。
すべての例は、KotlinとJetpack Composeを組み合わせて記述されていますが、spine-androidは Java やプレーンなAndroidビューとも互換性があります。サンプルプロジェクトには、以下の例が含まれています:
SimpleAnimation.kt: エクスポートしたSpineスケルトンをロードして、ビューに表示し、特定のアニメーションを再生するためのSpineViewとSpineControllerの基本的な使用方法を実演しています。PlayPause.kt: アニメーションの一時停止と再開の方法を実演しています。AnimationStateEvents.kt: スロットカラーを設定する方法、複数のアニメーションをキューに入れる方法、アニメーションステートイベントをリッスンする方法を実演しています。DebugRendering.kt:SpineControlleronAfterPaintコールバックを介して、レンダリングされたスケルトンの上にカスタム描画を実行する方法を実演しています。DressUp.kt: Spineのスキン機能と、キャラクター作成UIで使用するためにスケルトンをサムネイルにレンダリングする方法を実演しています。IKFollowing.kt: マウスまたはタッチ入力でスケルトンのボーンをドラッグさせる方法を実演しています。DisableRendering.kt:SpineViewが画面外に移動したときにレンダリングを無効にする方法を実演しています。これは、CPU/GPUリソースを維持する必要がある場合に重要です。SimpleAnimationActivity.java:XMLベースの相対レイアウト内のSpineViewをJavaでspine-androidを使用する方法を実演しています。
spine-androidランタイムのアップデート
プロジェクトのspine-androidランタイムをアップデートする前に、Spineエディターとランタイムのバージョン管理に関するガイドを参照してください。
spine-androidランタイムを更新するには、単に build.gradle 依存関係のspine-androidパッケージのバージョン文字列を変更してください。
注意: spine-androidパッケージの
major.minorバージョンを変更した場合、使用しているSpineスケルトンを同じmajor.minorバージョンのSpineエディターを使って再エクスポートする必要があることに注意してください!
spine-androidを使用する
spine-androidランタイムはspine-libgdxの上に構築されており、libGDXにも依存しています。Spineで作成したアニメーションのロード、再生、操作をサポートします。 spine-androidランタイムは spine-libgdx を使用して、AndroidでSpineのスケルトンを簡単に表示し、操作することができます。
アセットのマネージメント
spine-android用にエクスポートする
以下の実行方法については、Spineユーザーガイド内で紹介されています :
スケルトンのデータとテクスチャアトラスをエクスポートすると、以下のファイルが得られます:

skeleton-name.jsonまたはskeleton-name.skel: これはスケルトンとアニメーションのデータを含んでいます。skeleton-name.atlas: これはテクスチャアトラスの情報を含んでいます。- 1つまたは複数の
.pngファイル: これはテクスチャアトラスの各ページで、スケルトンが使用するイメージを含んでいます。
補足: JSONエクスポートよりもバイナリ形式でのスケルトンエクスポートの方がサイズが小さく、読み込みが速いので、基本的にはそちらを選択したほうが良いでしょう。
ファイルは、AndroidTextureAtlas、SkeletonDataUtils、AndroidSkeletonDrawable、SpineView のようなspine-androidクラスを介してロードすることができます。
注意: spine-android ランタイムは現在、Android の技術的制限により、乗算済みアルファを使用してエクスポートされたアトラスをサポートしていません。Android のレンダリングエンジンでは、一般的な非乗算済みアルファのアーティファクトは回避されます。
Spineアセットの更新
開発中にスケルトンデータやテクスチャアトラスファイルを更新したい時は、単純にSpineエディターから再エクスポートを行なって、Androidプロジェクト内の既存のファイル(.json、.skel、.atlas、.png)を置き換えるだけで簡単にこれらのソースファイルを更新できます。
その際、spine-androidの major.minor バージョンとエクスポートを行ったSpineエディターの major.minor が一致していることを確認してください。詳しくは「バージョンの同期」を参照してください。
コアクラス
spine-androidのAPIはspine-libgdxの上に構築されており、Spineスケルトンのロード、クエリ、変更、アニメーションのためのコアクラスとアルゴリズムを提供しています。
ここでは、spine-androidの日々の使用で遭遇する最も重要なコアクラスについて簡単に説明します。Spineランタイムのアーキテクチャ、コアクラス、APIの使用法の詳細については、Spineランタイムガイドを参照してください。
spine-androidクラス
AndroidSkeletonDrawable クラスは、AndroidTextureAtlas、Skeleton、AnimationState のロードと更新を、ひとつにまとめて簡単に扱えるクラスです。
AndroidTextureAtlas クラスは、.atlas ファイルおよび対応する .png 画像ファイルからロードされたデータを保持します。
SkeletonDataUtils クラスは、スケルトンファイルのアセットから SkeletonData クラスを読み込むための静的関数を提供します。
SkeletonRenderer クラスは、 Skeleton を現在のポーズで変換し、それを SkeletonRenderer.RenderCommand コマンドに変換して Canvas に描画する役割を担います。
これらはすべて spine-androidの一部であり、spine-libgdx の拡張、またはその上にAndroid固有の機能を追加するものです。
spine-libgdxクラス
SkeletonData クラスは、 .json または .skel スケルトンファイルから読み込まれたデータを保持します。スケルトンデータには、ボーンの階層構造、スロット、アタッチメント、コンストレイント、スキン、アニメーションに関する情報が含まれます。SkeletonData インスタンスは通常、スケルトンが使用する画像を取得するための Atlas を併せて指定することで読み込まれます。このデータは Skeleton インスタンスを作成するための設計図となります。同じアトラスとスケルトンデータから複数のスケルトンをインスタンス化することが可能で、これによりロード時間と実行時のメモリ使用量の両方を最小限に抑えることができます。
Skeleton クラスは、SkeletonData インスタンスから作成されたスケルトンのインスタンスを保持します。スケルトンは現在のポーズ、すなわちボーンの位置、スロットの構成、アタッチメント、アクティブなスキンの状態を保持します。現在のポーズは、ボーン階層を手動で変更するか、より一般的には AnimationState を使ってアニメーションを適用することで計算されます。
AnimationState クラスは、どのアニメーションをスケルトンに適用すべきかを管理し、前と現在の描画フレーム間の経過時間に基づいてアニメーションを進行・ミックスし、それらをスケルトンインスタンスに適用して現在のポーズを設定する役割を持ちます。AnimationState は AnimationStateData インスタンスからアニメーション間のミックス時間を取得し、対応するミックス時間がない場合はデフォルトのミックス時間を使用します。
spine-androidランタイムは、これらのコアクラスの上に構築されています。
SpineView

SpineView は、Spineスケルトンのロードと表示を担当する Android Viewです。また、アニメーションの設定やスケルトンのスキンの変更など、ビューの状態の変更を行う SpineController インスタンスを受け取る必要があります。
Jetpack Compose を使用する場合、 SpineView は AndroidView 内でインスタンス化できます:
factory = { context ->
SpineView.loadFromAssets(
"spineboy.atlas",
"spineboy-pro.json",
context,
SpineController {
it.animationState.setAnimation(0, "walk", true)
}
)
}
)
インスタンス化すると、SpineView は指定されたファイルを非同期にロードし、そのファイルから基礎となるコアクラスのインスタンス、すなわち AndroidTextureAtlas、SkeletonData、Skeleton、AnimationStateData、および AnimationState のインスタンスを構築します。
ロードが完了すると、SpineController コンストラクタのコールバック onInitialized が呼び出され、1つ以上のアニメーションの設定、ボーン階層の操作、スケルトンのスキンの変更など、ビューの状態を変更できるようになります。詳しくは後述のSpineController セクションを参照してください。
SpineView クラスは、異なるソースからスケルトンとアトラスファイルをロードするための複数の静的ファクトリーメソッドを提供します:
SpineView.loadFromAsset()は、ルートバンドルまたは指定されたバンドルからファイルをロードします。SpineView.loadFromFile()ファイルシステムからファイルをロードします。SpineView.loadFromHttp()URLからファイルをロードします。SpineView.loadFromDrawable()は、AndroidSkeletonDrawableからビューを構築します。これは、スケルトンデータをプリロード、キャッシュ、および/またはSpineViewインスタンス間で共有する場合に便利です。詳しくはDisableRendering.ktサンプルを参照してください。
Spineスケルトンをウィジェット内でどのようにフィットさせ、整列させるかをさらに定義する必要がある場合、以下のように SpineView のセッターを使用できます:
spineView.setContentMode(ContentMode.FILL)
spineView.setAlignment(Alignment.BOTTOM_CENTER)
spineView.setBoundsProvider(SkinAndAnimationBounds("flying"))
または SpineView.Builder を使用する場合:
.setLoadFromAssets("dragon.atlas", "dragon-ess.skel")
.setContentMode(ContentMode.FILL)
.setAlignment(Alignment.BOTTOM_CENTER)
.setBoundsProvider(SkinAndAnimationBounds("flying"))
.build()
setContentModeは、スケルトンをビュー内に収めるために使用するContentModeを設定します。setAlignmentは、スケルトンをビュー内で整列させるために使用するAlignmentを設定します。setBoundsProviderは、フィットおよび整列を計算する際に、スケルトン用のバウンディングボックスのピクセルサイズを算出するために使用されるBoundsProviderを設定します。デフォルトでは、スケルトンのセットアップポーズのバウンディングボックスが使用されます。詳細については、SetupPoseBoundsおよびSkinAndAnimationBoundsのクラスドキュメントを参照してください。
SpineView には isRendering/setRendering というゲッター/セッターのペアがあり、これを使って描画を無効にすることができます。Jetpack Compose において isRendering のような SpineView のプロパティを更新するには、 AndroidView.update パラメータを追加してください:
factory = ...
update = { spineView ->
spineView.isRendering = isSpineBoyVisible.value
}
)
詳しくは DisableRendering.kt サンプルを参照してください。
SpineController
SpineController は、 SpineView のスケルトンがどのようにアニメーションされ、描画されるかを制御します。コントローラーには、メソッドインジェクションまたは SpineController.Builder クラスを通じて、任意のコールバック群を指定することができます。これらのコールバックは SpineView のライフサイクル中の特定のタイミングで呼び出されます。
このコントローラーは、AndroidTextureAtlas、SkeletonData、Skeleton、AnimationState など、Spine Runtimes API オブジェクトを返すゲッターを通じてスケルトンの状態を公開しており、それによって状態の操作が可能になります。詳細は Spineランタイムガイドおよび各クラスのドキュメントを参照してください。
SpineView の初期化時には、コントローラーの onInitialized() コールバックメソッドが一度呼び出されます。このメソッドを使用して、再生する初期アニメーションやスケルトンのスキンの設定などを行うことができます。
初期化が完了すると、SpineView は画面のリフレッシュレートに合わせて継続的に描画されます。各フレームで AnimationState が現在キューにあるアニメーションに基づいて更新され、それが Skeleton に適用されます。
次に、任意の onBeforeUpdateWorldTransforms() コールバックが呼び出されます。これは Skeleton.updateWorldTransform() を使って現在のポーズを計算する前にスケルトンを変更するためのもので、SpineController.setOnBeforeUpdateWorldTransforms() または SpineController.Builder.setOnBeforeUpdateWorldTransforms() を使って設定できます。
現在のポーズが計算された後には、任意の onAfterUpdateWorldTransforms() コールバックが呼び出され、スケルトンが描画される前にポーズをさらに変更することができます。ここはボーンの位置を手動で調整するのに適した場所です。設定には SpineController.setOnAfterUpdateWorldTransforms() または SpineController.Builder.setOnAfterUpdateWorldTransforms() を使います。
スケルトンが SpineView によって描画される直前には、任意の onBeforePaint() コールバックが呼び出されます。これにより、Canvas 上にスケルトンの背後に表示すべき背景や他のオブジェクトを描画することができます。設定には SpineController.setOnBeforePaint() または SpineController.Builder.setOnBeforePaint() を使用します。
SpineView が現在のスケルトンポーズを Canvas に描画した後には、任意の onAfterPaint() コールバックが呼び出され、スケルトンの上に追加のオブジェクトを描画することができます。これは SpineController.setOnAfterPaint() または SpineController.Builder.setOnAfterPaint() により設定可能です。
デフォルトでは、ビューは毎フレームごとにスケルトンを更新し、描画します。SpineController.pause() メソッドを使うと、スケルトンの更新と描画を一時停止できます。SpineController.resume() メソッドで再開が可能です。現在の再生状態は、SpineController.isPlaying() ゲッターで確認できます。詳しくは PlayPause.kt のサンプルを参照してください。
AndroidSkeletonDrawable
AndroidSkeletonDrawable は、Skeleton とそれに関連する AnimationState のロード、保持、更新、描画をひとまとめにした、使いやすいクラスです。このクラスは、カスタムビューを実装する際の基礎として使用することができます。SpineView は、表示するスケルトンの状態を AndroidSkeletonDrawable のインスタンスを通じてカプセル化しています。
ファイルアセットから AndroidSkeletonDrawable を作成するには、fromAsset()、fromFile()、fromHttp() の静的関数を使用します。複数の AndroidSkeletonDrawable インスタンス間で AndroidTextureAtlas や SkeletonData を共有するには、同じアトラスとスケルトンデータをコンストラクタに渡してインスタンスを生成してください。
AndroidSkeletonDrawable は、スケルトンを参照・変更・アニメーションするために、Skeleton と AnimationState を公開しています。また、スケルトンとAnimationStateの構築元となる AndroidTextureAtlas と SkeletonData も公開されています。
スケルトンをアニメーションさせるには、AnimationState.setAnimation() や AnimationState.addAnimation() などの AnimationState APIを使って、1つまたは複数のトラックにアニメーションをキューに追加します。
AnimationStateを更新し、それをスケルトンに適用し、現在のスケルトンポーズを更新するには、AndroidSkeletonDrawable.update() メソッドを呼び出し、アニメーションを進行させるためのデルタ秒(経過時間)を渡します。
アニメーションの適用
SpineView で表示されているスケルトンにアニメーションを適用するには、SpineController のコールバックでAnimationState を使用します。
注意: アニメーショントラックやアニメーションのキューイングなど、より詳しい情報については、Spineランタイムガイドのアニメーションの適用を参照してください。
トラック0に特定のアニメーションを設定するには、 AnimationState.setAnimation() を呼び出します:
// Set the walk animation on track 0, let it loop
initializedController.animationState.setAnimation(0, "walk", true)
}
最初のパラメーターはトラック、2番目のパラメーターはアニメーションの名前、3番目のパラメーターはアニメーションをループさせるかどうかを指定します。
複数のアニメーションをキューに入れることもできます:
controller.animationState.addAnimation(0, "jump", false, 2f)
controller.animationState.addAnimation(0, "run", true, 0f)
addAnimationByName() の最初のパラメーターはトラックです。2番目のパラメーターはアニメーションの名前です。3番目のパラメーターはアニメーションをループさせるかどうかを指定します。最後のパラメーターは、このアニメーションが同じトラック上の前のアニメーションと置き換わるまでの時間(ディレイ)を秒単位で指定します。
上の例では、まず "walk" アニメーションが再生されます。その2秒後に "jump" アニメーションが一度再生され、続いて "run" アニメーションに切り替わり、ループします。
あるアニメーションから別のアニメーションに遷移するとき、AnimationState はミックスタイムと呼ばれる特定の時間だけアニメーションをミックスします。これらのミックスタイムは AnimationStateData インスタンスで定義され、AnimationState はそこからミックスタイムを取得します。
AnimationStateData インスタンスはコントローラーを介して利用することもできます。デフォルトのミックスタイムや、特定のアニメーションのペアのミックスタイムを設定することができます:
controller.animationStateData.setMix("walk", "jump", 0.1f)
アニメーションを設定または追加すると、AnimationState.TrackEntry オブジェクトが返されます。これを利用してアニメーションの再生をさらに変更できます。例えば、アニメーションを逆再生するようにTrackEntryを設定することができます:
entry.setReverse(true)
利用できるオプションについて詳しくは AnimationState.TrackEntry クラスのドキュメントをご覧ください。
注意:
AnimationState.TrackEntryインスタンスを使用している関数の外部で保持しないでください。TrackEntryは内部で再利用されるため、それが表すアニメーションが完了すると無効になります。
スケルトンをスムーズにセットアップポーズに戻したい場合は、アニメーショントラックに空のアニメーションをセットまたはキューに追加します:
controller.animationState.addEmptyAnimation(0, 0.5f, 0.5f)
setEmptyAnimation() の最初のパラメーターはトラックを指定します。2番目のパラメーターは、前のアニメーションをミックスアウトし、"空の"アニメーションをミックスするために使用するミックスタイムを秒単位で指定します。
addEmptyAnimation() の最初のパラメーターはトラックを指定します。2番目のパラメーターはミックス時間を指定します。3番目のパラメーターはディレイ(秒単位)で、このディレイの後に空のアニメーションがミキシングされてトラック上の前のアニメーションと置き換わります。
AnimationState.clearTrack() を使えばトラック上のすべてのアニメーションを即座にクリアすることができます。すべてのトラックを一度にクリアするには AnimationState.clearTracks() を使います。しかしこれはスケルトンを最後に適用されたポーズのままにする点に注意してください。
スケルトンのポーズをセットアップポーズに戻すには、Skeleton.setToSetupPose() を使います:
これはボーンとスロットの両方をセットアップポーズの設定にリセットします。スロットだけをセットアップポーズの設定にリセットしたい場合は Skeleton.setSlotsToSetupPose() を使用してください。
AnimationStateイベント
AnimationState は、再生中のアニメーションのライフサイクル中に様々なイベントを発行します。必要に応じてこのイベントをリッスンすることで、それらに反応させることができます。SpineランタイムのAPIでは、イベント用のコールバック付きの AnimationState.AnimationStateListener インターフェースを定義しています:
start: アニメーションが開始された時に発されます。interrupt: アニメーションのトラックがクリアされた、または新しいアニメーションが設定されたなどにより中断された時に発されます。end: アニメーションが二度と適用されない時に発されます。dispose: アニメーションのTrackEntryが破棄された時に発されます。complete: アニメーションが1ループを完了するごとに発されます。event: ユーザーが定義したイベントが発生した時に発されます。
イベントを受け取るには、すべてのアニメーションにわたってイベントを受信する AnimationState、または再生キューに追加された特定のアニメーションの AnimationState.TrackEntry のどちらかに AnimationState.AnimationStateListener コールバックを登録します:
entry.setListener(object : AnimationState.AnimationStateListener {
override fun start(entry: AnimationState.TrackEntry?) {}
override fun interrupt(entry: AnimationState.TrackEntry?) {}
override fun end(entry: AnimationState.TrackEntry?) {}
override fun dispose(entry: AnimationState.TrackEntry?) {}
override fun complete(entry: AnimationState.TrackEntry?) {}
override fun event(entry: AnimationState.TrackEntry?, event: Event?) {
if (event != null) {
print("User defined event: ${event.data.name}");
}
}
})
controller.animationState.addListener(object : AnimationState.AnimationStateListener {
override fun start(entry: AnimationState.TrackEntry?) {}
override fun interrupt(entry: AnimationState.TrackEntry?) {}
override fun end(entry: AnimationState.TrackEntry?) {}
override fun dispose(entry: AnimationState.TrackEntry?) {}
override fun complete(entry: AnimationState.TrackEntry?) {}
override fun event(entry: AnimationState.TrackEntry?, event: Event?) {
if (event != null) {
print("Animation state event $event")
}
}
})
詳しくは AnimationStateEvents.kt をご覧ください。
スキン

多くのアプリケーションやゲームでは、髪や目、ズボン、イヤリングやバッグなどのアクセサリーなど、さまざまなアイテムを組み合わせてカスタムアバターを作ることができます。Spineでは、複数スキンを組み合わせることでこれを実現することができます。
以下のようにして、他のスキンからカスタムスキンを作成することができます:
val skeleton = controller.skeleton
val 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() を呼び出して、以前のスキンやアニメーションのアタッチメントが残らないようにします。
DressUp.kt の例では、AndroidSkeletonDrawable を使ってスキンのサムネイルプレビューを描画する方法もあわせて紹介されています。
ボーンのトランスフォームの設定

Spineエディターでスケルトンを作成する際、スケルトンは「スケルトン座標系」と呼ばれる座標系で定義されます。この座標系は、スケルトンが描画される SpineView の座標系と一致しない場合があります。そのため、ユーザーがタッチでボーンを動かせるようにする場合など、SpineView を基準としたタッチ座標をスケルトンの座標系に変換する必要があります。
SpineController は toSkeletonCoordinates() メソッドを提供しており、これはそのコントローラーに関連付けられた SpineView を基準とした Point を受け取り、それをスケルトン座標系に変換します。
IKFollowing.kt の例をご覧ください。
SpineRendererを使用したレンダリング
スケルトンを SpineView の外で描画する必要がある場合(たとえば Canvas や Bitmap に描画する場合)、SkeletonRenderer を使用できます。
スケルトンの現在のポーズを描画するには、SkeletonDrawable.render() を使って SkeletonRenderer.RenderCommand オブジェクトのリストを作成します。これらを用いて、Canvas に描画する場合は SkeletonDrawable.renderToCanvas()、Bitmap に描画する場合は SkeletonDrawable.renderToBitmap() を呼び出します。
SpineランタイムのAPI呼び出し
spine-android APIはspine-libgdxの上に構築されているため、spine-libgdxが提供するすべてのAPIを使用できます。詳しくはSpineランタイムガイドを参照してください。