Anima2Dのセットアップを自動化しよう!編


f:id:sgtech:20180520233811p:plain

こんにちは。
セガゲームス 開発統括部 アート&デザイン部 TAセクション 廣田です。

おかげさまで、コトダマンはサービス開始1ヶ月で500万ダウンロードを越え、たくさんの方に楽しんでいただいております!
今回も引き続き、コトダマンを題材にAnima2Dの紹介をしていきたいと思います。

f:id:sgtech:20180520234033g:plain:w300

前回はAnima2Dでセットアップを行い、アニメーションを作るところまで紹介しました。
セットアップがとても大変だったと思いますが、他の2Dアニメーションツールで用意されている、
・Photoshpからのエクスポート
・Unityへのインポート

がありませんでした。
今回はこの面倒な部分の自動化と描画最適化を行っていきましょう。

■Photoshopからのエクスポート

まずはPhotoshopからパーツ画像と位置情報を出力するスクリプトを作成していきます。

Photoshopデータの作り方

スクリプトで処理するために、データに決まりを作っておきます。
全てのデータ構成に対応しようとすると処理が複雑になりますので、ある程度決めごとを作ります。
今回は下記のようなデータを想定して進めていきます。

  • 1パーツ=1レイヤー
  • グループでまとめられているものも1パーツとして扱う
  • レイヤー第一階層にあるものを出力対象にする
  • レイヤー名を画像ファイル名,Anima2Dのスプライト名として使う

f:id:sgtech:20180520233817j:plain

必要な処理を考えよう

いきなりスクリプトを書き始めずに、まずは必要な処理をリストアップしてから取り掛かります。

  • 各レイヤーを画像として書き出し。
  • Anima2Dでパーツを並べる時に必要な情報を出力。
    • 各パーツの画像位置, サイズ
    • レイヤー名(=画像名)
    • レイヤー順

便利な処理も追加しよう

  • 画像の縮小処理
    • ゲームで使うには、元のPSDサイズでは大きすぎると思います。
    • 大きさはゲーム中でキャラが表示される最大サイズに合わせて決めましょう。
  • レイヤー名のチェック
    • 全角文字のチェック(ゲームデータでは全角は使わないというお約束として)
    • スペースのチェック

仕様

上記をまとめて、このような仕様でスクリプトに実装してきます。

  • 事前にPSDのエラーチェックを行う
  • 画像を適正サイズに縮小
  • 1レイヤー=1パーツとしてpngファイルを出力
  • PSDファイルと同名のフォルダを作成してpngファイルを出力
  • PSDファイルと同名の位置情報ファイルを出力
  • 位置情報ファイルの出力形式:json形式

パーツの画像位置を取得する

エクスポートのキモの部分、パーツ位置を取得する部分について詳しく説明します。
PhotoshopとUnityで原点(0,0)の基準位置が違います。
Photoshop:左上
Unity:中央

f:id:sgtech:20180520233804j:plain

エクスポートする時にUnityの座標でエクスポートするようにしましょう。

f:id:sgtech:20180520233924j:plain

画像の中心を求める

  • Photoshop画像サイズの縦横半分の位置を中心にします。(例:512x512の半分の256,256)

各パーツの位置を求める

  • レイヤーの選択範囲を作成し、selection.boundsで選択範囲のバウンディング座標を取得できます。この座標を利用してパーツ位置を割り出します。こちらもバウンディングサイズの半分をパーツ位置とします。(例:329, 296)
  • そして、先ほど求めた画像の中心を引くことによって、Unity座標(=中心からどのくらい離れた位置にいるか)を求めることができます。(例:+73, -40)

f:id:sgtech:20180520233956j:plain

JSONファイルに情報を出力

求めたパーツ位置をUnityから読めるように、ファイルに記録しましょう。
Order in Layerを設定するためのレイヤーの順序も記録します。
ファイル形式はJSONを使用し、レイヤー数分、下記のようなフォーマットで情報を書き込んでいきます。

{
	"[レイヤー名1]":{"index":[レイヤー順序], "x":[パーツx座標], "y":[パーツy座標]},
	"[レイヤー名2]":{"index":[レイヤー順序], "x":[パーツx座標], "y":[パーツy座標]},
	"[レイヤー名3]":{"index":[レイヤー順序], "x":[パーツx座標], "y":[パーツy座標]},
	"[レイヤー名4]":{"index":[レイヤー順序], "x":[パーツx座標], "y":[パーツy座標]},
                  ・
                  ・
}

スクリプトの流れ

1. エラーチェック
 そもそもデータが仕様にあっていなければエクスポートする状況ではないので、チェック処理は最初に行います。

2. PSDを新ドキュメントに複製
 PSDに対してスクリプトで色々操作しますので、元のPSDを破壊しないように複製してから操作します。

3. 画像を縮小
 出力サイズに縮小します。

4. レイヤー位置を求める
 Photoshopに画像位置を問い合わせる

5. 画像をバウンディングサイズで切り抜く
 パーツレイヤーをそのまま出力すると余白が多いですので、パーツ画像のサイズに切り抜きます

6. JSONファイルに情報を出力

完成スクリプト

以上をまとめたのが、こちらのスクリプトになります。


ExportAnima2D.jsx
f:id:sgtech:20180520234002j:plain
▶クリックで展開



スクリプトの保存先(Windows PhotoshopCC2018の場合)

C:\Program Files\Adobe\Adobe Photoshop CC 2018\Presets\Scripts
(Photoshopを起動したままスクリプトを入れた方は、Photoshopを再起動するとメニューに出てきます)
f:id:sgtech:20180520233807j:plain

実行してみる

出力したいPSDファイルを開いて、実行します。
(スクリプト中でUndoを実行しているところがあるので、もし途中で止まってしまうようでしたらUndo回数を上げてください)
PSDと同名の下記ファイルが無事に出力されましたでしょうか?
f:id:sgtech:20180520234005j:plain

Textureアトラスの作成

フォルダ内にパーツ画像のpngファイルが出力されていますので、TexturePackerなどを使って1枚のテクスチャーにまとめます。
f:id:sgtech:20180520234049j:plain

f:id:sgtech:20180520234238g:plain:w300


■Unityへの取り込み

Photoshop情報を元にAnima2Dスプライトを並べていきます。

事前準備(MiniJSON)

PhotoshopからJSON形式で画像位置情報を出力しましたので、UnityでもJSONを扱えるようにしましょう。
今回はMiniJSONを使用します。

MiniJSONの入れ方

  • https://gist.github.com/darktable/1411710にアクセス
  • ページ右上のDownload ZIPを押してダウンロード&解凍
  • 出てきたMiniJSON.csをプロジェクト内のPluginsフォルダに入れる(無ければ作成)

必要な処理を考えよう

Unity側に関しては、難しい処理はありません。
Photoshopで出力したJSONを読み込みながら、スプライトとボーンの配置を行います。

  • Meshの配置(JSONから位置情報を読み込んで、Photoshopパーツ位置の再現)
  • ボーンのアサイン
  • OrderInLayerの設定(JSONから読み込み)


前回の記事を引き継いで、シーン構成は下記の構成を予定しています。
f:id:sgtech:20180520234100j:plain


仕様

  • 選択されているスプライトを処理の対象にする
  • 選択されているものと同じフォルダにあるJSONを参照
  • 選択されているスプライト名からJSON内の配置情報を見つける
  • スプライトをmesh階層に配置,位置設定
  • ボーンをbone階層に配置,位置設定
  • メッシュにボーンをアサイン

完成スクリプト

以上をまとめたのが、こちらのスクリプトになります。

スクリプトの保存先

UnityプロジェクトのEditorフォルダに入れてください(無ければ作成)。

実行してみる

Projectウインドウで配置したいスプライトを選択し、Window -> Anima2D -> ImportAnima2D を実行します。
mesh階層に選択したキャラパーツのメッシュが配置され、ボーンが自動的に割り当てられます。
この後は、前回の記事を参考に、ボーンの向きの設定・メッシュ割り・ウエイトの調整を行ってください。
ここまで自動でできるとずいぶん楽ですね。

f:id:sgtech:20180520234054j:plain
f:id:sgtech:20180520234057j:plain

f:id:sgtech:20180520234211g:plain:w350


■描画最適化

さて、セットアップは自動化されて一件落着なのですが、FrameDebugger(Window->Frame Debugger)で確認すると、パーツ数分DrawMeshが走っています。
今回はシンプルなキャラクターですが、パーツ数の多いキャラクターや、モバイル環境などではパフォーマンスに影響が出てきますので、まとめて描画できるものは1回で描画するようにしましょう。

f:id:sgtech:20180520233917j:plain
FrameDebuggerでの確認(最適化前)

SkinnedMeshCombinerとは

前回の記事で、Examplesの中に入っているSkinnedMeshCombinerのお話をしました。
SkinnedMeshCombinerは登録してあるSpriteMeshをまとめて、1メッシュにしてくれるスクリプトです。
ゲーム再生時に1つのメッシュにまとめられます。
(プロジェクトに入っていない方は、Anima2Dのアセットからインポートしましょう)
f:id:sgtech:20180520233820p:plain

SkinnedMeshCombinerの使い方

SkinnedMeshCombinerコンポーネントを追加し、Sizeに登録したいメッシュ数を入力後、レイヤーが下のパーツから順に登録していきます。(この辺はPhotoshopのレイヤー並びと逆順になるので、混乱しますね)
1番目に登録してあるものから、順番に重ねていき、最終結果としてまとめられるイメージです。

f:id:sgtech:20180520233914j:plain

