問題
做Unity開發時,經常需要寫一些Editor代碼,用來提高開發的效率,常見的一種情況就是通過代碼修改場景里Prefab的參數。一般修改后會發現一切如期望般正常,但一旦你重啟Unity,或者重新加載Scene,那么就會驚喜地發現,之前做出的修改都沒有生效。
由於我習慣使用新版的Unity,所以下面都是基於Unity 2019版本,其他版本是否會有這個問題,能不能用這種方式解決,就各自嘗試了。
原因
假設我們有一個簡單的腳本,如下
using UnityEngine; public class Test : MonoBehaviour { public int Value; }
我們把它掛接在一個空的GameObject下,做成一個Prefab,如下:
如果我們直接在Editor下修改Value的值,如下圖,可以看到Value左邊有一條藍色的長條,同時Value和3都變成了粗體。(細心的讀者還會注意到,Unity的標題欄最后會多了個表示Scene修改的*)
這是因為我們不是直接修改Prefab,而是在Prefab上做了一個Override。關於Override,可以理解為是一個盒子,盒子里面是它對應的Prefab,而我們的修改是修改了Override,而沒有直接修改到Prfab,所以不會影響其他同一個Prefab的值。當Unity要獲取Prefab的參數時,會優先找這個參數有沒有被Override,有的話就用Override的值,沒有則用Prefab的值。
注意就算我們把Value的值改為0,Override還是生效了,可以通過Ctrl+z撤銷之前的操作,可以看到Override消失了。
我們寫一個簡單的Editor代碼來實驗下通過代碼直接修改Value的值,如下:
[CustomEditor(typeof(Test))] public class TestEditor : Editor { public override void OnInspectorGUI() { DrawDefaultInspector(); var test = target as Test; if (GUILayout.Button("Change")) { test.Value = UnityEngine.Random.Range(1, 100); }}}
這時候如果你點擊Change按鈕,會發現Value的值確實修改了,但沒有了表示Overide的藍條,文字沒有變成粗體(標題欄的*號也沒了),而且你按Ctrl+z是無法撤銷這次修改的。更糟糕的是,你重啟Unity后會發現值並沒有生效。
這是因為Unity並不知道我們做出了修改,所以沒有幫我們保存這次修改。
解決
解決方法有幾種,但最推薦的是采用Undo,如下:
[CustomEditor(typeof(Test))] public class TestEditor : Editor { public override void OnInspectorGUI() { DrawDefaultInspector(); var test = target as Test; if (GUILayout.Button("Change")) { Undo.RecordObject(test, "modify test value"); test.Value = UnityEngine.Random.Range(1, 100); EditorUtility.SetDirty(test); }}}
再次點擊Change按鈕,會發現和直接在Editor修改的結果是一致的。Ctrl+z也能生效。那么Undo.RecordObject的第二個參數是干嘛的?你打開菜單欄的Edit菜單會有驚喜。
友情提示,如果是一次修改大量的Prefab,可以采用Undo.RecordObjects。
友情提示2,Undo.RecordObject有時會失效,這個函數好像不太可靠,更可靠的方法是在最后加上EditorUtility.SetDirty()。