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>
,因此您的更改永远不会保存到磁盘。
这里有两个选择:
- 使用
List<T>
。这个类型可以被Unity序列化,只要T
也可以序列化。
- 实施ISerializationCallbackReceiver to handle the serialization yourself, e.g. store the HashSet's contents in a List when serializing and move them back into the HashSet when deserializing. See https://docs.unity3d.com/Manual/script-Serialization-Custom.html
编辑
这是一个示例,您可以如何使用 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);
}
}
}
我正在 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>
,因此您的更改永远不会保存到磁盘。
这里有两个选择:
- 使用
List<T>
。这个类型可以被Unity序列化,只要T
也可以序列化。 - 实施ISerializationCallbackReceiver to handle the serialization yourself, e.g. store the HashSet's contents in a List when serializing and move them back into the HashSet when deserializing. See https://docs.unity3d.com/Manual/script-Serialization-Custom.html
编辑
这是一个示例,您可以如何使用 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);
}
}
}