PrefabUtilityに定義されているメソッドのうち、下の記事で適当に流してしまった
GetCorrespondingObjectFromSource()
とGetCorrespondingObjectFromOriginalSource()
について改めて調べました。
これら二つのメソッドの違いと、おかしな挙動を発見したのでまとめます。
Unity2018.3.12
二つのメソッドの違い
いま、prefab_0という名前のPrefabがあるとします。
このprefab_0からバリアントprefab_1を派生させます。
さらに、prefab_1からバリアントprefab_2を派生させます。
これらを図でまとめると次のような派生関係になります。
ここで、prefab_2に対してGetCorrespondingObjectFromSource()
を使うと一つ上のprefab_1が取得できます。
一方、prefab_2に対してGetCorrespondingObjectFromOriginalSource()
を使うとprefab_0が取れます。
つまり、PrefabのSourceとは位置階層上の派生元のPrefabを指し、OriginalSourceとは大元のPrefabを指すようです。
実装はC# Referenceに載ってる、けど・・・
このあたりのソースコードは公開されているのでC# Referenceを見るとわかります。
実装は単純で、GetCorrespondingObjectFromOriginalSource()
は内部的にGetCorrespondingObjectFromSource()
を何回も読んでるだけです。
private static Object GetCorrespondingObjectFromOriginalSource_Internal(Object instanceOrAsset) { var sourceObjectInPrefabAsset = instanceOrAsset; if (!EditorUtility.IsPersistent(sourceObjectInPrefabAsset)) { sourceObjectInPrefabAsset = GetCorrespondingObjectFromSource(sourceObjectInPrefabAsset); if (sourceObjectInPrefabAsset == null) return null; } while (true) { var inner = GetCorrespondingObjectFromSource(sourceObjectInPrefabAsset); if (inner == null) return sourceObjectInPrefabAsset; sourceObjectInPrefabAsset = inner; } }
でもこれ最初にvar sourceObjectInPrefabAsset = instanceOrAsset;
のように自身のインスタンスを変数に入れているので、派生元が無かったら自身が返ってしまう気がします。
その場合派生先のバリアントでオーバーライドしたGameObjectやコンポーネントに対して使った時に期待する結果が得られなそうです。
試してみる
そんなわけで実際に試してみます。
まずprefab_0のバリアントprefab_1にCharacterControllerをアタッチします。Applyはしません。
この状態でCharacterControllerに対してGetCorrespondingObjectFromSource()
とGetCorrespondingObjectFromOriginalSource()
を使ってみます。
var prefab = Selection.activeObject as GameObject;
var controller = prefab.GetComponent<CharacterController>();
Debug.Log(PrefabUtility.GetCorrespondingObjectFromSource(controller));
Debug.Log(PrefabUtility.GetCorrespondingObjectFromOriginalSource(controller));
期待する結果としては、派生元にCharacterControllerがアタッチされていないため両方ともnullになりそうなものですが、
前節の理由からGetCorrespondingObjectFromOriginalSource()
の方だけ自身のインスタンスが返ってきました。
こういう仕様なのかよくわかりませんが、使うときには注意が必要そうです。
オーバーライドを取りたければGetAddedComponents()
ちなみに上記のようなケースで、例えばバリアントでオーバーライドしたコンポーネントを得たいときにはGetAddedComponents()
を使うのがよさそうです。
var prefab = Selection.activeObject as GameObject;
var overrideComponents = PrefabUtility.GetAddedComponents(prefab);
また、オーバーライドしたGameObjectを得たいときにはGetAddedGameObjects()
を使うのがよさそうです。
var prefab = Selection.activeObject as GameObject;
var overrideGameObjects = PrefabUtility.GetAddedGameObjects(prefab);