ランタイムスケルトン
Spineランタイムはボーン、スロット、アタッチメント、その他諸々のスケルトン要素へのアクセスができます。また、スケルトンをカスタマイズし、様々な方法で環境に反応させることができます。
ワールドトランスフォーム
ボーンは階層的に配置されており、各ボーンは親ボーンの影響を受け、これがrootボーンまで続きます。例えば、ボーンが回転すると、その子ボーン以下の全ての子ボーンが回転します。これを実現するために、それぞれのボーンに以下で構成されるローカルトランスフォームを持たせています:
- xおよび- y
- rotation
- scaleXおよび- scaleY
- shearXおよび- shearY
rootから始まり、親ボーンから順に、ローカルトランスフォームが各ボーンのワールドトランスフォームの計算に使用されます。ワールドトランスフォームは以下で構成されます:
- a、- b、- c、- dこれは2x2の行列で、ボーンと、rootに戻るまでのすべての親ボーンの回転、スケール、シアーの組み合わせをエンコードします。- aと- cはX軸、- bと- dはY軸です。
- worldXと- worldYこれはボーンのワールドポジションで、ワールド座標系はrootボーンが配置される座標系です。
ワールドトランスフォームはボーンのローカル座標からの任意の点をワールド座標に変換できます。例えば、ボーンにアタッチされたメッシュの頂点を、ボーンのワールドトランスフォームに変換することができます。その結果、頂点はボーンと全ての親ボーンに影響されます。この仕組みがSpineのスケルトンアニメーションの中核になっています。
ワールドトランスフォームはその反対も実行できます。ワールド座標の任意の点をボーンのローカル座標に変換することができます。
updateWorldTransform
ボーンのワールドトランスフォームは通常、直接変更されません。その代わり、ローカルトランスフォームが変更され、ローカルトランスフォームと親ボーンのワールドトランスフォームを使用してワールドトランスフォームが計算されます。ボーンのローカルトランスフォームが変更されると、Bone updateWorldTransformを呼び出してそのボーンと全ての子孫のワールドトランスフォームを再計算する必要があります。しかしながら、ボーンを正しい順番でアップデートする必要があるため、全てのボーンを正しい順番でアップデートするだけではなく、コンストレイントも適用するSkeleton updateWorldTransformを呼び出す方が一般的です。
アニメーションを適用すると、ほとんどの場合、ボーンのローカルトランスフォームが変更されます。スケルトンのレンダリングはボーンのワールドトランスフォームを使用します。このため一般的には、アニメーションの適用後およびレンダリングの前にupdateWorldTransformを呼び出します。
state.apply(skeleton);
skeleton.updateWorldTransform();
renderSkeleton(skeleton);
プロシージャルアニメーション
様々な効果のために、プログラムでボーンにアクセスして調整することができます。例えば、ボーンのローカル回転をマウスカーソルをターゲットにするように設定できます。またプログラムでIKターゲットボーンを配置し、IKコンストレイントで様々なボーンを異なるIKミックス値で調整することもできます。
一般的にはまずアニメーションを適用し、ボーンを調整します:
...
state.update(delta);
state.apply(skeleton);
torso.rotation = ... // compute rotation for torso
skeleton.updateWorldTransform();
renderSkeleton(skeleton);
}
アニメーションポーズからのワールドトランスフォームがボーンの調整に必要な場合、調整前にupdateWorldTransformを呼び出し、ローカルトランスフォームが変更された後、再び呼び出します:
...
state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
torso.rotation = ... // compute rotation for torso
skeleton.updateWorldTransform();
renderSkeleton(skeleton);
セットアップポーズ
アニメーションはセットアップポーズに対して適用されます。これはBoneDataが調整される場合、そのBoneDataを使用する全てのアニメーションと全てのスケルトンが影響されることを意味します。アニメーションの適用はBoneDataを使用するため、アニメーションの適用前に変更を行う必要があります。
...
torso.data.rotation = ... // compute rotation for torso
state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
renderSkeleton(skeleton);
ボーンのポジション
ボーンのワールドトランスフォームはパーティクルやその他のエフェクトなどのゲーム要素の位置決めに使用できます:
...
state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
renderSkeleton(skeleton);
renderParticles(rightHand.worldX, rightHand.worldY);
この例では、まずアニメーションが適用され、トランスフォームが計算され、そしてスケルトンがレンダリングされます。その後rightHandのボーンのワールドポジションがパーティクルエフェクトを描画するために使用されています。また、ボーンのワールド回転とスケールもパーティクルをボーンの方向に流すなどに利用できます。同様に、ボーンのワールドトランスフォームはボーンを使うUI要素の位置決め、回転、スケーリングなどUIのアニメーションにも使用できます。
汎用的なレンダリング
特定のゲームツールキット用に用意されているランタイムはフルソリューションになっており、レンダリングを含む全てを実行します。汎用ランタイムはゲームツールキットに依存することなく、実際のレンダリング以外を全て実行します。汎用ランタイムを使用する場合は、レンダリングについてのみ考慮する必要があります。
汎用ランタイムでレンダリングを行うには、Skeletonクラスが描画順のスロットリストであるdrawOrderプロパティを提供する必要があります。レンダリングは各スロットからのアタッチメントの取得、そのタイプの検査を行い、必要な場合はレンダリングします。以下はレンダリングが必要なアタッチメントタイプです:
- RegionAttachmentこれは4頂点を持つ四角形です(常に長方形とは限りません)。
- MeshAttachmentこれは任意の数の頂点と三角形を持ちます。どちらもスケルトンデータで提供されているので、レンダラーが三角測量を行う必要はありません。
以下はレンダリングの疑似コードです:
Attachment attachment = slot.attachment;
AtlasRegion region;
if (attachment is RegionAttachment) {
attachment.computeWorldVertices(slot.bone, vertices);
triangles = quadTriangles;
region = attachment.rendererObject;
} else if (attachment is MeshAttachment) {
attachment.computeWorldVertices(slot.bone, vertices);
triangles = attachment.triangles;
region = attachment.rendererObject;
}
if (texture != null) {
Texture texture = region.page.rendererObject;
draw(texture, vertices, triangles, slot.data.blendMode);
}
}
アタッチメントのrendererObjectプロパティは、スケルトンデータがロードされた時にAttachmentLoader により設定されます。この疑似コードはSpineアトラスが使用されることを想定しているため、rendererObjectはAtlasRegionになっています。
AtlasRegionは、アトラスがロードされる時TextureLoaderによって設定されるrendererObjectプロパティを持つAtlasPageを持っています。Textureクラスはゲームツールキット固有のクラスを表しています。
アタッチメントの変更
スロットは常に単一アタッチメントを持つか、または全くアタッチメントを持たないかのどちらかになります。各スロットのアタッチメントはSlotのattachmentを設定することで変更できます。また、スロットとアタッチメントオブジェクトを名前で検索できる便利なメソッド、Skeleton setAttachmentもあります。スロットのアタッチメントは再変更されるまで保持されます。
// 名前でスロットを検索。
Slot slot = skeleton.findSlot("slotName");
// スケルトンのスキンまたはデフォルトスキンから名前でアタッチメントを取得。
Attachment attachment = skeleton.getAttachment(slot.index, "attachmentName");
// スロットのアタッチメントをセット。
slot.attachment = attachment;
// または、skeleton setAttachmentメソッドで上記を行えます。
skeleton.setAttachment("slotName", "attachmentName");
アタッチメントは他の方法でも変更できます。Skeleton setToSetupPoseまたはsetSlotsToSetupPoseを呼び出してスロットアタッチメントを変更できます。アニメーションの中には、アタッチメントを変更するキーフレームを持っていることがあります。Skeleton setSkinを呼び出してアタッチメントを変更することも可能です(スキンの変更を参照)。