我的環境配置:Windows 10、Unity 5.3.4f1。
 
 
在 Unity 中,可以將場景中的 GameObejct 製作成 Prefab,以便之後使用。
 
若有在 GameObject 中進行修改的話,也可以按下 Apply 按鈕,將修改過後的 GameObject 覆蓋掉舊的 Prefab
image01.jpg
 
 
但在 Unity 運作中時,想要針對調整好數值的 GameObject 執行 Apply Prefab 時,應該怎麼做呢?因為 Apply 按鈕消失了。
 
這邊可以使用 GameObject > Apply Changes To Prefab
image02.jpg
 
 
或者也可以自行撰寫腳本,來執行 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 資料夾內即可使用,若無該資料夾,可以直接建立一個
image03.jpg
 
 
使用方法為,直接選取需要 Replace 的 GameObject
image04.jpg
 
 
接著執行 MyTools > Replace > ConnectToPrefab 或任一選項即可
image05.jpg
 
 
這邊需要注意!不可以將需要 Replace 的 GameObject 放在另一個需要 Replace 的 GameObject 底下!
以下是錯誤的!因為 GO_1、GO_2、GO_3 都是擁有各自的 Prefab,若是這樣選取並 Replace 的話,會造成不可預期的錯誤!
image06.jpg
上面圖片可以看到,我把 GO_3 掛到 GO_2 底下,再把 GO_2 掛到 GO_1 底下,接著我全選了所有物件,執行 Replace。但系統卻是先執行 GO_2 的 Replace。之後才執行 GO_1,這導致 GO_2 的 Prefab 被修改了!
 
所以說,若真有這需求的話,請直接點選 GO1 執行 Replace 就好,避免出現非預期的情況發生。
 
我想 Unity 官方沒有提供多個 GameObject 同時 Replace 的功能,應該也是基於這個原因吧?!
 
 
arrow
arrow

    岳 發表在 痞客邦 留言(0) 人氣()