ランタイムスケルトン

Spineランタイムはボーン、スロット、アタッチメント、その他諸々のスケルトン要素へのアクセスができます。また、スケルトンをカスタマイズし、様々な方法で環境に反応させることができます。

ワールドトランスフォーム

ボーンは階層的に配置されており、各ボーンは親ボーンの影響を受け、これがrootボーンまで続きます。例えば、ボーンが回転すると、その子ボーン以下の全ての子ボーンが回転します。これを実現するために、それぞれのボーンに以下で構成されるローカルトランスフォームを持たせています:

  • x および y
  • rotation
  • scaleX および scaleY
  • shearX および shearY

rootから始まり、親ボーンから順に、ローカルトランスフォームが各ボーンのワールドトランスフォームの計算に使用されます。ワールドトランスフォームは以下で構成されます:

  • abcd これは2x2の行列で、ボーンと、rootに戻るまでのすべての親ボーンの回転、スケール、シアーの組み合わせをエンコードします。acはX軸、bdはY軸です。
  • worldXworldY これはボーンのワールドポジションで、ワールド座標系はrootボーンが配置される座標系です。

ワールドトランスフォームはボーンのローカル座標からの任意の点をワールド座標に変換できます。例えば、ボーンにアタッチされたメッシュの頂点を、ボーンのワールドトランスフォームに変換することができます。その結果、頂点はボーンと全ての親ボーンに影響されます。この仕組みがSpineのスケルトンアニメーションの中核になっています。

ワールドトランスフォームはその反対も実行できます。ワールド座標の任意の点をボーンのローカル座標に変換することができます。

updateWorldTransform

ボーンのワールドトランスフォームは通常、直接変更されません。その代わり、ローカルトランスフォームが変更され、ローカルトランスフォームと親ボーンのワールドトランスフォームを使用してワールドトランスフォームが計算されます。ボーンのローカルトランスフォームが変更されると、BoneのupdateWorldTransformを呼び出してそのボーンと全ての子孫のワールドトランスフォームを再計算する必要があります。しかしながら、ボーンを正しい順番でアップデートする必要があるため、全てのボーンを正しい順番でアップデートするだけではなく、コンストレイントも適用するSkeletonのupdateWorldTransformを呼び出す方が一般的です。

アニメーションを適用すると、ほとんどの場合、ボーンのローカルトランスフォームが変更されます。スケルトンのレンダリングはボーンのワールドトランスフォームを使用します。このため一般的には、アニメーションの適用後およびレンダリングの前にupdateWorldTransformを呼び出します。

state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
renderSkeleton(skeleton);

プロシージャルアニメーション

様々な効果のために、プログラムでボーンにアクセスして調整することができます。例えば、ボーンのローカル回転をマウスカーソルを標的にするように設定できます。またプログラムでIKターゲットボーンを配置し、IKコンストレイントで様々なボーンを異なるIKミックス値で調整することもできます。

一般的にはまずアニメーションを適用し、ボーンを調整します:

Bone torso = skeleton.findBone("torso");
...
state.update(delta);
state.apply(skeleton);
torso.rotation = ... // compute rotation for torso
skeleton.updateWorldTransform();
renderSkeleton(skeleton);
}

アニメーションポーズからのワールドトランスフォームがボーンの調整に必要な場合、調整前にupdateWorldTransformを呼び出し、ローカルトランスフォームが変更された後、再び呼び出します:

Bone torso = skeleton.findBone("torso");
...
state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
torso.rotation = ... // compute rotation for torso
skeleton.updateWorldTransform();
renderSkeleton(skeleton);

セットアップポーズ

アニメーションはセットアップポーズに対して適用されます。これはBoneDataが調整される場合、そのBoneDataを使用する全てのアニメーションと全てのスケルトンが影響されることを意味します。アニメーションの適用はBoneDataを使用するため、アニメーションの適用前に変更を行う必要があります。

Bone torso = skeleton.findBone("torso");
...
torso.data.rotation = ... // compute rotation for torso
state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
renderSkeleton(skeleton);

ボーンのポジション

ボーンのワールドトランスフォームはパーティクルやその他のエフェクトなどのゲーム要素の位置決めに使用できます:

Bone rightHand = skeleton.findBone("right hand");
...
state.update(delta);
state.apply(skeleton);
skeleton.updateWorldTransform();
renderSkeleton(skeleton);
renderParticles(rightHand.worldX, rightHand.worldY);

この例では、まずアニメーションが適用され、トランスフォームが計算され、そしてスケルトンがレンダリングされます。その後rightHandのボーンのワールドポジションがパーティクルエフェクトを描画するために使用されています。また、ボーンのワールド回転とスケールもパーティクルをボーンの方向に流すなどに利用できます。同様に、ボーンのワールドトランスフォームはボーンを使うUI要素の位置決め、回転、スケーリングなどUIのアニメーションにも使用できます。

汎用的なレンダリング

特定のゲームツールキット用に用意されているランタイムはフルソリューションになっており、レンダリングを含む全てを実行します。汎用ランタイムはゲームツールキットに依存することなく、実際のレンダリング以外を全て実行します。汎用ランタイムを使用する場合は、レンダリングについてのみ考慮する必要があります。

汎用ランタイムでレンダリングを行うには、Skeletonクラスが描画順のスロットリストであるdrawOrderプロパティを提供する必要があります。レンダリングは各スロットからのアタッチメントの取得、そのタイプの検査を行い、必要な場合はレンダリングします。以下はレンダリングが必要なアタッチメントタイプです:

  • RegionAttachment これは4頂点を持つ四角形です(常に長方形とは限りません)。
  • MeshAttachment これは任意の数の頂点と三角形を持ちます。どちらもスケルトンデータで提供されているので、レンダラーが三角測量を行う必要はありません。

以下はレンダリングの疑似コードです:

foreach (Slot slot in skeleton.drawOrder) {
   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のsetAttachmentを呼び出すことで変更できます。Skeletonは、スロットとアタッチメントオブジェクトを名前で再発見する便利なメソッド、setAttachmentを持っています。スロットのアタッチメントは再変更されるまで保持されます。

Skeleton skeleton = ...

// Find the slot by name.
Slot slot = skeleton.findSlot("slotName");
// Get the attachment by name from the skeleton's skin or default skin.
Attachment attachment = skeleton.getAttachment(slot.index, "attachmentName");
// Sets the slot's attachment.
slot.attachment = attachment;

// Alternatively, the skeleton setAttachment method does the above. 
skeleton.setAttachment("slotName", "attachmentName");

アタッチメントは他の方法でも変更できます。SkeletonのsetToSetupPoseまたはsetSlotsToSetupPoseを呼び出してスロットアタッチメントを変更できます。アニメーションの中には、アタッチメントを変更するキーフレームを持っていることがあります。SkeletonのsetSkinを呼び出してアタッチメントを変更することも可能です(スキンの変更を参照)。

次: ランタイムスキン 前: アニメーションの適用