🐋

複数の Spine アニメーションを動的に合成する

2023/12/08に公開

「Happy Elements Advent Calendar 2023」 12月8日の記事です。

はじめに

『あんさんぶるスターズ!!Basic』開発エンジニアのY.K.です。
キャラクターの多く登場するゲームにおける Spine 及び Spine-Unity SDK 機能の実際の活用事例及び対応手順をご紹介したいと思います。

「Spine アニメーションを合成する」とは

キャラクターアニメーションが複数あり、それぞれのキャラクターに何かを持たせたいような場合、そのものを持ったキャラクターアニメーションをキャラクター数分制作するのは現実的ではない場合があります。
キャラクターアニメーションの中に重ね合わせることができれば、持たせたいものを制作するのみで同じアニメーションが表示可能となります。
ここでは、あるアニメーションに対して別のアニメーションを挟み込む(例えば、体と手の間など)ような場合を例として、独立した複数の Spine アニメーションを、処理によって動的に合成する方法をご紹介したいと思います。

ものを持ったキャラクター
キャラクター + 持ち物 として合成されたアニメーション

キャラクターの持ち物
キャラクターの持ち物単体表示

Spine について

Spine は Unity 向けランタイムが提供されているアニメーションミドルウェアです。
http://ja.esotericsoftware.com/spine-in-depth

対応手順

2つのアニメーションを重ねるということ自体については、Spine 公式の方法にて提供があり、ドキュメントにも紹介があります。
http://ja.esotericsoftware.com/spine-unity#SkeletonRenderSeparator

このドキュメントでは Unity Inspector にて事前に設定を行う方法として記載されていますが、ここでは動的にプログラムで合成する方法をご紹介します。

Spine アニメーション側の準備

キャラクターアニメーションに、表示要素を含まないセパレーター専用の Skin (以下、セパレーター Skin)を定義します。
複数個定義することができますので、必要に応じた個数分定義するということでよいかなと思います。
あくまで分割点ですので、必ずしも「このセパレーターは体と手の間のセパレーター」と用途を明示して定義する必要はありません。
*例えば、あるアニメーションでは、あるセパレーターより前に手があり、別のアニメーションでは同じセパレーターより後ろに手があるということはありえるためです。

セパレーター Skin は、その Skin 名で区別されますので、他の Skin とは異なる特別なプレフィクス(例えば "--Separator_" など)を定義しておきます。

プログラム処理

ポイントとしては以下の2点です。

  • SkeletonAnimation の Renderer をセパレーター Skin を区切りとして複数の Renderer に分割する
  • 重なり順は Renderer#sortingOrder(Inspector 上は Order in Layer)の値により決定される

分割された Renderer(キャラクターアニメーション側)
分割された Renderer(キャラクターアニメーション側)

分割されていない、通常の SkeletonAnimation(挟み込まれるアニメーション側)
分割されていない、通常の SkeletonAnimation(挟み込まれるアニメーション側)

キャラクターアニメーションにセパレーター Skin を反映する

Spine SDK に含まれる SkeletonRenderSeparator#AddToSkeletonRenderer を使用して SkeletonRenderSeparator を Attach します。
sortingOrderIncrement 引数によって、分割された各 Renderer の SortingOrder が決まり(0, 10, 20など)、挟み込まれる側は、その表示位置に応じてこの間の値を指定することになります。
https://github.com/EsotericSoftware/spine-runtimes/blob/4.1/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderSeparator/SkeletonRenderSeparator.cs#L100-L108

SkeletonRenderer#FindAndApplySeparatorSlots を使用して、セパレーター Skin を反映します。
startsWith の引数には、セパレーター Skin として定義したプレフィクス(上の例であれば "--Separator_")を指定します。
https://github.com/5argon/spine-unity-upm/blob/master/Runtime/spine-unity/Components/SkeletonRenderer.cs#L416-L424

具体的には以下となります。

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
SkeletonAnimation GameObject Inspector

挟み込まれる側の SortingOrder を指定する

挟み込まれる側の SortingOrder を挟み込みたい位置の値に設定します。

SkeletonAnimation から MeshRenderer を取得

var meshRenderer = skeletonAnimation.GetComponent<MeshRenderer>();
meshRenderer.sortingOrder = sortingOrder;

合成する

以上で準備としては完了し、キャラクターアニメーションと挟み込まれる側アニメーションを同一のカメラで映すことで、指定した順序で挟み込まれて表示されます。

Unity ヒエラルキー
キャラクターに2つのセパレーター Skin を定義して、別の1つのアニメーションを合成した Unity ヒエラルキーの状態

この例では、キャラクターアニメーション GameObject の中に挟み込まれる側のアニメーション GameObject を含めていますが、表示においては必ずしもそのようでなくても問題ありません。

まとめ

一つの Spine アニメーションを複数の Render に分割し、その間に別の Spine アニメーションを表示するという方法をご紹介しました。
これは、あくまで、分割された Renderer のそれぞれの SortingOrder により重なり順が決定されるため、この方法を応用することにより、複数の Spine アニメーションのそれぞれを分割して、交互に重ね合わせるように合成することも可能となります。

Happy Elements

Discussion