close
我的環境配置:Windows 10、Unity 5.3.4f1。
在 Unity 中,可以將場景中的 GameObejct 製作成 Prefab,以便之後使用。
若有在 GameObject 中進行修改的話,也可以按下 Apply 按鈕,將修改過後的 GameObject 覆蓋掉舊的 Prefab

但在 Unity 運作中時,想要針對調整好數值的 GameObject 執行 Apply Prefab 時,應該怎麼做呢?因為 Apply 按鈕消失了。
這邊可以使用 GameObject > Apply Changes To Prefab

或者也可以自行撰寫腳本,來執行 Replace Prefab,自行撰寫腳本有個好處,就是可以同時 Replace 多份 Prefab,但這也是有缺點的,所以必須小心使用!
以下為 Replace Prefab 的腳本,該腳本可以選取多個 GameObject 同時進行 Replace 的動作
using UnityEngine; using UnityEditor; using System.Collections.Generic; /// <summary> /// 該工具是用來協助 Replace Prefab 的, /// 因為該工具引用到 UnityEditor, /// 所以必須存放在 Editor 資料夾內. /// </summary> public class AutoReplacePrefab { [MenuItem( "MyTools/ReplacePrefab/ConnectToPrefab" )] private static void ReplacePrefab_ConnectToPrefab() { // 該陣列是存放已經執行過 Replace 的 GameObject, // 目的是避免同一個的 GameObject 執行了多次 Replace, // 造成系統資源的浪費. List<GameObject> listGameObject = new List<GameObject>(); // 開始執行 Replace, 這邊程式也會自動判斷是否重複 Replace 同一個物件, // 是的話則會跳開, 避免資源浪費. for ( int i = 0; i < Selection.objects.Length; i++ ) { // 找 GameObject 最在場景中的源頭父層, // 這邊其實也可以使用 PrefabUtility.FindPrefabRoot 去得取物件的源頭, // 但 PrefabUtility.FindPrefabRoot 有時候會因為物件與父層的連結斷開, // 導致搜尋結果是錯誤的, 所以這邊我們自己手動搜尋. GameObject gobjRoot = Selection.objects[i] as GameObject; while ( true ) { GameObject gobj = PrefabUtility.GetPrefabParent( gobjRoot ) as GameObject; if ( gobj != null && gobj == PrefabUtility.FindPrefabRoot(gobj) ) break; if ( gobjRoot.transform.parent == null ) break; gobjRoot = gobjRoot.transform.parent.gameObject; } // 檢查該 GameObject 是否存在紀錄中, 若回傳值不為 -1, // 則表示該 GameObject 已經執行過 Replace 了, // 所以跳過該次迴圈, 不重複 Replace. if ( listGameObject.FindIndex(d=>d.GetHashCode() == gobjRoot.GetHashCode()) != -1 ) continue; // 若該物件無 Prefab 或者 Prefab Root 不正確的話, // 也跳出迴圈, 不繼續執行. GameObject target = PrefabUtility.GetPrefabParent( gobjRoot ) as GameObject; if ( target == null || (target != PrefabUtility.FindPrefabRoot(target)) ) continue; // 紀錄已經執行過 Replace 的 GameObjec listGameObject.Add( gobjRoot ); // 開始覆蓋 Prefab PrefabUtility.ReplacePrefab( gobjRoot, target, ReplacePrefabOptions.ConnectToPrefab ); // 以下 Log 是可以關閉的 Debug.Log( string.Format( "{0} 使用 {1} 的方式覆寫了 {2}", gobjRoot.name, ReplacePrefabOptions.ConnectToPrefab.ToString(), target.name ) ); Debug.Log( string.Format( "GameObject {0} 位置", gobjRoot.name ), gobjRoot ); Debug.Log( string.Format( "Prefab {0} 位置", target.name ), target ); Debug.Log( "" ); } } }
ReplacePrefabOptions 參數說明
ReplacePrefabOptions.Default:意思等同於 ReplacePrefabOptions.ReplaceNameBased。
ReplacePrefabOptions.ReplaceNameBased:意思為,將 GameObject 有修改的部分,直接在指定的 Prefab 裡面執行添加或移除,因為是使用直接添加或移除的方式,所以 GameObject 粗體字的數值部分不受 Prefab 影響。
ReplacePrefabOptions.ConnectToPrefab:意思等同於 Unity 的 Apply 按鈕,一樣是將 GameObject 與 Prefab 做同步,因為是使用同步的方式,所以 GameObject 的數值就會受 Prefab 影響了,大家會發現粗體字的數值部分都沒了。
PS:GameObject 的數值是否會受 Prefab 影響,這邊可以藉由該數值是否為粗體字來得知。詳細介紹可觀看 "GameObject 與 Prefab 的關聯性"。
PS:因為 Default 與 ReplaceNameBased 的用法較為特殊,所以這邊建議使用 ConnectToPrefab 即可。
PS:因為該工具引用到 UnityEditor,所以必須存放在 Editor 資料夾內。
PS:以上為我的測試結果,如有錯誤歡迎指正,謝謝。
下面有提供更完整的 AutoReplacePrefab.cs 腳本下載,下載腳本後,放在 Editor 資料夾內即可使用,若無該資料夾,可以直接建立一個

使用方法為,直接選取需要 Replace 的 GameObject

接著執行 MyTools > Replace > ConnectToPrefab 或任一選項即可

這邊需要注意!不可以將需要 Replace 的 GameObject 放在另一個需要 Replace 的 GameObject 底下!
以下是錯誤的!因為 GO_1、GO_2、GO_3 都是擁有各自的 Prefab,若是這樣選取並 Replace 的話,會造成不可預期的錯誤!

上面圖片可以看到,我把 GO_3 掛到 GO_2 底下,再把 GO_2 掛到 GO_1 底下,接著我全選了所有物件,執行 Replace。但系統卻是先執行 GO_2 的 Replace。之後才執行 GO_1,這導致 GO_2 的 Prefab 被修改了!
所以說,若真有這需求的話,請直接點選 GO1 執行 Replace 就好,避免出現非預期的情況發生。
我想 Unity 官方沒有提供多個 GameObject 同時 Replace 的功能,應該也是基於這個原因吧?!
文章標籤
全站熱搜