PrefabUtility 使えば楽勝やろ(鼻ホジー)と思ってたらハマった。
さてどうやろう
普通に考えれば以下3つが必要。
ここで 2 を 1を元にスクリプトで取得できないか と考えたのがハマりのもとだった。
PrefabUtility にそれっぽいのたくさんあるから...
PlayMode中にシーン内のインスタンスの元プレハブを取得する
結論から言うと できない ぽい。
まず、エディタスクリプト内でこれをやりたい場合は PrefabUtility#GetCorrespondingObjectFromSource を使う。
※前は PrefabUtility#GetPrefabParent だったが移行したようだ
プレイモードに入らずこの関数をシーン内のオブジェクトに対して使えば、ちゃんと元プレハブのパスが取得できる。
が、プレイモードに入った途端に、関数の返す値が null になる。
プレイモード中にGameObject#Instantiate したオブジェクトに対しても、同様にプレハブが取得できない。
何時間か試したが、どうしても取得することができなかった。
PlayMode中のプレハブ関係
どうもプレイモードに入った途端に 既存のプレハブ関係が破棄されているのではないか 。
まず再生をせずにシーンに配置されているプレハブのインスタンスをインスペクタで見ると、
プレハブ関係をたどるボタンが表示されている。
次にプレイモードで見る。
ボタンが消えてしまっている 。
そしてこのインスタンスをプロジェクトビューにドラッグし、プレハブを作成してみる。
ボタンは復活したがクリックできない 。
うーむ、謎。
と、ここまで試した段階で、PlayModeで元プレハブを辿るのはあきらめた。
解 : SaveAsPrefabAsset
今回やりたいことは、プレイモードに入る前にインスタントと元プレハブを一対一で取得しておき、PrefabUtility#SaveAsPrefabAssetでプレハブをまるごと置き換えてしまう ことで解決した。プレハブの更新はできるがシーン内の参照がこわれるorz。おそらくシーン内の状態を保存できれば解決するが...
PrefabUtility.ApplyPropertyOverride も試したがよくわからなかった。
詳細は省略するが、エディタスクリプト内で以下のようなかんじでやる。
GameObject instance; // シーン内のインスタンス GameObject prefab; // プロジェクトビュー内のプレハブ // 元アセットのパスを取得 var prefabPath = PrefabUtility.GetPrefabAssetPathOfNearestInstanceRoot(pref); // 上書き PrefabUtility.SaveAsPrefabAsset( instance, prefabPath );
これならプレイモード中でもプレハブの内容を上書きし、他シーンからのプレハブ参照も壊れない。
シーンの更新ができるまで宿題
マニュアル読み返そう...