解决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()。

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