あんスタ！！Musicにおける1800の衣装と100のモーションを持つSpineデータ配信の仕組み

# はじめに
Happy Elements Advent Calendar 2020 15日目の記事です。

あんスタ！！Music のオフィスとお仕事機能に登場するアイドルの2Dの姿は [Spine](http://ja.esotericsoftware.com) で制作されています。

![subaru.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38700/4d3ea136-4c0d-ed42-4ecc-4895011a2575.png)

オフィスアイドルは衣装の着せ替えができ、2020年11月時点で1800を超えるアイドルと衣装の組み合わせがあります。それと同時にオフィスアイテムやお仕事など、合計100を超える多様なモーションが存在します。このような規模では、プロジェクトの構成によって新規データの制作効率が著しく低下してしまったり、少しデータを追加するだけでユーザーさんに大量のデータをダウンロードさせてしまう事態になりかねません。

この記事では、本アプリでどんどん増えていくオフィス衣装とモーションの Spine データをどのように制作し、アプリ向けにアセット配信しているのかを紹介します。あくまでも大量のデータに対応する仕組みの紹介のため、Spine の通常のワークフローからは外れてしまう、一部強引な手段を含む内容であることをご了承ください。

なお、本記事で前提とする Spine エディタのバージョンは 3.7 系とします。現在最新版の 3.8 では多数の機能追加が行われているようなので、本記事で紹介する工夫の一部は簡略化できるかも知れません。

# Spine の基本要素についておさらい
この記事で重要な要素だけ軽く説明します。詳しくは公式のユーザーガイド（ [Spineユーザーガイド](http://ja.esotericsoftware.com/spine-user-guide) ）をご参照ください。

## スケルトン（Skeleton）
スケルトンはボーン階層一式やアニメーションなどを持つ、Spine データの基本的な単位です。1つのプロジェクトファイルには複数のスケルトンを格納することができます。

## スロット（Slot）、アタッチメント（Attachment）、スキン（Skin）
画像パーツをスケルトンのボーン階層の中に配置すると、Tree ウィンドウに ボーン＞スロット＞アタッチメント＞画像 というような階層構造が見られます。スロットとアタッチメントがごっちゃになりがちですが、大まかに言うとスロットは描画順の制御と表示アタッチメントの切替え（表情など）を担当しています。アタッチメントに対応付ける画像はスキンによって変更することができます。

## Unity での扱い
本アプリはゲームエンジンの Unity を使っています。Spine の Unity 用ランタイムを使うことで、Spine データの Unity へのインポートと再生が可能となります。基本の手順は以下のようになります。

1. Spine エディタからスケルトン（JSON形式）とアトラスをエクスポートする。
2. スケルトンとアトラスを Unity プロジェクトにインポートすると、再生に必要な `SkeletonDataAsset` や他のアセットが生成される。
3. `SkeletonAnimation` などのコンポーネントを使い、指定した `SkeletonDataAsset` を再生する。

必要に応じてインポートされたアセットをアセットバンドルとしてビルドし配信することになります。

# プロジェクト構成とアセットバンドル化、ランタイム処理のフローの全体像
先にフローの全体像を示すと次の図のようになります。

![スクリーンショット 2020-10-27 17.04.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38700/a60b8811-4fd4-f6ae-da28-3ff914adab2c.png)

以降の節で各部分を説明していきます。

# キャラクターのスケルトン構成
本アプリでは、キャラクターのスケルトンデータは全アイドル・衣装で共通となっています。

アイドル・衣装ごとのパーツの切り替えには Spine のスキン機能を使っています。それではスケルトンの中に1800個のスキンが入っているのかというと、そのようなことはありません。共通衣装をはじめとした多くの衣装は複数アイドルに対して共通化できること、頭部は衣装によって変化することが少ないということから、アイドルによって決まる頭パーツと衣装によって決まる体パーツでスキンを分けています。Spine エディタ上では、Spine 3.7 から追加されたスキンの組み合わせ機能を使い、頭と体のスキンの組み合わせを表示することができます（参考:  [スキンの組み合わせ](http://ja.esotericsoftware.com/blog/Spine-3-7-released#スキンの組み合わせ) ）。実際には手の肌の色を顔と合わせたり、アイドルごとに異なる装飾があったりという事情から、1つの衣装スキンを使い回せるアイドルの範囲は限られています。しかし、この分け方によって全体のスキン数や総アセット容量を数分の1に削減できています。

![スクリーンショット 2020-10-27 16.09.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38700/d4be1541-637b-0030-ddd4-b3e3b25e7c0e.png)

# プロジェクト構成
次に、モーションの作成とプロジェクトファイルの構成について見ていきましょう。キャラクターに必要なモーションの種類は以下のように分けることができます。

* 汎用モーション（例：待機、歩く、喜び、挨拶など）
* オフィスアイテムモーション
* お仕事モーション

また、キャラクター以外に必要なスケルトンとして以下のものが挙げられます。

* オフィスアイテム
* お仕事の小道具

これらを踏まえて、Spine 制作は以下のように分けられたプロジェクトファイル上で行われます。このようにプロジェクトを分ける主な理由は、衣装やオフィスアイテムの追加作業を複数人で並行して行えるようにするためです。

* キャラクターのメインプロジェクト
	* キャラクターのスケルトンを含み、新規の衣装スキンはここに追加されます。
	* 汎用モーションが実装されています。（Spine の用語としてはアニメーションですが、アプリでの呼び方に合わせて以下モーションと呼ぶことがあります。）
* オフィスアイテムごとのプロジェクト
	* キャラクターのスケルトンとオフィスアイテムのスケルトンを含みます。
	* キャラクターのスケルトンには必ずしも全ての衣装が含まれているわけではありません。
	* それぞれのスケルトンに対してオフィスアイテムモーションが実装されています。
	* 同じ動作をする別デザインのオフィスアイテムは、同じスケルトンの別スキンとして作られています。
* お仕事の種類ごとのプロジェクト
	* キャラクターのスケルトンと、必要な場合は小道具のスケルトンを含みます。
	* オフィスアイテムと同様、必ずしもキャラクターの全衣装が含まれているわけではありません。
	* お仕事モーションが実装されています。

汎用以外のモーションの追加先がメインプロジェクトとは異なるため、どのように必要なアニメーションを統合しているか疑問に思われる方もいるでしょう。この問題について次節で説明します。

# アニメーションのアセットバンドル化・ランタイム統合
オフィスアイテムモーションやお仕事モーションは、最終的に任意の衣装を着たアイドルのスケルトンで動作させる必要がありますが、あらかじめキャラクターのメインプロジェクトと統合しているわけではありません。アニメーション単体でアセットとして扱っています。オフィスアイテムモーションの追加・修正時にキャラクターのメインプロジェクトを更新する必要はありませんし、オフィスアイテムの追加に伴ってキャラクターのスケルトンが肥大化していくこともありません。

全体のフローからアニメーションに関する部分を抜き出すと次のようになります。黒矢印はデータの流れを、青矢印はアセットの参照関係を表しています。右に取っ手の付いたノードはアセットバンドル化対象を表しています。

![スクリーンショット 2020-10-27 14.24.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38700/3be9807c-23d9-3a47-8b7d-ceaa8d09c5cc.png)


一般的な Spine のワークフローから外れることで代わりに必要となる処理は、「スケルトンのアニメーション部分の抽出」と「ランタイムでのスケルトンへのアニメーション流し込み」の2つです。アニメーションの抽出については、スケルトンをJSON形式でエクスポートしておけば簡単にアニメーション部分を抽出することができます。ランタイムのアニメーション流し込みについては、パフォーマンスの違いはありますがいくつかの方法が考えられます。

* メインのスケルトンと抽出したアニメーションをJSONとして普通に合成し、スケルトンデータとしてロードする。
* アニメーションデータのロード処理で内部的に使われている `SkeletonJson.ReadAnimation()` メソッドを何らかの方法で呼び出し、ロード済みのスケルトン（ `SkeletonData` ）にアニメーションを付け加える。

# スキンのアセットバンドル化・ランタイム統合
キャラクタースケルトンのスキン（頭パーツ・体パーツ）も、それぞれスケルトンとは別にアセットバンドル化して配信しています。1人のアイドルを表示するために数十の頭パーツと数百の体パーツを全てロードするわけにはいかないので、これはまあ当然の話です。オフィスアイテムのスキンについても同様にスキンごとにアセットバンドル化しています。

本アプリではスキンのアセットバンドル化の方法をもう少し工夫し、スキンの追加時にスケルトンの再ビルドが不要なようにしました。普通の方法ではスケルトンの再ビルドが必要な理由を見るためにスキンに関連する情報を整理しましょう。Spine エディタからスケルトンとアトラスをエクスポートしたとき、それぞれに含まれるスキン由来の情報は以下のとおりです。

* `.png` : そのスキンの画像がパックされている
* `.atlas.txt` : 画像ファイル中にパーツがどのように配置されているかを示すデータ（テキストファイル）
* `.json` : スケルトンデータ
	* スキンのリスト、各スキンに対するアタッチメントの情報（画像名・アタッチメントの位置）

つまり、スキン固有のファイルである `.png` と `.atlas.txt` は、それだけではスキンとして動作させるための十分な情報を持っていないのです。

![スクリーンショット 2020-10-27 14.10.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38700/da6169ae-c144-56e9-84a6-d3428c246141.png)


本アプリではスケルトンの JSON ファイルから該当スキンのアタッチメント情報を抽出し、 `.png` と `.atlas.txt` と一緒にアセットバンドル化するようにしました。アニメーションを抽出・流し込みしたのと同じ構図なので、こちらもやはりランタイムでのスキンの流し込み処理が必要になります。実装方法もちょうど同じように、JSON として普通に合成する方法、 `SkeletonJson.ReadAttachment()` や `Skin.AddAttachment()` といったメソッドを呼び出すなどの方法があります。

# 制約と課題
## ボーン階層の固定化
最初に作成したメインのキャラクタースケルトンを各オフィスアイテムやお仕事のプロジェクトにクローンしていく都合上、どうしてもアニメーション制作がメインスケルトンのボーン階層一式の仕様に縛られることになります。これは1つのプロジェクトファイルにあらゆるアニメーションデータを集約した場合でも、アニメーションそのものの制約としては同じ状況になるので、当然といえば当然です。

複数のプロジェクトファイルに分かれていることにより、ボーン構造を少し変更した場合にすべてのプロジェクトで正常に再生できるかどうかを確認するのがかなり大変という問題がありそうです。

## 任意のアイドルや衣装に対するアニメーションのチェック
1800の衣装と100のモーションに対応するための本記事の工夫はプロジェクトファイル管理とアセット配信を改善しますが、そもそもの組み合わせの多さを減らすものではありません。スキンとアニメーションの18万通りの組み合わせで正常に再生できるようにするためには、スキン作成とアニメーションの自由度に関してレギュレーションを適切に定めることは必要不可欠です。

アニメーションのチェックについて本記事の方法で主にネックになるのは、後から追加した衣装スキンが既存のオフィスアイテムやお仕事で正しく動作するかどうか、それをどう確認するかという点でしょう。どのような揺れものを許容するかといったスキン作成のレギュレーションやアニメーターの技量次第ではありますが、エンジニアとしては少なくとも確認が簡単にできるように改善していきたいと思っています。

# 参考資料
Spine 公式の資料は以下のとおりです。

* [Spineユーザーガイド](http://ja.esotericsoftware.com/spine-user-guide)
* [Spine Runtimesガイド](http://ja.esotericsoftware.com/spine-runtimes-guide)
* [Spine-Unity Runtime Documentation](http://ja.esotericsoftware.com/spine-unity)

# メンバー募集
Happy Elements株式会社 カカリアスタジオでは、
いっしょに【熱狂的に愛されるコンテンツ】をつくっていただけるメンバーを大募集中です！
もし弊社にご興味持っていただけましたら、是非一度
下記採用サイトをご覧ください。
[Happy Elements株式会社 採用特設サイト](https://recruit.happyelements.co.jp/)