Spine
Esoteric SoftwareのSpineは、2Dスケルタルアニメーションのためのエディターです。playcanvas-spineプラグインを使うと、Spineからエクスポートしたアニメーションを、WebGL2とWebGPUの両方でPlayCanvas上で再生できます。

Spine 4.3のサンプル。Esoteric SoftwareのサンプルプロジェクトであるCelestial Circus、Diamond、Tankを表示しています。
プラグインはエンジンにspineコンポーネントを追加します。また、spine-coreランタイムをグローバル変数spineとして公開するため、スクリプトからSpineのAPIをすべて使用できます。サポートされている各Spineバージョンのサンプルも参照してください。
バージョン
アニメーションのエクスポートに使用したSpineエディターのバージョンに対応するプラグインビルドを使用してください。ビルドはリポジトリのbuildフォルダーにあり、それぞれ縮小版の.min.jsファイルも用意されています。
| Spineエディター | プラグイン |
|---|---|
| 4.3 | playcanvas-spine.4.3.js |
| 4.2 | playcanvas-spine.4.2.js |
| 4.1 | playcanvas-spine.4.1.js |
| 4.0 | playcanvas-spine.4.0.js |
| 3.8 | playcanvas-spine.3.8.js |
| 3.6 | playcanvas-spine.3.6.js |
Spineからのエクスポート
各スケルトンをJSONとしてエクスポートし、テクスチャアトラス (.atlasファイルと1つ以上の.pngページ) も一緒にエクスポートします。アンチエイリアスされたエッジを最も高品質に表示できるため、乗算済みアルファ (Premultiplied alpha) を推奨します。
Spine 4.3プラグインは、バイナリの.skel形式でエクスポートされたスケルトンも読み込めます。この形式はJSONより数倍小さく、読み込みも高速です。それ以前のSpineバージョンのプラグインは、バイナリスケルトンを渡されるとエラーをスローします。
テクスチャ
テクスチャアセットは正しいsRGB設定で読み込む必要があります。そうしないと、スケルトンが正しい色で描画されません。
- Spine 4.3: sRGBを無効にします。SpineエディターやSpineランタイムと同様に、4.3プラグインはガンマ空間で描画します。アトラスのテクスチャがsRGBの場合は警告がログに出力されます。
- Spine 3.6〜4.2: sRGBを有効にします。これはEditorにインポートしたテクスチャのデフォルトです。
プラグインはアトラスの各ページのテクスチャを名前で探すため、テクスチャアセットにはspineboy-pma.pngのように画像ファイルの名前を付ける必要があります。Editorにインポートしたテクスチャには、ファイル名がそのまま付けられます。
EditorでSpineを使う
- 使用するSpineバージョンのプラグインビルドとspine.jsをプロジェクトに追加します。
- スクリプトの読み込み順序で、プラグインが
spine.jsより先に読み込まれるようにします。 - エクスポートした
.jsonまたは.skel、.atlas、.pngファイルをインポートします。アトラスはテキストアセットとして、.skelスケルトンはバイナリアセットとしてインポートされます。Spine 4.3では、インポートしたテクスチャのsRGBを無効にします。 - エンティティにスクリプトコンポーネントを追加して
spineスクリプトを割り当て、その属性にアトラス、スケルトン、テクスチャの各アセットを設定します。バイナリスケルトンはskeletonの代わりにskeletonBinary属性に設定します。priority属性は重なり合うスケルトンの描画順を制御し、physicsInheritance属性はエンティティの移動にPhysicsを反応させます。
spineスクリプトは初期化時にspineコンポーネントを追加するため、アニメーションは別のスクリプトのpostInitializeメソッドから制御します。
import { Script } from 'playcanvas';
export class PlayRun extends Script {
static scriptName = 'playRun';
postInitialize() {
this.entity.spine.state.setAnimation(0, 'run', true);
}
}
Spineアニメーションチュートリアルは、バイナリとJSONのスケルトン、およびそのアニメーションを制御するパネルを含む、完全なEditorプロジェクトです。
エンジンのみのプロジェクトでSpineを使う
プラグインはグローバル変数pcを通してエンジンを使用するため、エンジンをモジュールとしてインポートする場合は、プラグインを読み込む前に代入しておきます。プラグインは読み込まれた時点でアプリケーションにspineコンポーネントシステムを追加するため、アプリケーションを作成した後にスクリプトアセットとして読み込みます。AppBaseで作成したアプリケーションでこれらのアセットを読み込むには、ScriptHandler、JsonHandler、TextHandler、TextureHandlerのリソースハンドラーが必要です。
import * as pc from 'playcanvas';
import { Asset, AssetListLoader, Entity } from 'playcanvas';
window.pc = pc;
// ... アプリケーションを作成して開始する ...
const assets = {
plugin: new Asset('playcanvas-spine', 'script', { url: 'playcanvas-spine.4.3.min.js' }),
skeleton: new Asset('spineboy-pro.json', 'json', { url: 'spineboy-pro.json' }),
atlas: new Asset('spineboy-pma.atlas', 'text', { url: 'spineboy-pma.atlas' }),
// アトラスのページ名を付け、Spine 4.3ではsRGBを無効にする
texture: new Asset('spineboy-pma.png', 'texture', { url: 'spineboy-pma.png' }, { srgb: false })
};
await new Promise((resolve) => {
new AssetListLoader(Object.values(assets), app.assets).load(resolve);
});
const spineboy = new Entity('spineboy');
spineboy.addComponent('spine', {
atlasAsset: assets.atlas.id,
skeletonAsset: assets.skeleton.id,
textureAssets: [assets.texture.id]
});
app.root.addChild(spineboy);
spineboy.spine.state.setAnimation(0, 'run', true);
Spine 4.3プラグインでバイナリの.skelスケルトンを読み込むには、binaryアセットとして読み込みます。
const skeleton = new Asset('spineboy-pro.skel', 'binary', { url: 'spineboy-pro.skel' });
Spineの座標は0.01倍にスケーリングされるため、Spineエディターで高さ700ピクセルのスケルトンは、シーン内では高さ7ユニットになります。
Spineboyサンプルは、プラグインを使用したエンジンのみのプロジェクトの完全な例です。
アニメーションの制御
spineコンポーネントから、スケルトンのspine-coreオブジェクトにアクセスできます。
| プロパティ | 説明 |
|---|---|
entity.spine.state | AnimationState。トラック上でアニメーションを再生し、アニメーション間をミックスします。 |
entity.spine.skeleton | Skeleton。ボーン、スロット、スキン、色を持ちます。 |
entity.spine.spine | プラグインオブジェクト。priority、layers、timeScaleプロパティを持ちます。 |
例えば、アニメーション間のミックス、2つ目のトラックでのアニメーション再生、イベントの受信は次のように行います。
const state = entity.spine.state;
// アニメーションが切り替わるときに0.2秒かけてミックスする
state.data.defaultMix = 0.2;
// トラック0でrunアニメーションをループ再生し、トラック1でshootアニメーションを1回再生する
state.setAnimation(0, 'run', true);
state.setAnimation(1, 'shoot', false);
state.addEmptyAnimation(1, 0.2, 0);
state.addListener({
event: (entry, event) => {
console.log(`event ${event.data.name}`);
}
});
APIの詳細は、Spineランタイムガイドとspine-core APIリファレンスを参照してください。
色
Spine 4.3プラグインはspine-coreランタイムの色をそのまま描画するため、スケルトンの色付けにはこれらの色を使用します。
| 色 | 色付けの対象 |
|---|---|
skeleton.color | スケルトン全体。 |
slot.getPose().color | スロット。skeleton.findSlot(slotName)で取得します。スロットの色をキーにしたアニメーションによって上書きされます。 |
slot.getPose().darkColor | Tint black用の、スロットのダークカラー。 |
attachment.color | アタッチメント。skeleton.getAttachment(slotName, attachmentName)で取得します。 |
const skeleton = entity.spine.skeleton;
// スケルトン全体に色を付ける
skeleton.color.set(1, 0.5, 0.5, 1);
// headスロットに色を付ける
skeleton.findSlot('head').getPose().color.set(1, 0, 0, 1);
Spine 4.3プラグインは、スロットのブレンドモード (normal、additive、multiply、screen) とTint blackをサポートしています。
Physics
Spine 4.2で追加されたPhysics制約は、髪や布などのスケルトンの一部を自然に揺らし、落ち着かせます。4.2プラグインと4.3プラグインは、スケルトンのアニメーションに合わせてこれをシミュレーションします。
Spine 4.3プラグインでは、Physics制約がエンティティの移動と回転にも反応できるため、エンティティがシーン内を移動すると、Physicsで動く部分が遅れてついてきたり、揺れたり、落ち着いたりします。このPhysicsの継承はデフォルトで無効です。Editorでは、spineスクリプトのphysicsInheritance属性を有効にします。コードでは、プラグインのskeletonPhysicsオブジェクトを使用します。これはspine-coreランタイムのSkeletonPhysicsMovementです。
const { skeletonPhysics } = entity.spine.spine;
// エンティティの移動と回転をすべてPhysicsに渡す
skeletonPhysics.setPositionInheritance(1, 1);
skeletonPhysics.rotationInheritance = 1;
水平方向と垂直方向の移動、および回転には、それぞれ別の係数があります。0から1の間の値は移動の一部を渡し、0は何も渡しません。回転は、スケルトン平面の法線であるエンティティのローカルZ軸周りの回転です。
リスポーンなどでエンティティを瞬時に移動させる場合は、その移動がPhysicsに渡されないようにトランスフォームをリセットします。
entity.setPosition(10, 0, 0);
entity.spine.spine.skeletonPhysics.resetTransform();
Spine 4.3
Spine 4.3ではspine-core APIの一部が変更されました。例えばskeleton.setToSetupPose()はskeleton.setupPose()になったため、4.3に移行する際はグローバル変数spineを使用するスクリプトの更新が必要です。詳細はSpineランタイムの変更履歴を参照してください。
以前のプラグインのsetTintメソッドは4.3プラグインではサポートされておらず、呼び出すと警告がログに出力されます。代わりに、スケルトン、スロット、アタッチメントの色を使用してください。
4.3プラグインは、エンティティの移動をPhysicsに渡すこともできます。
レイヤーと描画順
スケルトンはデフォルトでUIレイヤーに描画されます。他のレイヤーに描画するには、レイヤーIDを設定します。
const worldLayer = app.scene.layers.getLayerByName('World');
entity.spine.spine.layers = [worldLayer.id];
プラグインオブジェクトのpriorityは、重なり合うスケルトンの描画順を設定します。priorityの値が小さいスケルトンが先に描画されます。