複数の Spine アニメーションを動的に合成する
「Happy Elements Advent Calendar 2023」 12月8日の記事です。
はじめに
『あんさんぶるスターズ!!Basic』開発エンジニアのY.K.です。
キャラクターの多く登場するゲームにおける Spine 及び Spine-Unity SDK 機能の実際の活用事例及び対応手順をご紹介したいと思います。
「Spine アニメーションを合成する」とは
キャラクターアニメーションが複数あり、それぞれのキャラクターに何かを持たせたいような場合、そのものを持ったキャラクターアニメーションをキャラクター数分制作するのは現実的ではない場合があります。
キャラクターアニメーションの中に重ね合わせることができれば、持たせたいものを制作するのみで同じアニメーションが表示可能となります。
ここでは、あるアニメーションに対して別のアニメーションを挟み込む(例えば、体と手の間など)ような場合を例として、独立した複数の Spine アニメーションを、処理によって動的に合成する方法をご紹介したいと思います。
キャラクター + 持ち物 として合成されたアニメーション
キャラクターの持ち物単体表示
Spine について
Spine は Unity 向けランタイムが提供されているアニメーションミドルウェアです。
対応手順
2つのアニメーションを重ねるということ自体については、Spine 公式の方法にて提供があり、ドキュメントにも紹介があります。
このドキュメントでは Unity Inspector にて事前に設定を行う方法として記載されていますが、ここでは動的にプログラムで合成する方法をご紹介します。
Spine アニメーション側の準備
キャラクターアニメーションに、表示要素を含まないセパレーター専用の Skin (以下、セパレーター Skin)を定義します。
複数個定義することができますので、必要に応じた個数分定義するということでよいかなと思います。
あくまで分割点ですので、必ずしも「このセパレーターは体と手の間のセパレーター」と用途を明示して定義する必要はありません。
*例えば、あるアニメーションでは、あるセパレーターより前に手があり、別のアニメーションでは同じセパレーターより後ろに手があるということはありえるためです。
セパレーター Skin は、その Skin 名で区別されますので、他の Skin とは異なる特別なプレフィクス(例えば "--Separator_" など)を定義しておきます。
プログラム処理
ポイントとしては以下の2点です。
- SkeletonAnimation の Renderer をセパレーター Skin を区切りとして複数の Renderer に分割する
- 重なり順は Renderer#sortingOrder(Inspector 上は Order in Layer)の値により決定される
分割された Renderer(キャラクターアニメーション側)
分割されていない、通常の SkeletonAnimation(挟み込まれるアニメーション側)
キャラクターアニメーションにセパレーター Skin を反映する
Spine SDK に含まれる SkeletonRenderSeparator#AddToSkeletonRenderer
を使用して SkeletonRenderSeparator
を Attach します。
sortingOrderIncrement
引数によって、分割された各 Renderer の SortingOrder が決まり(0, 10, 20など)、挟み込まれる側は、その表示位置に応じてこの間の値を指定することになります。
SkeletonRenderer#FindAndApplySeparatorSlots
を使用して、セパレーター Skin を反映します。
startsWith
の引数には、セパレーター Skin として定義したプレフィクス(上の例であれば "--Separator_")を指定します。
具体的には以下となります。
SkeletonRenderSeparator.AddToSkeletonRenderer(skeletonAnimation, extraPartsRenderers: "追加する Renderの数(つまり、セパレーター Skin 数と同じとなる)", sortingOrderIncrement: "SortingOrder の刻み値(例えば 10を指定すると、0, 10, 20...)");
skeletonAnimation.FindAndApplySeparatorSlots("Skin として定義したプレフィクス", updateStringArray: true);
また、セパレーター Skin 数は以下の処理により SkeletonAnimation
自体から取得可能です。
foreach (var slot in skeletonAnimation.skeleton.Slots)
{
var slotName = slot.Data.Name;
if (slotName.StartsWith("Skin として定義したプレフィクス"))
この時点で、キャラクターアニメーション側は以下のように複数の Renderer に分割され、それぞれに対して表示順序が定義された状態となります。
SkeletonAnimation GameObject Inspector
挟み込まれる側の SortingOrder を指定する
挟み込まれる側の SortingOrder を挟み込みたい位置の値に設定します。
SkeletonAnimation
から MeshRenderer
を取得
var meshRenderer = skeletonAnimation.GetComponent<MeshRenderer>();
meshRenderer.sortingOrder = sortingOrder;
合成する
以上で準備としては完了し、キャラクターアニメーションと挟み込まれる側アニメーションを同一のカメラで映すことで、指定した順序で挟み込まれて表示されます。
キャラクターに2つのセパレーター Skin を定義して、別の1つのアニメーションを合成した Unity ヒエラルキーの状態
この例では、キャラクターアニメーション GameObject の中に挟み込まれる側のアニメーション GameObject を含めていますが、表示においては必ずしもそのようでなくても問題ありません。
まとめ
一つの Spine アニメーションを複数の Render に分割し、その間に別の Spine アニメーションを表示するという方法をご紹介しました。
これは、あくまで、分割された Renderer のそれぞれの SortingOrder により重なり順が決定されるため、この方法を応用することにより、複数の Spine アニメーションのそれぞれを分割して、交互に重ね合わせるように合成することも可能となります。
Discussion