【问题标题】:Custom inspector for HashSet in UnityUnity 中 HashSet 的自定义检查器
【发布时间】:2017-10-10 22:21:15
【问题描述】:

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

1) 使用 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();

}

}

【问题讨论】:

    标签: c# unity3d editor hashset


    【解决方案1】:

    Unity 无法序列化HashSet&lt;T&gt;,因此您的更改将永远不会保存到磁盘。

    你有两个选择:

    编辑

    这是一个使用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);
            }
        }
    }
    

    【讨论】:

    • 1) 我相信它会序列化。众所周知,如果您执行public class HashSetItemType : HashSet&lt;GlobalEnums.ItemType&gt; { },则 Unity 可以序列化。 answers.unity3d.com/answers/462070/view.html
    • 2) ISerializationCallbackReceiver 知道会导致奇怪的问题,可能与多线程有关。看到这个link和这个link
    • 我不相信黑客可以工作,特别是因为您链接的帖子已有 4 年历史。是的,序列化是在另一个线程中完成的,但这只是您必须处理的事情。如您的第二个链接所述,可以避免这些问题。
    • 好的。我对 C# 和编辑器内部行为的经验是有限的,所以你能提供一个如何使用它的例子吗?我需要为每种类型的哈希集做特殊的类吗?
    • 有效!我只需要将开头修改为public HashSet&lt;GlobalEnums.ItemType&gt; typeAllowed = new HashSet&lt;GlobalEnums.ItemType&gt;();,因为我遇到了错误。非常感谢!
    【解决方案2】:

    只要有变化,请尝试致电EditorUtility.SetDirty(target)。否则 Unity 不会检测到集合中的值正在发生变化,因此不会保存它们。

    【讨论】:

    • 对编辑器脚本没有经验,那么在这种情况下,目标应该是什么? component?
    • 似乎不起作用。还尝试将场景标记为脏,但当我重新启动 Unity 时,场景中的预制件或对象似乎都不记得了。
    猜你喜欢
    • 1970-01-01
    • 2012-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-30
    相关资源
    最近更新 更多