ですが、これでめでたく1ドローで描画されてめでたしめでたし!という訳にはいきません。
再生すると、SpriteMeshAnimationで付けた、目パチ・口パクが動かなくなっています。
どうやらパターンチェンジまで1メッシュに統合されて、パターンチェンジできなくなってしまうようです。

コンバインしてはいけないもの

メッシュ自体を操作している場合は、コンバインすると操作できなくなりますので、対象から外します。

  • SpriteMeshAnimation(パターンチェンジ)
  • 表示非表示アニメ(こちらも1メッシュにされてしまうと、ActiveのON/OFFアニメが効かなくなります)

SortingGroupの設定

さて、コンバインしてはいけないものを分けたとして、また新しい問題が出てきます。
今までパーツの前後関係はOrderInLayerで設定していましたが、内部的に生成されたコンバインメッシュに対して設定する方法がありません。
そこで、コンバインするもの/しないものを階層で分け、SortingGroupを使ってそれぞれの前後関係を指定します。

描画最適化(SkinnedMeshCombiner + SortingGroup)のキャラセットアップ例

以上の状況をふまえ、キャラクターセットアップは画像のようになります。
基本的に

  • 体 (draw1階層)
  • 目パチ, 口パク=SpriteMeshAnimation (draw2階層)→コンバインしない
  • 目口より手前のもの (draw3階層)

で4DrawMeshに収まると思います。

キャラトップ階層にもSortingGroupを付けておくことで、ゲーム中に複数のAnima2Dキャラクターを出したときの、キャラ間の前後関係にも対応することができます。
(SortingGroupはコンポーネントが付いている同階層内で前後関係を並べてくれますので、draw階層とは別の番号を割り当てて制御することができます)

f:id:sgtech:20180520233953j:plain
(基本構造説明のため、今まで説明していた青いキャラとは違うキャラの階層構造の画像を使用しています)

結果

FrameDebuggerで確認すると、かなり描画数が減ったと思います。(体+目+口)
f:id:sgtech:20180520233910j:plain

FrameDebuggerでの確認(最適化後)

スクリプト化する

以上の手順をスクリプト化したいのですが、デフォルトの状態ではSkinnedMeshCombinerにアクセスできないパラメーターがありますので、SkinnedMeshCombiner.csのコードを書き換えます。

SkinnedMeshCombiner.cs
修正前

using UnityEngine;
using System.Collections.Generic;
using Anima2D;

public class SkinnedMeshCombiner : MonoBehaviour
{
	[SerializeField]
	SpriteMeshInstance[] m_SpriteMeshInstances;

	SpriteMeshInstance[] spriteMeshInstances {
		get {
			return m_SpriteMeshInstances;
		}
	}


修正後

using UnityEngine;
using System.Collections.Generic;
using Anima2D;

public class SkinnedMeshCombiner : MonoBehaviour
{
	[SerializeField]
	SpriteMeshInstance[] m_SpriteMeshInstances;

	public SpriteMeshInstance[] spriteMeshInstances {
		get {
			return m_SpriteMeshInstances;
		}
		set {
			m_SpriteMeshInstances = value;
		}
	}

10行目に「public」を追加
14行目に「set {~}」の部分を追加

スクリプトの流れ

SpriteMeshAnimationを別グループにまとめるように処理をします。
1.階層をOrderInLayerの順で並べ替え
2.下の階層からSpriteMeshAnimationが出てくるまでSkinnedMeshCombinerに登録し、drawグループを作成していく。
3.SpriteMeshAnimationが出てきたら新規drawグループを作成。SpriteMeshAnimationが無くなるまで上にたどる。
4.SpriteMeshAnimationが無くなったら、引き続きSkinnedMeshCombinerに登録してdrawグループを作成していく。
階層の一番上まで繰り返す。

完成スクリプト

スクリプトの保存先

UnityプロジェクトのEditorフォルダに入れてください。

実行してみる

Winodw -> Anima2D -> Create Drawgroupを実行してください。
SpriteMeshAnimationが付いているものを避け、drawグループが作成されます。


まとめ

以上、2回に渡ってAnima2Dを紹介してきましたが、いかがでしたでしょうか?
Unity内のアニメーションツールなので、足りない機能などがあれば今回のように追加したり、ShurikenやSpringBoneなどの他のUnityコンポーネントとも自由に組み合わせて使うことができます。
今回PhotoshopからのエクスポートとUnityへのインポートスクリプトを掲載しましたが、基本的な部分のみですので、みなさんの作業に合わせてコードを改造して使ってみてください。
他のツールに比べて、制作物に合わせたフローを構築しやすいと思います。

f:id:sgtech:20180520233847g:plain


 TAセクションではこのようにアーティストの制作に役立つ環境を提供できるよう力を注いでおります。やりたいことを遠回りせずに行える環境でお仕事したい方はぜひ下記の弊社グループ採用サイトをご確認ください。現在さまざまな職種でアーティストを多数募集しております!
採用情報 | セガ企業情報サイト


(C)SEGA

Powered by はてなブログ