【Unity】機能を深堀してみる【Aseprite Importer】
概要
当記事は、ゲームエンジンのUnity、ピクセルアート作成に特化したグラフィックスツールのAseprite、それらをつなぐAseprite Importerを組み合わせて2D Animation構築で楽できないか?を試行錯誤した前回の記事の続きの内容です。
参考資料は少ない現状ですが、公式Documentを中心に見ていき、前回記事では取り上げきれなかった機能や実際の組み合わせ例を試行錯誤してみます。
使用環境
各種ソフトウェアは前回記事と一緒です。
- aseprite Importer ver 1.1.6
公式のサポートする機能
基本用語
確認用に画像を載せておきます。
Aseprite feature (Aseprite上での操作が中心となる機能)
- .ase形式と.aseprite形式(以下、Asepriteファイル)の取り込みが可能。
- Asepriteで任意のピクセルアートを作成後、所定の形式で保存した後、UnityのProjectウィンドウにドラッグ&ドロップすればOK。
- カラーモードについても特別気にする必要はなく、Asepriteで編集可能な以下のすべてが問題なく取り込み可能。
- RGBA
- グレースケール
- インデックス(gifのような形式)
- 表示・非表示レイヤーに対応
- デフォルトでは非表示レイヤーは取り込まない設定
- ただし、UnityEditor上でいつでも非表示レイヤーを再表示可能
- 非表示レイヤーを個別に再表示できるわけではなく、まとめて再表示する動作である点には注意
- レイヤー合成モードに対応
- 取り込み設定、Import Modeプロパティの値がMerge framesのときは全ての合成モードに対応(詳細は後述)
- レイヤーとセルの透明度対応
- 前述のレイヤー合成モードと組み合わせると半透明の画像を重ねた(含めた)アニメーションも取り込み可能
- Linked Celsにも対応
- Asepriteファイル取り込み後にセル内容が展開された画像を確認すると、AsepriteのLinked Celsと同じ仕様で1つの画像に複数のAnimation Clipから参照(リンク)されていることがわかる。
- Tags
- TagのプロパティにあるAnimation Directionの項目がForwardの時のみ対応
- Tagに付与されるrepeating(繰り返し)設定については状況に応じて、以下のように解決される
- ∞のとき:ループされるAnimation Clipを生成する
- 1以上の何かしらの定数のとき:ループをしないAnimation Clipを生成する
- Individual frame timingsに対応
- アニメーションの切り替わり間隔をフレーム別にそれぞれ設定できること
- フレームのプロパティにあるDurationの数値(ミリ秒単位)を変更することで設定できる
- それらの値はUnityにて、Animationウィンドウ(Animation Clip)のタイムラインを操作することで各フレームへ個別に設定できるアニメーション間隔へ忠実に反映される
- Layer groups
- レイヤーグループそれぞれに施された表示・非表示設定を忠実に反映する
- import modeがindividual layersであるときのみLayer groupのデータがUnityに生成される(詳細は後述)
Importer feature (UnityEditor上での操作が中心となる機能)
取り込まれたAsepriteファイルは3Dモデルデータを取り込んだときのようなPrefabの形でProjectウィンドウ上に展開されます。ファイルをクリックすると、InspectorにAseprite Importerによる各種取り込み方法に関わる情報が表示され、その中身を細かく変更できる形になっています。
ここでは、各種プロパティとそこに設定できる値に触れ、Asepriteでの編集内容とUnity上でのAsepriteファイルの取り扱われ方のすり合わせを行います。
General
- Import Mode : .aseおよび.asepriteファイルをどのようにUnityに取り込むかの設定
- Sprite Sheet : Sprite Editorで編集可能な単一の「スプライトシート」として取り込む.
- Asepriteファイルの中身がフレームに分かれていて、Tag付けなどがなされていても全てのフレームの画像が1枚のスプライトに変換される。スプライトアトラスのイメージに近いか。
- 内部的には、裏側でアニメーションデータなどは全て保有している状態にあり、再度値をAnimated Spriteに戻してApplayすれば、元に戻る。
- Animated Sprite : デフォルト値。Asepriteファイル内部に2フレーム以上の情報が存在するとアニメーション関連のデータが自動生成される
- Sprite Sheet : Sprite Editorで編集可能な単一の「スプライトシート」として取り込む.
- Pixels Per Unit : Textureの取り込み設定でおなじみのプロパティのため割愛
- Mesh Type : Textureの取り込み設定でおなじみのプロパティのため割愛
- Generate Physics Shape : 透明ではない部分の画像形状(アウトライン)を元に物理エンジン(Sprite Renderer + Polygon Collider 2D)処理などで使える当たり判定(Physics Shape)を「まだ生成していなければ自動で生成する」オプション。ただし、このオプションにチェックをいれていなくてもPolygon Collider 2DにResetをかけると自動で生成されている様子が見て取れ、正しく動作しているか判断がつかない。
Layer Import
-
Include Hidden Layers : Asepriteファイル内にAsepriteで非表示にしていたレイヤーが含まれている場合、それも取り込み対象とする。
-
Import Mode : 同じフレーム上に存在する全レイヤーを統合して取り込むのか、レイヤーをそれぞれ別のものとして取り込むのかを選択する
- Merge Frame、同じフレーム上のレイヤーを全て統合して処理する。デフォルト値
- Indivisual Layers、レイヤーをそれぞれ別のものとして取り込む。こちらを選択したうえで、Asepriteファイル内部にレイヤー複数用意されていた場合、それぞれのレイヤー毎に専用のプレハブが生成される。なお、レイヤーが1枚しかない場合は選択しても効果はない。次の画像はレイヤーが2層あった場合のもの。
-
Pivot Space : AsespriteファイルのPivot(配置基準点)を何を根拠に算出するかを選択する。ピクセルアートが非線対称に書かれている場合など、中心軸に影響するので注意すること。
- Canvas、元々Asperiteで編集していたキャンバスサイズに従う。デフォルト。例:下図の赤枠
- Local、Asepriteファイル取り込み後のUnity内部のルールに従う。具体的には、余白をカットされた後の長方形画像サイズ?という理解でよさそう。例:下図の青枠。後述のSprite Paddingの影響を受ける。
-
Pivot Alignment : このスプライトが実際に配置される場合に、位置揃えを前述のPivot Space(枠)のどこを基準にするかを決める。デフォルトはBottom(=Bottom Center)、(x,y)=(0.5, 0.0)の位置。トップビューのゲームではCenterなどがオススメ。以下の図は、Asepriteファイルで取り込んだデータのPivot SpaceをCanvas、Pivot AligmentをBottomにした状態で、Positionが(x,y,z)=(0,0,0)のGameObjectとして表示したもの。
赤点が原点(0,0,0) -
Mosaic Padding : AsepriteファイルはUnityに取り込まれる際、その過程でデータ内部の各セルの画像を展開した1枚の画像を作成します。このMosaic Paddingはセル展開の際に、隣接するセル同士の距離をどれくらい離すかというものです。大きすぎても余白がもったいないので、デフォルト値のままで問題はないと思います。
余白が4のとき
余白が8のとき -
Sprite Padding : Mosaic Paddingと関連して、セル展開時にセル内容に設けたい余白サイズです。デフォルトは0。
Paddingが4のとき
Generate assets
Aseprite Importerには、取り込んだAsepriteファイルをそのままプレハブとしてアセット化する機能、Animation関係のファイルを生成する機能があり、以下のプロパティのON/OFFを切り替えてその内容を調整できます。
-
Model Prefab : 取り込んだAsepriteファイルそのものを第1フレームの画像がセットされたSprite RendererコンポーネントをもつGameObjectのプレハブとしてHierarchyに配置できるようにする。デフォルトはON(true)。
- Asepriteファイル内部が複数のレイヤーで構成されており、Layer Import -> Import Mode が Indivisual Layers にセットしてある場合には、Animatorコンポーネントだけをもった親GameObjectに(Groupも考慮された)レイヤー毎に別れた子GameObjectをもつ階層化されたプレハブが生成される。Asepriteでの編集作業時に使っていたレイヤー順序もOrder in Layerプロパティにその順序として綺麗に反映されます。
- Asepriteファイル内部が複数のレイヤーで構成されており、Layer Import -> Import Mode が Indivisual Layers にセットしてある場合には、Animatorコンポーネントだけをもった親GameObjectに(Groupも考慮された)レイヤー毎に別れた子GameObjectをもつ階層化されたプレハブが生成される。Asepriteでの編集作業時に使っていたレイヤー順序もOrder in Layerプロパティにその順序として綺麗に反映されます。
-
Sorting Group : Layer Import -> Import Mode が Indivisual Layers にセットしてある場合、生成されるプレハブの親GameObjectに「Sorting Group」コンポーネントを追加する。Model PrefabがONの時のみ編集可能。Sorting Groupコンポーネントは複雑に階層化されたプレハブやGameObject同士がゲーム画面内で重なったときの表示順序を調整するためのコンポーネントです。
-
Shadow Casters : 生成されるプレハブに「Shadow Caster 2D」コンポーネントを追加する。Layer Import -> Import Mode が Indivisual Layers にセットしてある場合、親GameObjectには「Composite Shadow Caster 2D」コンポーネントが、子GameObjectには「Shadow Caster 2D」コンポーネントが自動で取り付けられます。Model PrefabがONの時のみ編集可能、デフォルトはOFF(false)。次の画像は、画面右上から左下に向けて強い光を放つスポットライト2Dを設置し、Asepriteファイルのプレハブに設定されている「Shadow Caster 2D」コンポーネントの効果によって影ができている画像。
-
Animation Clip : Asepriteファイル内部に複数のフレームが存在する場合にAnimation Clip、Animator Controllerファイルを生成する。Animation Clipはフレームがタグ付けされている場合はその数分が、ない場合は全フレームを結合した1つのファイルを生成する。デフォルトはON(true)。
-
Export Animation Assets : ボタン。押下するとAnimation ClipとAnimator Controllerの2種のファイル群を任意のフォルダに再出力するためのウィンドウが表示される。Asepriteファイル下のデータは外部ツール(Aseprite)でいつ上書きされてもおかしくない状態となっていて、基本読み取り専用と考えるべきです。そのため、それらを編集したい場合はこのボタンから保存しなおし、外部の影響を受けない状態にさせましょう。
Texture, Platform Settings, Advanced
この3種の項目は、Textureの取り込み設定とほぼ同一のものとなるため、以下の留意事項以外は割愛します。
- Asepriteファイルの取り込み後、少しでもなにかしらのプロパティを編集してApplyすると、ピクセルアートを綺麗に見せる設定でおなじみのFilter Mode : Point(no filter) および Platform Settings -> 任意のプラットフォーム(ないしはDefault) -> Compression : None の2点のデフォルト設定が上書きされ、画像が汚くなる。
- 例えば、取り込み直後はCompression:Normal Qualityと記載はあるが、どうみてもCompression:Noneの様に無圧縮な綺麗な状態に見えており、何か編集してApplayしてしまうとそこで初めてCompression:Normal Qualityの状態に変わってしまっている様子。
改めて目的の値に設定しなおして、再度Applyするべし!
- 例えば、取り込み直後はCompression:Normal Qualityと記載はあるが、どうみてもCompression:Noneの様に無圧縮な綺麗な状態に見えており、何か編集してApplayしてしまうとそこで初めてCompression:Normal Qualityの状態に変わってしまっている様子。
- Platform Settings -> 任意のプラットフォーム(ないしはDefault) -> Max Size の値は取り込まれたAsepriteファイル内部にある、全セルをひとまとめにしたTextureを確認し、横幅、立幅のうち、ピクセル数のより大きい方を選択、その値以上の2のべき乗値を設定すると良い。例えば、以下の画像上では128*256で記録されていたことが判明したので256の数値を採用した。
その他の特殊操作、特殊機能
Animation Event
Animation EventはUnityに備わっている、Animation Clipの任意の瞬間を検出する仕組みです。任意のAnimation Clipをダブルクリックすると出現するAnimationウィンドウ上にあるAdd Eventボタンをクリックすれば、挿入可能です。
Aseprite Importerでは、特定のステップを踏むことでAseprite上でEventのタイミングを仕込むことが可能です。
まずは、AsepriteでAsepriteファイルを以下のように編集します。
- Asepriteのタイムライン上から任意のセルを選択し、右クリック-> Cel Properties... をクリック。
- 出現した小窓の右上、×ボタンの下側にある「User Data」ボタンをクリック
- 追加で出現した、User Data入力枠に次のフォーマットで半角英数字を入力
- event:EventName
- 「event:」まで固定、「EventName」の部分は任意の文字列(例:Impact)を指定する
- ファイルを上書き保存
ここから、Unity側へシフト。当該Asepriteファイルを取り込んでなければ、取り込みます。プロジェクトに取り込み済のAsepriteファイルであれば、自動更新されるのでそのまま作業を続行します。
- 上記で編集したセルを含んだTagがついているAnimation ClipをダブルクリックしてAnimationウィンドウをひらく
- 指定した名称のイベント(例:Impact)が挿入されていることを確認する
続けて、発生したEventを検出する仕組みをスクリプトで用意します。
- MonoBehaviourを継承した任意のC#スクリプトファイル(コンポーネント)を作成する
- 前述の「EventName」で指定したモノと同じ名称(例:Impact)、publicで引数なしのメソッドを作成する
using UnityEngine;
public class EventReceiverSample : MonoBehaviour
{
public void Impact()
{
Debug.Log("Impact");
}
}
- 上記のコンポーネントを任意のGameObjectにアタッチする
- 同じGameObjectにSpriteRendererコンポーネントをアタッチする
- 同じGameObjectにAnimatorコンポーネントをアタッチし、Controllerプロパティの参照値をAsepriteファイル内のAnimator Controllerファイルに変更(User Dataを仕込んだAnimation Clipへの参照をもっているデータであること)
UnityEditor.U2D.Aseprite
Editor拡張などに利活用できるScripting API
が提供されており、Asepriteファイルの内部情報(属性)にアクセスしたり、Asepriteファイルをプロジェクトに取り込んだ事をイベントとして検出できるようになっています。
未実装機能
以下の機能はまだ(2024/11/13, ver1.1.6時点で)未実装とのこと。どちらもAseprite関係なしにUnityEditor上で既に実現されている機能だが、もしこれらの機能をAsepriteから橋渡しで行える(自動生成される)様になるのであれば、アーティストが普段使いのグラフィックスツールで作業を99%完結できる点がとても強力ですね。早期の実装が待たれる・・・。
- Slices:9-Sliceのこと。Unityでは、ボタン枠や会話枠用の背景画像をSprite Editorで分割、枠を伸縮させてもそれに合わせて画像が伸び縮みできる(ないしは繰り返しで敷き詰め表示が行える)という例のあれです。
- Tilemaps:Unityにも同じ名称でほぼほぼ同じ機能のものが存在します。
追記(2024/12/3)
以下、特定の作業において不都合が出たので、対処例を紹介
uGUIにおけるImage Animation
Aseprite Importerは現状、Asepriteで作成したアニメーションデータをUnityではSprite Rendererコンポーネント(のSpriteプロパティ)を対象に再生するAnimation Clipを生成する形で実現している。さて、ここでAsepriteがピクセルアートに特化したグラフィックツールである点を考慮すると、作ったアニメーションデータ・・・UIでも使いたいよね?ということで、UnityのuGUI上でImageオブジェクト(ImageコンポーネントのついたRectTransformのGameObject)で使うためのワークフローを自分なりに整理してみました。
- アニメーションのあるasepriteファイルをProjectウィンドウにドラッグ&ドロップ
- 1の工程で取り込んだファイルをクリックし、Inspector上のExport Animation Assetsボタンを押下してAnimation ClipとAnimator Controllerのファイルを出力
- Canvasオブジェクトをルートとした(つまりはuGUI上)の任意のGameObjectに、Imageコンポーネント/Animatorコンポーネントをアタッチし、2の工程で出力したAnimator Controllerファイルの参照を渡す
- 3の工程により、2の工程で出力したAnimation ClipをAnimationウィンドウで開くと、Add Propertyボタンが押下できるようになるので、Image > Image.Spriteの横にある+をクリック。GameObject : Image.Sprite がリストに出現する。
- 初めから存在していたGameObject:Spriteのプロパティの内容(Keyとそれに紐づいているSpriteデータ)をコピーし、4の工程で追加した、GameObject : Image.Spriteのプロパティにペーストする。
最後の工程は、まとめてコピーペーストができれば楽なのですが、仕様上できない?ようなのでKey毎にひとつひとつSpriteを移植する作業をしました。操作にコツがいる。Animation ClipをuGUI用のAnimation(Image+Animator)として出力する機能が是非欲しいですね。
余白詰めによるアニメーション不整合
上記のuGUIでImage Animationを行おうとした時に、合わせて発生した問題点です。
当記事でも何度か説明しましたが、Aseprite Importerは複数フレームに分かれているアニメーションデータをまとめて取り込むにあたって、各フレーム毎に描かれたピクセルアートの(透明でない部分を最大限囲うことのできる長方形のサイズにまで)余白を削った上で全てを1枚の画像に整理して詰め込んでいます。軽量化と処理効率の観点から良い裏処理ではありますが、次のような問題もはらんでいました。
例えば、フレーム毎に激しく横幅(もしくは縦幅、さらには双方)、ピクセルアートの中心軸、中心点の位置などが変化するアニメーションデータがあったとします。これをImageコンポーネントに適用すると、ImageコンポーネントはSpriteプロパティに格納した画像をImageコンポーネントの横幅と縦幅の大きさに合わせて拡大縮小する仕様となるため、「Imageの大きさは変わらずとも、中の画像の大きさが変化するため、それをムリヤリ拡大縮小する」動きをとってしまいます。
縦長の画像がImageの大きさに合わせて縮小されてしまい、アニメーションがゆがむサンプル
さてこの現象への対処ですが、一つ目はAseprite Importerの仕様を踏まえた「基準」をアーティストとエンジニア間に設ける形です。例えば、以下のように基準を決めたうえでAseprite Importerで適切なプロパティを設定することで問題を解消できる可能性があります。
- キャンバスの最下段(y=0)を床と見立ててアートを描く
- キャンバスの中心点を必ずキャラクターの正中線とする
それ以外の方法では、Aseprite Importerの仕様の裏をつき、技術的にゴリ押す方法もあります。私の場合は、Aseprite側で編集しているピクセルアートの背景に透明色を用いず、任意の1色(クロマキー)で塗りつぶしを行った上でAseprite Importerに通しています。これにより余白を詰める処理が効かなくなります。その後、Unity側で任意の1色を透過させるシェーダー(もしくはシェーダーグラフ)を作成し、任意の1色を透明色に変化させます。
ただ、できればで良いのですが(軽量化のために良い処理であることを理解はできるが)余白を詰めないで取り込む機能も是非検討して欲しいですね。
終わりに
今回整理した機能に関しては、Asepriteにピクセルアートをどのように構築したか、Unity上でAseprite Importerをどのように設定したか、そしてそれらを最終的にゲームにどのような形で利活用するのかという広い範囲にまたいでいることに加え、どのプロパティでどのような取り込み結果になるか名称から予想がつかないことが多かった点で手を焼きました。なんとか整理できたかなー・・・?
全てのUnityとAsepriteユーザーに幸あれ!!
Discussion