本記事は、サムザップ Advent Calendar 2021の12/19の記事です。
はじめに
株式会社サムザップでUnityエンジニアをしている高橋です。
現在はサムザップ所属ですが、前部署であるピグ事業部所属時に作成した「Spine変換ツール(ピグアニメーション→Spineデータ)」の紹介をしたいと思います。
ピグパーティとは
ピグパーティは、スマホ版アメーバピグとして、2015年にios/androidでリリースされたアバター着せ替えゲームです。
(PC版アメーバピグは、2009年リリース。2019年にクローズ。)
経緯 【レガシー技術との向き合い × 資産活用 × 採用】
ピグパーティ自体はCocos2D-xで実装されていますが、アイテム作成・アニメーションアセット制作などの各種ツール郡はAdobe Flash、Adobe Airを使用していました。
PC版アメーバピグから移植されたアイテムやツールも多いため、Flashとは切っても切れない縁となっておりました。
(まだまだ移植されていないアイテム・モーションも大量にあります。)
Flash自体はAnimateとして残っていますが、 Airの開発は終了し(のちにオープンソース化)、ツールのアップデートも難しい状況になってきました。
また、Flash自体の衰退によりFlashを扱えるエンジニア・アニメーターも減り続け、人材確保も難しい状況になってきました。
そこで、
- アニメーターが集めやすい
- アプリ・ランタイム開発も活発
- ピグアニメーションとの親和性も高く資産活用もできそう
という点からSpineを採用する流れとなりました。
Spineの変換ツールについて
Spineを使うという方向性はなんとなく決まったのですが、既存の大量の資産(数百のモーションデータ)をSpineに置き換えていく必要がありました。
手作業でできる量ではなかったので変換ツールを作ることにしました。
構成
変換ツールの構成は主に3つの工程をに分けられます。
1. モーションデータ(後述)からspineにインポート可能なデータに変換する(Node js)
2. インポートしてspineファイルを生成する(Spineのコマンドを叩くシェル)
3. spineファイルからエクスポートデータを生成する(Spineのコマンドを叩くシェル)
1. モーションデータからspineにインポート可能なデータに変換する(Node js)
ピグの既存モーションデータについて
まずは、変換前データの説明です。
ピグのモーションはAdobeFlashで作られています。
それを専用ツールで書き出して、アプリに読みこんで再生させています。
モーションデータは、
- アニメーション情報(フレームごとの変換行列の配列データ)
- 画像(アトラスデータ)
から構成されています。
Spineのインポートデータのフォーマットについて
次に、変換後のデータフォーマットについて把握する必要がありますが、Spineインポートデータフォーマットについて公式ではドキュメントが用意されてません。(2021年自分が知る限り)
なので、spineに適当にオブジェクトを配置してはエクスポートし、.jsonを確認する。を繰り返して、データフォーマットを理解して行く必要があります。
x: 0 からx:100に移動するアニメーションするデータ
{
"skeleton": {
"hash": "np7neao+yJ7+7d45HmWqrklJcPw",
"spine": "3.8.99",
"x": -75,
"y": -75,
"width": 150,
"height": 150
},
//ボーン設定
"bones": [
{
"name": "root"
},
{
"name": "TestBone",
"parent": "root"
}
],
//スロット設定
"slots": [
{
"name": "TestSlot",
"bone": "TestBone",
"attachment": "image/test"
}
],
//スキンの設定
"skins": [
{
"name": "default",
"attachments": {
"TestSlot": {
"image/test": {
"width": 150,
"height": 150
}
}
}
}
],
//アニメーション設定
"animations": {
"animation": { //アニメーション名がkeyになる。
"bones": {
"TestBone": {
"translate": [
//0個目のキーフレーム設定
{},
//1個目のキーフレーム設定
{
"time": 0.5,
"x": 100
}
]
}
}
}
}
}
Spineのインポートデータ(json)への変換
フォーマットがわかったら、後はデータを流し込んでいけば良いのですが、いくつか問題がありました。
変換行列の分解
ピグのモーションデータは変換行列(Skew(傾斜)可能)で記述されていました。
しかし、spineデータは、
- 座標(x,y)
- スケール
- Shear(剪断)
を、それぞれ記述する必要があります。
そこで、変換行列を分解する処理を書きました。
//行列を分解する
static decomposeMatrix(matrix){
//scaleを求める。
const scale ={
x: this.square(matrix.sx, matrix.r0),
y: this.square(matrix.sy, matrix.r1),
}
//剪断を求める
const shear = {
x: -this.toDeg(Math.atan2(matrix.sx / scale.x, matrix.r0 / scale.x)) + 90,
y: this.toDeg(Math.atan2(matrix.sy / scale.y, matrix.r1 / scale.y)) - 90,
};
return {
scale: scale,
shear: shear,
position: {x: matrix.tx, y: matrix.ty}
}
}
static square(x, y){
return Math.sqrt((x*x) + (y*y));
}
static toDeg(rad){
return rad * (180.0/Math.PI);
}
シンボル問題
Flashには共通オブジェクトをシンボルとして登録し使いまわせるという機能(UnityでいうところのPrefabみたいなもの)がありましたが、それはSpineには存在しません。
なので、変換ツール側で分解してアニメーションを再現する必要がありました。
入れ子・拡大縮小・カラー変更・ループなどなどを考慮しないといけないことが多いためなかなか骨の折れる作業でした。
変換前Flash
変換後Spine
カラー変換問題
Flashの方がさまざまなカラー変換に対応しているため、Spineで再現できないという問題が起きました。どうすることもできなかったのエラー出力にとどめました。
繊細なインポートデータ
インポートデータが繊細で少しも誤りも許してくれません。
エラーログを出力してくれてますが、大抵の場合エラー特定に繋がる情報は見つけられませんでした。
2. インポートしてspineファイルを生成する
ここまできたら後は簡単です。
Spineの用意してくれているコマンドラインを叩くだけだからです。
以下の例は、複数のskeleton.jsonを一つのspineファイルにする例です。
Spine --input {skeleton1.json} --output {project.spine} --import --input {skeleton2.json} --output {project.spine} --import
3. spineファイルからエクスポートデータを生成する
こちらもSpineのコマンドラインを叩くだけでした。
Spine --input {input_spine} --output {output_dir} --export {export_setting}
※{export_setting}ファイルはこちらから生成しておくことができます。
最後に
Flashから脱却を図るピグパーティの事例を紹介させていただきました。
長期プロジェクトでの大量の資産を眠らせておくのはもったいない!!。再活用の一案となれれば幸いです。
ピグ事業部では、新規・既存プロジェクトを一緒に開発してくれるUnityエンジニアを大募集中です!!