当编辑器重新编译时,防止在dll中丢失EditorWindow的数据

时间:2018-06-11 14:57:47

标签: c# unity3d

我正在制作自定义的EditorWindow。非常简化的版本如下所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEditor;
using UnityEngine;

namespace testDllForUnity {
    [Serializable]
    public class WindowTestDllForUnity : EditorWindow {
        string myString = "Hello World";
        private static float x = 0;
        private static float y = 0;        
        private static int yOffset = 10;
        private static float width = 100f;
        private static float height = 40f;

        private static List<Rect> buttonsRectList = new List<Rect>();

        [MenuItem("Window/WindowTestDllForUnity")]
        static void Init() {            
            WindowTestDllForUnity window = (WindowTestDllForUnity)EditorWindow.GetWindow(typeof(WindowTestDllForUnity));
            buttonsRectList.Add(new Rect(x, y, width, height));

            window.Show();
        }


        void OnGUI() {
            GUILayout.Label(myString, EditorStyles.boldLabel);
            if (GUILayout.Button("AddButton")) {
                y += height + yOffset;                
                buttonsRectList.Add(new Rect(x, y, width, height));
            }

            for (int i = 0; i < buttonsRectList.Count; i++) {
                if (GUI.Button(new Rect(buttonsRectList[i]), "button_" + i))
                    Debug.Log("button_" + i + " clicked!");                
            }
        }
    }
}

实际上,我的窗口比这更复杂。

这段代码我用VisualStudio构建为dll,并放入Unity&#34;资产/编辑/&#34;文件夹中。

它工作正常,但是......如果我在Unity中添加任何C#脚本,写任何内容并保存它 - Unity启动他的自动编译,我的自定义窗口变空,只是清除窗口

我应该怎么做以防止我的窗户清理?是否有可能要求Unity在编译其他脚本时不重建窗口?或者我应该每次都保存数据并在重新编译后恢复它?这不是一个坏方法吗?

1 个答案:

答案 0 :(得分:0)

在Unity中添加,更改或删除脚本(或DLL)时,将重新编译并重新加载所有受影响的程序集。当您进入和退出播放模式时,也会重新加载程序集。这会导致清除所有静态变量,因为它们属于已加载的程序集,并且不会保存在其他任何位置。

要通过程序集重新加载来保持更改,Unity需要能够将数据(您的成员变量)保存到磁盘,然后在重建所有内容后重新填充它们。幸运的是,在很多情况下,这是自动完成的。 EditorWindow基类已标记为[Serializable],因此无需标记我们的子类。接下来,所有需要保存的变量也必须是可序列化的。像string和int这样的简单数据类型是可序列化的,但是某些类型如Rect不是,这就是你的列表不能保存的原因,即使它是实例的一部分(不是静态的)。请参阅Unity Manual - Script Serialization。请注意,EditorWindow类的规则似乎与MonoBehaviour类的序列化略有不同。例如,私有变量是序列化的,没有[SerializeField]属性,至少在我的Unity 2018.1.0f2版本中。

尝试以下代码以便更好地理解:

using UnityEditor;
using UnityEngine;
using System.Collections.Generic;

namespace PersistChangesThroughAssemblyLoad
{
    public class MyEditorWindow : EditorWindow
    {
        Vector2 buttonStartPosition = new Vector2(0, 0);
        Vector2 buttonStartSize = new Vector2(100f, 40f);
        int yOffset = 10;

        // All data types which should be peristent, need to be
        // serializable, Vector2 is, but Rect is not.
        List<Vector2> buttonPositions = new List<Vector2>();
        List<Vector2> buttonSizes = new List<Vector2>();

        [MenuItem("Window/MyEditorWindow")]
        static void Init()
        {
            MyEditorWindow window = GetWindow<MyEditorWindow>("MyWindow");
            LogMessage(window, "Window will be opened via the menu item.");
            window.Show();
        }

        void OnEnable()
        {
            LogMessage("Window is enabled (either after opening or after assembly reload/recompile).");
        }

        void OnDisable()
        {
            LogMessage("Window is disabled (either when being closed or because the assembly is about to reload/recompile).");
        }

        void OnGUI()
        {
            if (GUILayout.Button("Add Button"))
            {
                buttonStartPosition.y += buttonStartSize.y + yOffset;
                AddNewButton(buttonStartPosition, buttonStartSize);
            }

            for (int i = 0; i < buttonPositions.Count; i++)
            {
                string buttonName = "Button " + i;
                if (GUI.Button(new Rect(buttonPositions[i], buttonSizes[i]), buttonName))
                {
                    LogMessage(buttonName + " was clicked!");
                }
            }
        }

        void AddNewButton(Vector2 position, Vector2 size)
        {
            buttonPositions.Add(position);
            buttonSizes.Add(size);
            LogMessage("Added new button. Total count: " + buttonPositions.Count);
        }

        void LogMessage(string message)
        {
            LogMessage(this, message);
        }

        static void LogMessage(Object context, string message)
        {
            Debug.Log("Window [" + context.GetInstanceID() + "]: " + message);
        }
    }
}

注意控制台中记录的实例ID。从菜单创建新窗口时,ID会更改,但如果窗口是可序列的,则它会保持播放模式。这种方法应该可以帮到您,因为大多数数据可以分解为简单的数据类型。创建自定义结构并使用[Serializable]属性标记它们也很方便:

[System.Serializable]
public struct MyRect
{
    public float x, y, width, height;
}

除此之外,可以在OnDisable中将数据写入EditorPrefs并将其加载到OnEnable中。