Unity 中 HashSet 的自定义检查器

Custom inspector for HashSet in Unity

我正在 Unity 中创建 RPG 游戏。最近,我设法为 HashSet 实现了自定义检查器。但是,有两个主要问题我需要帮助。

1) 在 prefab/object 和 Itemslot.cs 上对 Hashset 所做的更改有效,但只会持续到我退出 Unity。此外,当我对预制件进行更改并将其拖到场景中时,新创建的对象具有其 HashSet,而我之前对预制件所做的任何更改都没有。

2) 自从实施此功能后,当我更改 .cs 文件并尝试编译时,编辑器有时会无限期地冻结。

外观:

Itemslot.cs

public class Itemslot : MonoBehaviour, IDropHandler, IPointerEnterHandler, IPointerExitHandler
{
[System.Serializable]
public class HashSetItemType : HashSet<GlobalEnums.ItemType> { }
public HashSetItemType typeAllowed = new HashSetItemType();

[System.Serializable]
public class HashSetClassType : HashSet<GlobalEnums.GameClasses> { }
public HashSetClassType classAllowed = new HashSetClassType();

public int inventoryIndex;
public GameObject socket;

}

ItemslotEditor.cs

[CustomEditor(typeof(Itemslot))]
public class ItemslotEditor : Editor
{


public override void OnInspectorGUI()
{        
    serializedObject.Update();
    Itemslot component = (Itemslot)target;

    GUILayout.BeginVertical("box");

    EditorGUILayout.PropertyField(serializedObject.FindProperty("socket"));
    EditorGUILayout.PropertyField(serializedObject.FindProperty("inventoryIndex"));

    var style = new GUIStyle(GUI.skin.button);
    int counter = 0;


    EditorGUILayout.LabelField("Allowed Types");

    GUILayout.BeginHorizontal();

    foreach (GlobalEnums.ItemType t in Enum.GetValues(typeof(GlobalEnums.ItemType)))
    {
        if (counter % 3 == 0 && counter > 1)
        {
            GUILayout.EndHorizontal();
            GUILayout.BeginHorizontal();
        }

        if (component.typeAllowed.Contains(t))
        {
            GUI.backgroundColor = Color.green;
        }
        else
        {
            GUI.backgroundColor = Color.white;
        }

        if (GUILayout.Button(t.ToString(), style, GUILayout.Width(120), GUILayout.Height(30)))
        {
            if (component.typeAllowed.Contains(t))
            {
                component.typeAllowed.Remove(t);
            }
            else
            {
                component.typeAllowed.Add(t);
            }
        }

        counter++;

    }

    GUILayout.EndHorizontal();
    GUILayout.BeginHorizontal();

    GUI.backgroundColor = Color.red;
    if (GUILayout.Button("NONE", style, GUILayout.Width(150), GUILayout.Height(25)))
    {
        component.typeAllowed.Clear();
    }

    GUI.backgroundColor = Color.yellow;
    if (GUILayout.Button("ALL", style, GUILayout.Width(150), GUILayout.Height(25)))
    {
        foreach (GlobalEnums.ItemType t in Enum.GetValues(typeof(GlobalEnums.ItemType)))
        {
            component.typeAllowed.Add(t);
        }
    }

    GUILayout.EndHorizontal();

    EditorGUILayout.LabelField("Allowed Classes");
    counter = 0;

    GUILayout.BeginHorizontal();

    foreach (GlobalEnums.GameClasses t in Enum.GetValues(typeof(GlobalEnums.GameClasses)))
    {
        if (counter % 3 == 0 && counter > 1)
        {
            GUILayout.EndHorizontal();
            GUILayout.BeginHorizontal();
        }

        if (component.classAllowed.Contains(t))
        {
            GUI.backgroundColor = Color.green;
        }
        else
        {
            GUI.backgroundColor = Color.white;
        }

        if (GUILayout.Button(t.ToString(), style, GUILayout.Width(120), GUILayout.Height(30)))
        {
            if (component.classAllowed.Contains(t))
            {
                component.classAllowed.Remove(t);
            }
            else
            {
                component.classAllowed.Add(t);
            }
        }

        counter++;

    }

    GUILayout.EndHorizontal();
    GUILayout.BeginHorizontal();

    GUI.backgroundColor = Color.red;
    if (GUILayout.Button("NONE", style, GUILayout.Width(150), GUILayout.Height(25)))
    {
        component.classAllowed.Clear();
    }

    GUI.backgroundColor = Color.yellow;
    if (GUILayout.Button("ALL", style, GUILayout.Width(150), GUILayout.Height(25)))
    {
        foreach (GlobalEnums.GameClasses t in Enum.GetValues(typeof(GlobalEnums.GameClasses)))
        {
            component.classAllowed.Add(t);
        }
    }

    GUILayout.EndHorizontal();
    GUILayout.EndVertical();

    serializedObject.ApplyModifiedProperties();

}

}

每当有变化时尝试调用 EditorUtility.SetDirty(target)。否则 Unity 不会检测到您的集合中的值正在更改,因此不会保存它们。

Unity 无法序列化 HashSet<T>,因此您的更改永远不会保存到磁盘。

这里有两个选择:

编辑

这是一个示例,您可以如何使用 ISerializationCallbackReceiver 界面来做到这一点。

public class Itemslot : MonoBehaviour, IDropHandler, IPointerEnterHandler, 
IPointerExitHandler, ISerializationCallbackReceiver
{
    public HashSet<GlobalEnums.ItemType> typeAllowed = new HashSet<GlobalEnums.ItemType>();

    // private field to ensure serialization
    [SerializeField]
    private List<GlobalEnums.ItemType> _typeAllowedList = new List<GlobalEnums.ItemType>();

    public void OnBeforeSerialize()
    {
        // store HashSet contents in List
        _typeAllowedList.Clear();
        foreach(var allowedType in typeAllowed)
        {
            _typeAllowedList.Add(allowedType);
        }
    }

    public void OnAfterDeserialize()
    {
        // load contents from the List into the HashSet
        typeAllowed.Clear();
        foreach(var allowedType in _typeAllowedList)
        {
            typeAllowed.Add(allowedType);
        }
    }
}