解決Unity下通過代碼修改prefab的參數不生效的問題

解決Unity下通過代碼修改prefab的參數不生效的問題

問題

做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()。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章