關於 web service, unity, blogger 等軟體工程筆記

Unity Editor: Apply multiple prefabs

Edit icon 沒有留言
Unity

這是一段在 Unity Editor 中小工具 (test on Unity5.3.5),一次操作對在場景中每一個選取的 Prefab objects,執行 Apply 將修改寫入到 Prefab 資料中。

文章結構先說結論,先介紹程式碼以及已知議題 (Issues),再說明為什麼有這段程式碼,以及這段程式碼如何被設計出來。

結論

將以下程式碼放置在專案中的 Editor 資料夾,便可以在 MenuItems > Assets > Apply Prefabs(s) 找到其操作執行。

MenuItems > Assets > Apply Prefabs(s)

// Assets/Editor/PrefabsMenuItems.cs
using UnityEditor;
using UnityEditor.SceneManagement;

public class PrefabsMenuItems
{
[MenuItem("Assets/Apply Prefabs(s)", false, 10000)]
static void ApplyPrefabs()
{
var selections = Selection.gameObjects;
foreach (var go in selections)
{
var prefabType = PrefabUtility.GetPrefabType(go);
if (prefabType == PrefabType.PrefabInstance ||
prefabType == PrefabType.DisconnectedPrefabInstance)
{
var goRoot = PrefabUtility.FindValidUploadPrefabInstanceRoot(go);
var prefabParent = PrefabUtility.GetPrefabParent(goRoot);
var option = ReplacePrefabOptions.ConnectToPrefab;

PrefabUtility.ReplacePrefab(goRoot, prefabParent, option);
EditorSceneManager.MarkSceneDirty(goRoot.scene);
}
}
}

[MenuItem("Assets/Apply Prefabs(s)", true, 10000)]
static bool ApplyPrefabs_Validate()
{
if (AnimationMode.InAnimationMode())
{
return false;
}

return Selection.gameObjects.Length > 1;
}
}
  • Line7: 建立 MenuItem,只有 static function 才可正常作用
  • Line10: 目前選取的 GameObjects 物件 (Hierarchy)
  • Line13: 取得 Prefab 類型,PrefabType
  • Line14-15: 只有由 Prefab 產生的實體,或是由 Prefab 產生的實體但是中斷連結才會執行 Apply
  • Line17: 取得選取物件的實體 Prefab root
  • Line18: 取得真正的 Prefab 物件 (in Project)
  • Line21: Apply
  • Line22: 標示場景 dirty,讓 UnityEditor 在下次更新時更新該場景
  • Line27: MenuItem 的 Validate function,當回傳 false 表示禁用該菜單選項 (e.g. 不能點選該選項)
  • Line30: 如果現在正在編輯 Animation,不可點選此選項
  • Line35: 如果沒有任何 GameObjects,不可點選此選項
  • Others: Validate function 為了編輯效能,不檢查是否有選取 Prefab instances

已知議題

  • 當選取多個 Prefab instances,且該些 instances 都是使用相同 Prefab 時,apply 順序可能會造成問題,例如下面的示意圖。

    Prefab A 建立兩個實體

    兩個使用 Prefab A 建立實體,修改成 A' 以及 A'',執行程式進行 Apply,那是要更新 A' 還是更新成 A''?

為什麼會有這段程式碼

社團有人分享,如何快速 apply 場景上多個 prefabs,注意到該程式碼採用 PrefabUtility.CreateEmptyPrefab 來建立 prefab,檔案路徑也是寫死在程式碼中,而非抓取該物件的原始 prefab 並將資料寫入,因此工程師之魂小爆發,想要改善該段程式碼。

記得之也有專案也有做過類似的事情,藉由之前的經驗以及 Unity Editor 的原始碼,在短時間內建立以上的程式碼。

如何實作這段程式碼

這次有部分抄 Unity 的官方程式碼,那段在 Unity Editor Inspector 介面中的 Apply 按鈕程式碼,若選擇物件為 prefab instance 時,才會出現的按鈕,將其物件的參數寫入其 prefab。

那怎麼在 Windows 作業系統上撈取 Unity Editor 的程式碼,先找到 UnityEditor.dll 的檔案位置,使用工具 ILSpy 反彙編 (Disassembler) 程式碼,該工具可以列出該 CLR Dll 內部所有 class, members, functions 等等,由中尋找那段 prefab apply 的程式碼。

以下使用 ILSpy version 2.4.0.1963,示範兩種方式搜尋。

第一種,猜測 Prefab 操作都是使用 PrefabUtility 這 class 進行操作,因此使用 Types And Members 模式先找到 PrefabUtility,然後滑鼠右鍵點選 Analyze,查看 Used By 檢查有哪些函數有使用該 class。從中由經驗覺得可疑的 GameObjectInspector::DrawInspector,查看該函數程式碼,搜尋 Apply 找到該段程式碼。

由 PrefabUtility Used by 查詢

第二種,則是使用新版本的 Constant 模式來搜尋,直接搜尋存在字串 Apply,看看哪邊有使用到 Apply。相同的,從搜尋結果中嗅出 GameObjectInspector::DrawInspector 最有可能,查看該函數便可找到。

由 Const Apply 查詢

最後,看過 Unity Editor 如何進行 prefab apply,稍微修改便可以完成。

沒有留言: