本記事はサムザップ #1 AdventCalendar 2020 #1の12/12の記事です。
前日の記事は@higuchi_yuta さんの【Unity】チュートリアル実装で大活躍!逆マスクを作るUnmaskForUGUI【uGUI】でした。
はじめに
みなさんは**Unityの力を用いずに自力でPrefabを作成してみたい!**と思ったことはないでしょうか?
ちなみに私はありません。
本記事では、Unityにおいて必須機能のPrefabについて、Unity外から、具体的に言えばテキストエディタから作成する方法について紹介します。
Unity外からPrefabを作成する意味はほぼ皆無ですが、Prefabを理解することでgitのDiffから把握できる情報は増えるかもしれません。
なお、UnityのバージョンはLTS版である 2019.4.16f1 を用います。
Prefabの中身を理解する
テキストエディタからいきなり作るのは難しいため、まずはPrefabの中身について見ていきます。
PrefabはYAML形式で記述されています。
YAMLとは、クラスのインスタンスなどの構造化されたデータを人間から読みやすいように表現したフォーマットです。
詳しく知りたい方は以下を参照してください。
最も単純なGameObjectの作成
まず、単一のオブジェクトのみの最も単純なGameObjectの中身を見てみます。
手順は以下の通りです。
- ヒエラルキーでシーン中にGameObjectを作成
- そのままPrefab化
- 2.のPrefabをテキストエディタで開く(シンタックスハイライトがつくIDEで開くのがオススメです)
すると、以下のYAMLが表示されます。
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &6087850051989556154
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2636687155612065025}
m_Layer: 0
m_Name: GameObject
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &2636687155612065025
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6087850051989556154}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
これについて軽く解説をしていきます。
全体を見ると、このYAMLは"---"で区切られて各情報がまとめられています。
# ヘッダ情報
%YAML 1.1
...
# GameObject情報
--- !u!1 &ddddd
GameObject:
...
# Transform情報
--- !u!4 &ddddd
Transform:
...
そのため、Prefabの情報は大きく分けて以下の3つに大別できます。
- ヘッダ
- GameObjectの情報
- コンポーネント情報(ここではTransformコンポーネント)
ここから、Prefabの各項目について解説していきます。
ヘッダ
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
これがない場合、他のUnityプロジェクトで使おうとした場合に正常に読み出せません。
GameObject
--- !u!1 &6087850051989556154
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 2636687155612065025}
m_Layer: 0
m_Name: GameObject
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
まず最初の行--- !u!1 &6087850051989556154
文字列について見ていきます。
YAMLでは、—-
から始まる行はドキュメントマーカーと呼び、Unityではこれをオブジェクト(コンポーネント)の境界として用います。
また、!u!
の後の最初の数値はクラスを表し(この場合ではGameObject)、続く&
の後の数値はこのオブジェクトのfileID(後述)を表します。
ドキュメントマーカー以下にはGameObject本体、名前やタグの情報などに加えて、
...
m_Name: GameObject # 名前
m_TagString: Untagged # タグ情報
...
付与されているコンポーネントへの参照情報がfileIDを通じて記述されています。
m_Component:
- component: {fileID: 2636687155612065025} # 複数ある場合は下に - component: {fileID: xxxx} と続く
なお、各行は
変数名: 値
で記述されており、それぞれがクラス(ここではGameObject)のメンバ変数に対応しています。
fileID
Prefab内における各GameObject、コンポーネントへの参照関係はfileIDを使って保持しています。
それぞれのObjectのfileIDは、---!u! &
以下に記述されています。
例えば、上記TransformコンポーネントのfileIDは2636687155612065025
です。
参照関係を保持するため、一つのPrefab内におけるfileIDはユニークでなければなりません。
逆に言えば、ユニークでさえあればfileIDは適当に設定しても問題ありません。
なお、適当に設定してokとは言いましたが、多少の制約はあるようで、fileIDを0 or 1
に設定するとUnityがクラッシュします。
コンポーネント
最後に、コンポーネントの情報です。
--- !u!4 &2636687155612065025
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6087850051989556154}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_Children: []
m_Father: {fileID: 0}
m_RootOrder: 0
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
ここには、コンポーネントの各情報が記述されます。
まずGameObject同様、ドキュメントマーカーが記述されていますが!u!
の後の数値が異なることが分かります。(今回はTransformコンポーネントなので4
)
本体の記述部には、TransformコンポーネントらしくRotationやPosition、Scaleに加えて、子オブジェクト(Transform)への参照が記述されています。
...
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} # Rotation
m_LocalPosition: {x: 0, y: 0, z: 0} # Position
m_LocalScale: {x: 1, y: 1, z: 1} # Scale
m_Children: [] # 子への参照
...
また、それに加えて、付与先のGameObject情報がfileID(後述)を介して記述されています。
...
m_GameObject: {fileID: 6087850051989556154}
...
Prefabの最小構成を模索
さて、これまでPrefabの構成をざっくり解説してきましたが、実際に自分で書くにはいささか行数が多いように見えます。
そこで、Prefabの無駄な行を減らすことを考えてみます。
なお、YAML上で値の指定がない変数はデフォルト値が設定されるようです。
そのため、デフォルト値が設定されている行をエラーが出なくなるまで削除していきます。
その結果が下のPrefabです。
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &6087850051989556154
GameObject:
m_Component:
- component: {fileID: 2636687155612065025}
--- !u!4 &2636687155612065025
Transform:
m_GameObject: {fileID: 6087850051989556154}
大分スリムになったと共に、本当に必要な情報はGameObjectとコンポーネントの双方への参照であるということが分かりました。(※少なくとも最小のGameObjectにおいては)
なお、GameObjectやTransform自体の情報を削除してしまうと「Prefabが壊れています」といったニュアンスのエラーが出ます。
実際に作ってみた
では実際にテキストエディタからPrefabを作成してみます。
まず、空のファイルを作成し、"(任意の文字列).prefab"とリネームします。
Windowsの場合はwarningのポップアップが出ますが無視しましょう。
次に、テキストエディタで作ったPrefabを開きます。
開いたらまず、YAMLのヘッダ情報を記述します。
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
これを書かない場合、そもそもPrefabとして認識されない、UnityからPrefabのバージョンが異なるという警告が表示される、などが発生します。
次はGameObjectの情報を記述します。
--- !u!1 &GameObjectのfileID
GameObject:
m_Component:
- component: {fileID: TransformのfileID}
まず、オブジェクトのドキュメントマーカーを記述します。
今回はGameObjectを記述したいので、!u!
の後の数値は1
を指定してください。
次に、Transformの情報を記述します。
--- !u!4 &TransformのfileID
Transform:
m_GameObject: {fileID: GameObjectのfileID}
ドキュメントマーカーの記述の際、Transformの場合は!u!
の後の数値は4
を指定する必要があることに注意してください。
最後に、GameObjectのfileIDとTransformのfileIDを記述して完成です。
今回は適当にそれぞれ256と512を指定しました。
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &256
GameObject:
m_Component:
- component: {fileID: 512}
--- !u!4 &512
Transform:
m_GameObject: {fileID: 256}
これをUnityプロジェクトのAssets以下に配置して読ませてみます。
無事インポート出来ました。
また、Debugで表示すると、fileID(Inspector上ではLocal Identifier In File)も上記で設定した通り、256と512になっていることが分かります。
最後に
今回、Prefabの構造のざっくりした解説とテキストエディタからPrefabを作成しました。
繰り返しますが、テキストエディタからPrefabを作成する意味はほぼありませんが、結果的にプルリクからPrefabの変更点を多少読み取れるようにはなりました。
ここまで読んでいただきありがとうございました。
明日は、@s1na9ak1さんの記事です。
また、サムザップのアドベントカレンダーは人数の関係上2つあります。
こちらもよろしくお願いいたします。
サムザップ #2 Advent Calendar 2020 - Qiita
参考にしたページ:
- https://www.shibuya24.info/entry/2017/03/27/093000
- https://docs.unity3d.com/ja/2019.4/Manual/FormatDescription.html
- https://magazine.rubyist.net/articles/0009/0009-YAML.html
- https://yaml.org/spec/1.2/spec.html
余談
GameObjectからのTransformへの参照に fileID:0
を指定するとTransformが増殖。