「え！UnityYAML読めないんですか？」

なんて同僚たちにマウント取られる前にUnityYAMLの勉強をしておこうと思った記事です。
一応調べましたが、やっぱりよくわかりません。

QualiArtsアドカレ 3日目の記事になります。

# UnityYAMLとは
[UnityYAML](https://docs.unity3d.com/2021.3/Documentation/Manual/UnityYAML.html)とは、Unityのアセットのシリアライズ形式の一つで、yaml記法のサブセットです。
Asset SerializationにForce Textを指定するとこの形式でprefabなどのアセットが保存されるようになります。

![force text.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5973/a97490f2-f94b-1eb5-18e3-bb10e2aaf0da.png)

とりあえずForce Textを選択しているものの、特にprefabは巨大なyamlになりがちで、読み方もよくわからないしで、ほとんど向き合ってきませんでした。
ところがしばらく前に、Unity公式から
***[Unity のシリアライズ言語 YAML を理解する](https://blog.unity.com/ja/technology/understanding-unitys-serialization-language-yaml)***
という記事が公開されました。ありがたいことに日本語翻訳されているのでまだ読んでない方は是非読んでみてください。

# [Unity のシリアライズ言語 YAML を理解する](https://blog.unity.com/ja/technology/understanding-unitys-serialization-language-yaml) を読んでみて
上記記事の内容が素晴らしいので、一通り目を通してUnityYAMLを完全に理解した気持ちになれたので、手元のprefabを読んでみました。

<details><summary>CardBoxScreen.prefab(sample)</summary>

```y
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1865014687417162
GameObject:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  serializedVersion: 6
  m_Component:
  - component: {fileID: 224475894893193696}
  - component: {fileID: 222259688764093502}
  - component: {fileID: 114916577866252144}
  m_Layer: 5
  m_Name: Text
  m_TagString: Untagged
  m_Icon: {fileID: 0}
  m_NavMeshLayer: 0
  m_StaticEditorFlags: 0
  m_IsActive: 1
--- !u!224 &224475894893193696
RectTransform:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1865014687417162}
  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: 2527234515757153141}
  m_RootOrder: 0
  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
  m_AnchorMin: {x: 0.5, y: 0.5}
  m_AnchorMax: {x: 0.5, y: 0.5}
  m_AnchoredPosition: {x: 0, y: 215.39001}
  m_SizeDelta: {x: 366.41266, y: 203.70001}
  m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &222259688764093502
CanvasRenderer:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1865014687417162}
  m_CullTransparentMesh: 0
--- !u!114 &114916577866252144
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1865014687417162}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
  m_Name: 
  m_EditorClassIdentifier: 
  m_Material: {fileID: 0}
  m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
  m_RaycastTarget: 1
  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
  m_Maskable: 1
  m_OnCullStateChanged:
    m_PersistentCalls:
      m_Calls: []
  m_FontData:
    m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
    m_FontSize: 50
    m_FontStyle: 0
    m_BestFit: 0
    m_MinSize: 0
    m_MaxSize: 69
    m_Alignment: 4
    m_AlignByGeometry: 0
    m_RichText: 1
    m_HorizontalOverflow: 1
    m_VerticalOverflow: 1
    m_LineSpacing: 1
  m_Text: "\x04\u30AB\u30FC\u30C9\u4E00\u89A70"
--- !u!114 &2281633851196779435
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1794220058932668345}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 54274acba9bd3450992ce3a66d0e7af2, type: 3}
  m_Name: 
  m_EditorClassIdentifier: 
  _root: {fileID: 3368266533159312908}
--- !u!114 &5223233202539212553
MonoBehaviour:
  m_ObjectHideFlags: 0
  m_CorrespondingSourceObject: {fileID: 0}
  m_PrefabInstance: {fileID: 0}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1794220058932668345}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: c3ddc448688d94df2b8f9019109edfc6, type: 3}
  m_Name: 
  m_EditorClassIdentifier: 
  _view: {fileID: 2281633851196779435}
  _screenCommonRoot: {fileID: 6423277813639145140}
  _cardListPresenter: {fileID: 4475072414863852767}
--- !u!1001 &1793172451492897065
PrefabInstance:
  m_ObjectHideFlags: 0
  serializedVersion: 2
  m_Modification:
    m_TransformParent: {fileID: 0}
    m_Modifications:
    - target: {fileID: 1400207620163216, guid: 094164b28ecc049328ef3063313e741b, type: 3}
      propertyPath: m_Name
      value: CardBoxScreen
      objectReference: {fileID: 0}
    - target: {fileID: 2148239045628858729, guid: 094164b28ecc049328ef3063313e741b,
        type: 3}
      propertyPath: m_RootOrder
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 2148239045628858729, guid: 094164b28ecc049328ef3063313e741b,
        type: 3}
      propertyPath: m_LocalPosition.x
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 2148239045628858729, guid: 094164b28ecc049328ef3063313e741b,
        type: 3}
      propertyPath: m_LocalPosition.y
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 2148239045628858729, guid: 094164b28ecc049328ef3063313e741b,
        type: 3}
      propertyPath: m_LocalPosition.z
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 2148239045628858729, guid: 094164b28ecc049328ef3063313e741b,
        type: 3}
      propertyPath: m_LocalRotation.w
      value: 1
      objectReference: {fileID: 0}
    - target: {fileID: 2148239045628858729, guid: 094164b28ecc049328ef3063313e741b,
        type: 3}
      propertyPath: m_LocalRotation.x
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 2148239045628858729, guid: 094164b28ecc049328ef3063313e741b,
        type: 3}
      propertyPath: m_LocalRotation.y
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 2148239045628858729, guid: 094164b28ecc049328ef3063313e741b,
        type: 3}
      propertyPath: m_LocalRotation.z
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 2148239045628858729, guid: 094164b28ecc049328ef3063313e741b,
        type: 3}
      propertyPath: m_LocalEulerAnglesHint.x
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 2148239045628858729, guid: 094164b28ecc049328ef3063313e741b,
        type: 3}
      propertyPath: m_LocalEulerAnglesHint.y
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 2148239045628858729, guid: 094164b28ecc049328ef3063313e741b,
        type: 3}
      propertyPath: m_LocalEulerAnglesHint.z
      value: 0
      objectReference: {fileID: 0}
    m_RemovedComponents: []
  m_SourcePrefab: {fileID: 100100000, guid: 094164b28ecc049328ef3063313e741b, type: 3}
--- !u!1 &1794220058932668345 stripped
GameObject:
  m_CorrespondingSourceObject: {fileID: 1400207620163216, guid: 094164b28ecc049328ef3063313e741b,
    type: 3}
  m_PrefabInstance: {fileID: 1793172451492897065}
  m_PrefabAsset: {fileID: 0}
--- !u!114 &6423277813639145140 stripped
MonoBehaviour:
  m_CorrespondingSourceObject: {fileID: 4739671352265131933, guid: 094164b28ecc049328ef3063313e741b,
    type: 3}
  m_PrefabInstance: {fileID: 1793172451492897065}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 1794220058932668345}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: add964d6de6884f75a1f30247ae54694, type: 3}
  m_Name: 
  m_EditorClassIdentifier: 
--- !u!224 &2527234515757153141 stripped
RectTransform:
  m_CorrespondingSourceObject: {fileID: 4318999042065406556, guid: 094164b28ecc049328ef3063313e741b,
    type: 3}
  m_PrefabInstance: {fileID: 1793172451492897065}
  m_PrefabAsset: {fileID: 0}
--- !u!225 &3368266533159312908 stripped
CanvasGroup:
  m_CorrespondingSourceObject: {fileID: 3917247981887962917, guid: 094164b28ecc049328ef3063313e741b,
    type: 3}
  m_PrefabInstance: {fileID: 1793172451492897065}
  m_PrefabAsset: {fileID: 0}
--- !u!1001 &2728508175332412409
PrefabInstance:
  m_ObjectHideFlags: 0
  serializedVersion: 2
  m_Modification:
    m_TransformParent: {fileID: 2527234515757153141}
    m_Modifications:
    - target: {fileID: 3127823570204878437, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_AnchorMax.x
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 3127823570204878437, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_AnchorMax.y
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 3127823570204878437, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_AnchorMin.x
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_Pivot.x
      value: 0.5
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_Pivot.y
      value: 0.5
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_RootOrder
      value: 1
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_AnchorMax.x
      value: 0.5
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_AnchorMax.y
      value: 0.5
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_AnchorMin.x
      value: 0.5
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_AnchorMin.y
      value: 0.5
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_SizeDelta.x
      value: 100
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_SizeDelta.y
      value: 100
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_LocalScale.x
      value: 1
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_LocalScale.y
      value: 1
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_LocalScale.z
      value: 1
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_LocalPosition.x
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_LocalPosition.y
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_LocalPosition.z
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_LocalRotation.w
      value: 1
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_LocalRotation.x
      value: -0
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_LocalRotation.y
      value: -0
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_LocalRotation.z
      value: -0
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_AnchoredPosition.x
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_AnchoredPosition.y
      value: -120
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_LocalEulerAnglesHint.x
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_LocalEulerAnglesHint.y
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 4720499058283198052, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_LocalEulerAnglesHint.z
      value: 0
      objectReference: {fileID: 0}
    - target: {fileID: 4732023361674043342, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_Size
      value: 1
      objectReference: {fileID: 0}
    - target: {fileID: 4821767156134577128, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_AnchoredPosition.x
      value: -0.0000038146973
      objectReference: {fileID: 0}
    - target: {fileID: 7042572429314006190, guid: 631e66137c7824aa79f18da746121ef2,
        type: 3}
      propertyPath: m_Name
      value: CardList
      objectReference: {fileID: 0}
    m_RemovedComponents: []
  m_SourcePrefab: {fileID: 100100000, guid: 631e66137c7824aa79f18da746121ef2, type: 3}
--- !u!114 &4475072414863852767 stripped
MonoBehaviour:
  m_CorrespondingSourceObject: {fileID: 2001635751693651750, guid: 631e66137c7824aa79f18da746121ef2,
    type: 3}
  m_PrefabInstance: {fileID: 2728508175332412409}
  m_PrefabAsset: {fileID: 0}
  m_GameObject: {fileID: 0}
  m_Enabled: 1
  m_EditorHideFlags: 0
  m_Script: {fileID: 11500000, guid: 7cd2f7b60e5044a60b6a926eefc18d83, type: 3}
  m_Name: 
  m_EditorClassIdentifier: 
```

</details>

MonoBehaviourがいくつか付いている、ということ以外、僕の読解力では何もわかりませんでした・・・

# UnityYAML読解補助ツールを作ってみる

わからなすぎたので次のような可視化ツールを作ってみました。

- UnityYAMLの要素をTreeViewとして表示
- `!u!1`のような記述を `GameObject` などのようなクラス名に置き換える
- 参照やスクリプトに応じたIconを表示
- GUIDをファイルパスに置き換える

![movie3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5973/f2e4db86-1e13-922b-11f1-9102d7121ff7.gif)

これなら完全にUnityYAMLを理解したような気がしてきます。
・・・本当はヒエラルキー構造も表したいところですが。

以下、細かい実装やハマりどころを紹介して行きますが、もし、とりあえず試しに動かしてみたいよっていう方は、以下のrepoからインストールしてみてください。
https://github.com/satanabe1/asset-yaml-tree-view
注意点として、yamlのパーサにYamlDotNetを採用しているので、[README.md](https://github.com/satanabe1/asset-yaml-tree-view/blob/main/README.md)に書いてある通り、 `yamldotnet` かYamlDotNetを取り込んでいる `com.unity.visualscripting`(1.6.0以上)を一緒にインストールする必要があります。

# UnityYAML読解補助ツールを作るまで
## UnityYAMLの読み込み
### yamlパーサの選定
UnityYAMLの読み込みにはYamlDotNetを採用しました。これはYamlDotNetが.Net界隈で最もポピュラーなyamlパーサの一つであり、また、 `com.unity.visualscripting` にも取り込まれていて、Unity公式のUnity Registry内で完結することもできるためです。
ただし、`com.unity.visualscripting`を使いたくないよってこともあり得るので、本家の`yamldotnet`にも対応してみました。
Assembly DefinitionのVersion Definesを使えば両対応は難しくありません。
![define.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5973/a994341c-3a47-16a0-5905-d5d997cf3908.png)

Version Definesで定義したDefineでusingを切り分けます。

```c#
#if AYTV_YAMLDOTNET_11_2_OR_NEWER
using YamlDotNet.RepresentationModel;
#elif AYTV_VISUALSCRIPTING_1_6_0_OR_NEWER
using Unity.VisualScripting.YamlDotNet.RepresentationModel;
#else
#error require '"yamldotnet": "11.2.1"' or '"com.unity.visualscripting": "1.6.0"'
#endif
```

https://github.com/satanabe1/asset-yaml-tree-view/blob/33ec1a0c7b9c092a8888c3699d0a6a02ed7e9c28/Editor/UnityAssetYamlParser.cs#L5-L11

### 読み込み方
UnityYAMLを標準的なyamlパーサに流し込むと、ファイル先頭のヘッダーライン(`%TAG !u! ...`など)や、オブジェクトのヘッダー(`--- !u!1 &1865014687417162`など)の存在により、うまく読み込めません。
そこでアセットファイルを `--- !u!` の文字列で分割し、それぞれを独立したyamlとしてパーサに流し込むことで乗り越えます。 [参考実装](https://github.com/satanabe1/asset-yaml-tree-view/blob/33ec1a0c7b9c092a8888c3699d0a6a02ed7e9c28/Editor/UnityAssetYamlParser.cs#L36-L68)
`--- !u!` の行にも重要な情報があるので、捨てずにYamlDocumentとセットで取得しておくのがよさそうです。

## TreeView表示
[IMGUIのTreeView](https://docs.unity3d.com/Manual/TreeViewAPI.html)を利用してみました。(今風にするのであればUIToolKitを使うべきだったかもしれません・・・。)
基本的には[この辺の処理](https://github.com/satanabe1/asset-yaml-tree-view/blob/33ec1a0c7b9c092a8888c3699d0a6a02ed7e9c28/Editor/AssetYamlTreeUtil.cs#L63-L115)で、YamlDocumentのNodeの要素と、ほぼ１体１で[AssetYamlTreeElement](https://github.com/satanabe1/asset-yaml-tree-view/blob/33ec1a0c7b9c092a8888c3699d0a6a02ed7e9c28/Editor/AssetYamlTreeElement.cs#L16)からなる木構造に変換しました。guidなどに関しては若干のオリジナリティを加えましたが、ほとんど意味はありません。
安直にTreeViewでyamlを表示すると以下のようになります。
![スクリーンショット 2022-11-28 19.13.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5973/b5f157ae-736e-dd13-0ca4-6cfd1735e99b.png)
何もわかりやすくなってないですね。

## ClassIdの解決
UnityYAMLのよくわからなさを助長している`!u!XXXX`の部分をまともなクラス名に変えてみます。
`!u!XXXX`の数字の部分`XXXX`は解説ではクラスIDと呼ばれています。クラスIDとクラス名の関係は [こちらのドキュメント](https://docs.unity3d.com/2021.3/Documentation/Manual/ClassIDReference.html) で紹介されています。
![スクリーンショット 2022-11-29 9.57.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5973/a1157636-7165-6d9b-a4e5-83d98c704233.png)
とは言え、これを一つ一つハードコードしていくのも微妙なので、Unityの内部から拾ってきます。
Unity内では [UnityType.FindTypeByPersistentTypeID(int id)](https://github.com/Unity-Technologies/UnityCsReference/blob/280b05a874443464be435c70f26644d1703e272a/Editor/Mono/TypeSystem/UnityType.cs#L59-L64) というメソッドを通してクラスIDから型情報を取得できるようになっています。ただしUnityTypeはpublicクラスではないのでリフレクションが必要になります。
[こんな感じ](https://github.com/satanabe1/asset-yaml-tree-view/blob/c124aee327882202b7c93c87b98acf7b8b48962e/Editor/AssetYamlTreeUtil.cs#L203-L216)でクラスIDからクラス名を取得するメソッドを実装しました。

```c#
private static string GetTypeNameByPersistentTypeID(int id) {
    const BindingFlags flags = BindingFlags.Public
                               | BindingFlags.Static
                               | BindingFlags.Instance
                               | BindingFlags.InvokeMethod
                               | BindingFlags.GetProperty;
    var assembly = Assembly.GetAssembly(typeof(MonoScript));
    var unityType = assembly.GetType("UnityEditor.UnityType");
    var findTypeByPersistentTypeID = unityType.GetMethod("FindTypeByPersistentTypeID", flags);
    var nameProperty = unityType.GetProperty("name", flags);
    var typeInstance = findTypeByPersistentTypeID?.Invoke(null, new object[] { id });
    return typeInstance != null ? nameProperty?.GetValue(typeInstance) as string : null;
}
```

あとは取得したクラス名と`!u!XXXX`の部分を置換してやれば、以下のように、人間にも優しい表示になります。
![スクリーンショット 2022-11-29 10.08.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5973/fb572bf3-e3e0-aae2-ed64-028202e1c0d6.png)

## Iconの適用
文字列だけではやっぱり直感的ではないのでIconも表示してみます。イイカンジのIconの取得というのも結構厄介で、いろんなメソッドを組み合わせることになります。(もっとシンプルに一発で取得する方法、募集中です)



使用したAPIは以下の通りです。
- [AssetDatabase.GetCachedIcon(string path)](https://github.com/Unity-Technologies/UnityCsReference/blob/280b05a874443464be435c70f26644d1703e272a/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs#L481)
  - public
  - prefabやmaterialなどのファイルパスに応じたtextureが返ってくる
  - 固有のファイルパスを持つアセットに使える
- [EditorGUIUtility.FindTexture(string name)](https://github.com/Unity-Technologies/UnityCsReference/blob/280b05a874443464be435c70f26644d1703e272a/Editor/Mono/EditorGUIUtility.cs#L506) & [EditorResources](https://github.com/Unity-Technologies/UnityCsReference/blob/280b05a874443464be435c70f26644d1703e272a/Editor/Mono/EditorResources.bindings.cs#L12-L27)
  - public
  - テクスチャ名(`Folder Icon` `Assembly Icon` etc...)に応じたtextureが返ってくる
  - 名前を知っておく必要がある(よく使うものはEditorResourcesに定義されていたりする)
- [InternalEditorUtility.FindIconForFile(string fileName)](https://github.com/Unity-Technologies/UnityCsReference/blob/280b05a874443464be435c70f26644d1703e272a/Editor/Mono/InternalEditorUtility.cs#L24)
  - public (でもInternalプレフィックス？)
  - fileNameに含まれる拡張子に応じたtextureが返ってくる
- [AssetPreview.GetMiniTypeThumbnail(Type type)](https://github.com/Unity-Technologies/UnityCsReference/blob/d3aaa4d6a73002d216e8fdfda689a55b7c832442/Editor/Mono/AssetDatabase/AssetPreview.bindings.cs#L90)
  - public
  - System.Typeに応じたtextureが返ってくる
  - 眺めているとちょっと楽しい
- [AssetPreview.GetAssetPreviewFromGUID(string guid)](https://github.com/Unity-Technologies/UnityCsReference/blob/d3aaa4d6a73002d216e8fdfda689a55b7c832442/Editor/Mono/AssetDatabase/AssetPreview.bindings.cs#L36)
  - internal
  - GUIDに応じたtextureが返ってくる
  - GUIDは取得できるけどファイルパスが返ってこないパターンに使える？
- [AssetPreview.GetMiniTypeThumbnailFromClassID(int classId)](https://github.com/Unity-Technologies/UnityCsReference/blob/d3aaa4d6a73002d216e8fdfda689a55b7c832442/Editor/Mono/AssetDatabase/AssetPreview.bindings.cs#L99)
  - internal
  - クラスIDに応じたTransform等その他組み込みコンポーネントのtextureが返ってくる

どれも微妙にできることとできないことが違うので、UnityYAML中のクラスIDやGUIDに応じたIconをサポートしようとすると、こんなことに・・・
https://github.com/satanabe1/asset-yaml-tree-view/blob/c124aee327882202b7c93c87b98acf7b8b48962e/Editor/AssetYamlTreeUtil.cs#L117-L146

アドカレネタに突貫で書いたので不要なところもありそうですが、これで取得できたIconを表示してみると以下のようになります。

![スクリーンショット 2022-11-29 10.50.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5973/cfd73e80-c119-f0b0-39f6-01d048c1a2e2.png)

結構悪くない感じになってきました。

## GUIDの解決
![スクリーンショット 2022-11-29 10.56.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5973/4c10a630-830a-08af-7696-291ca3149d6a.png)
guidのところの情報が重要な割に、人間にはﾅﾆﾓﾜｶﾗﾅｲ所なので、可能な限りファイルパスに置き換えてみます。
これは何も難しい話はなくて、guidというYamlNodeの配下にあるYamlScalerNodeの値を取得して、AssetDatabaseに問い合わせるだけです。
![スクリーンショット 2022-11-29 13.19.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5973/524a0571-e40d-3e9e-a9ad-71db585bc312.png)

ついでで、ダブルクリックでPrefabやScriptを開けるようにしてあげるとより親切でしょう。

```c#
// override TreeView.DoubleClickedItem
protected override void DoubleClickedItem(int id)
{
    base.DoubleClickedItem(id);
    var first = FindRows(new List<int> { id }).FirstOrDefault() as AssetYamlTreeViewItem;
    if (first?.Data?.AssetPath == null) return;
    AssetDatabase.OpenAsset(AssetDatabase.LoadMainAssetAtPath(first.Data.AssetPath));
}
```

## FileIDの解決(未実装)
FileIDを使って、親子関係やComponentのアタッチ対象なども解決してあげると実用的なツールに近づきそう、という展望はありますが、今回は実装しませんでした。いつかモチベーションが上がったら・・・
![スクリーンショット 2022-11-29 13.25.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/5973/c510a916-2a20-b6a6-c573-adbf5efe1b7a.png)

# 終わりに
ツールを使えば僕でもUnityYAMLの雰囲気を感じ取ることができるようになりました。
ただ、Unity上でしか表示できないっていうのは、本来僕が求めているところじゃなかったりします。
Chrome Extension化してGithub PullRequestのところで同様の可視化ができたらいいのにな、とか夢は広がりますが、それは今後の課題ということで・・・

以上です。