動機
Scene内オブジェクトの参照をシリアライズしたい、ということが稀にある。
🦀ではScene内の光源を点灯→ライトマップをベイク→光源を消灯、という一連のタスクを複数回実行する必要があるんだけど、 Bakeryはベイク時にベイク対象のSceneをアンロードしてしまうので、UnityEngine.ObjectはDestroyされて参照がnullになってしまう。 それで、ベイク後にObjectを再度取得する必要があり、参照をシリアライズしたい、ということになる。
真っ先に思いつくのはUnityEngine.Object.GetInstanceId()
なんだけど、この値は可変なのでシリアライズには向いてない。
インスペクターをDebugモードにしたり、SceneアセットのYAMLを見ると、local file IDという概念があるのがわかるけど、これを生で取るのはこれまではReflectionを使う必要があった・・・
が、Unity2019くらいから GlobalObjectId というAPIが用意されていて、これを使うとシュッと実装できる。最高!
自分は https://github.com/NewBloodInteractive/com.newblood.lighting-internals を眺めていてこのAPIを知った。
ちなみに、GlobalObjectIdはUnityEditor名前空間なのでランタイムでは使えなくて、ランタイムでやりたい場合はヒエラルキーのパス + Componentの型で一応参照を保持することは可能で、 🦀v2ではこの方法でBakeryをサポートしてた(ObjectReference.cs)。
以下のような制約を飲める場合はこれでも許容できるケースもあるかもしれない:
* ヒエラルキー上のパスでGameObjectにuniqueに決まらないと駄目
* GetComponent
GlobalObjectId
ドキュメントを読めばわかるのであんまり詳細に書かないけど、中身は以下のようになっていて、4つのパラメータで1つのIDを構成してる: * assetGUID * identifierType: * targetObjectId: local file idのこと * targetPrefabId
ToString()
すると"GlobalObjectId_V1-{identifierType}-{assetGUID}-{targetObjectId}-{targetPrefabId}"
になっていて、逆にこれからGlobalObjectId.TryParse
で復元もできるので、文字列で保持するのでもよさそう。
Objectの取得はGlobalObjectId.GlobalObjectIdentifierToObjectSlow を使うだけ。
Sceneのコピー
🦀v3ではユーザーが作ったシーンに破壊的変更をあまり加えたくないので、ライトマップをベイクするときにSceneを複製して、複製したSceneに対してライトマップを焼くようにした。
(BakeryのstaticベイクはftLightmapStorage
のコピーが面倒すぎてやってないけど、やる意志は一応ある。 https://github.com/shivaduke28/kanikama/issues/166)
手続きとしては
- Scene内のObjectのGlobalObjectIdを取得して、assetGUID以外の値を控えておく。
- Sceneのコピーを作って開く。
- 上で控えていたやつにコピー先のSceneアセットのGUIDを加えてGlobalObjetIdを取得する
ということをやってる(Prefabが混ざるとさらに面倒そうなのでPrefabは非許可ということにしてる)。
nullだったらGlobalObjectIdentifierToObjectSlow
を呼んで~みたいなのが面倒なので、ラッパーみたいなのを用意しておくと便利だった。
余談
🦀でSceneのstaticなライトマップを焼くときにやることは
- Sceneをコピーする
- dynamic(= 🦀で動かす)光源を全部消灯する
- ライトマップを焼く
- 生成されたLightingDataAssetをオリジナルのSceneに参照させる
ということをやっているんだけど、4をコードから実行する方法が用意されていなくて、では.unity
ファイルを書き換えるか!と思ったんだけど
- https://github.com/aaubry/YamlDotNet はVCCのresolverが含んでいる影響で、VRC向けのUnityプロジェクトだとdllの衝突が起きたりして面倒くさい
- https://github.com/hadashiA/VYaml がかなり良さそうだけどUnity2019が非サポート
という感じで、どうにも面倒なので、生で書き換える汚いコードを書いた。南無三・・・ YAMLファイルが複数種類の改行コードを含んでいると壊れそうなので、何とかした方が良さそうではある。