Unity不序列化int?场地
Unity Doesnt Serialize int? field
我有一个 class 我想在编辑器中更改其属性。所以我制作了 class System.Serializable 并制作了我希望能够更改的变量 public。
像这样:
[System.Serializable]
public class UIOptionsRing
{
public float Radius, DistanceBetweenPoints, StartOffset, GapInDegrees;
public int? GapAfterElementNumer = 3; //this var doesnt show up
public Vector3 CircleCenter;
public GameObject CircleElementsContainer;
}
但我遇到的问题是 GapAfterElementNumer 没有显示在所有其他字段的编辑器中。我怎样才能让 int?
也出现?
可空类型在 Unity Editor 中不被序列化,因为它的序列化程序不支持 null
。
如果您不打算使用 JsonUtility
将此 class 序列化为 json,则有一个小的解决方法。
关键思想是您必须创建自己的可为空的 int。像
public class IntNullable
{
public int Value;
public bool HasValue;
}
就像在 .NET 中完成的一样。然后您可以为 IntNullable
或您的 UIOptionsRing
创建一个 Custom Editor。在此编辑器中,您可以为 int 值创建一个字段和一个按钮 "Set Null",这将更改 HasValue
变量的值。此外,您需要在代码中使用此自定义 IntNullable
。
对 vmchar 答案的改进,允许空赋值:
[Serializable]
public struct NullableInt
{
public int Value;
public bool HasValue;
public NullableInt(int value)
{
Value = value;
HasValue = true;
}
public static implicit operator NullableInt(int value) => new NullableInt(value);
public static implicit operator NullableInt(NullableNull value) => new NullableInt();
public static implicit operator int(NullableInt value) => value.Value;
public static implicit operator int? (NullableInt value) => value.HasValue ? value.Value : new int?();
}
public sealed class NullableNull
{
private NullableNull()
{ }
}
Unity 不仅不能在检查器中显示可为空的字段,也不能序列化它们。为了支持这一点,我们需要制作一个可序列化的 System.Nullable(正如@vmchar 解释的那样)的自定义版本,然后给它一个 属性 抽屉。对 System.Nullable 进行无缝替换不一定很明显,因此我提供了这个示例。它应该是 nullable 的替代品(int?
可以替换为 SN<int>
,由于隐式转换,其他所有东西都应该可以工作)以及基本的自定义 属性 抽屉。
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
/// <summary>
/// Serializable Nullable (SN) Does the same as C# System.Nullable, except it's an ordinary
/// serializable struct, allowing unity to serialize it and show it in the inspector.
/// </summary>
[System.Serializable]
public struct SN<T> where T : struct {
public T Value { get {
if (!HasValue)
throw new System.InvalidOperationException("Serializable nullable object must have a value.");
return v;
} }
public bool HasValue { get { return hasValue; } }
[SerializeField]
private T v;
[SerializeField]
private bool hasValue;
public SN(bool hasValue, T v) {
this.v = v;
this.hasValue = hasValue;
}
private SN(T v) {
this.v = v;
this.hasValue = true;
}
public static implicit operator SN<T>(T value) {
return new SN<T>(value);
}
public static implicit operator SN<T>(System.Nullable<T> value) {
return value.HasValue ? new SN<T>(value.Value) : new SN<T>();
}
public static implicit operator System.Nullable<T>(SN<T> value) {
return value.HasValue ? (T?)value.Value : null;
}
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(SN<>))]
internal class SNDrawer : PropertyDrawer {
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
EditorGUI.BeginProperty(position, label, property);
// Draw label
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
// Don't make child fields be indented
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
// Calculate rects
var setRect = new Rect(position.x, position.y, 15, position.height);
var consumed = setRect.width + 5;
var valueRect = new Rect(position.x + consumed, position.y, position.width - consumed, position.height);
// Draw fields - pass GUIContent.none to each so they are drawn without labels
var hasValueProp = property.FindPropertyRelative("hasValue");
EditorGUI.PropertyField(setRect, hasValueProp, GUIContent.none);
bool guiEnabled = GUI.enabled;
GUI.enabled = guiEnabled && hasValueProp.boolValue;
EditorGUI.PropertyField(valueRect, property.FindPropertyRelative("v"), GUIContent.none);
GUI.enabled = guiEnabled;
// Set indent back to what it was
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
}
#endif
它的性能可能不如 System.Nullable,但对于大多数用途来说应该没问题。到目前为止,它在启用 C# 4 的 Unity 2021.1 中一直为我服务。
我有一个 class 我想在编辑器中更改其属性。所以我制作了 class System.Serializable 并制作了我希望能够更改的变量 public。
像这样:
[System.Serializable]
public class UIOptionsRing
{
public float Radius, DistanceBetweenPoints, StartOffset, GapInDegrees;
public int? GapAfterElementNumer = 3; //this var doesnt show up
public Vector3 CircleCenter;
public GameObject CircleElementsContainer;
}
但我遇到的问题是 GapAfterElementNumer 没有显示在所有其他字段的编辑器中。我怎样才能让 int?
也出现?
可空类型在 Unity Editor 中不被序列化,因为它的序列化程序不支持 null
。
如果您不打算使用 JsonUtility
将此 class 序列化为 json,则有一个小的解决方法。
关键思想是您必须创建自己的可为空的 int。像
public class IntNullable
{
public int Value;
public bool HasValue;
}
就像在 .NET 中完成的一样。然后您可以为 IntNullable
或您的 UIOptionsRing
创建一个 Custom Editor。在此编辑器中,您可以为 int 值创建一个字段和一个按钮 "Set Null",这将更改 HasValue
变量的值。此外,您需要在代码中使用此自定义 IntNullable
。
对 vmchar 答案的改进,允许空赋值:
[Serializable]
public struct NullableInt
{
public int Value;
public bool HasValue;
public NullableInt(int value)
{
Value = value;
HasValue = true;
}
public static implicit operator NullableInt(int value) => new NullableInt(value);
public static implicit operator NullableInt(NullableNull value) => new NullableInt();
public static implicit operator int(NullableInt value) => value.Value;
public static implicit operator int? (NullableInt value) => value.HasValue ? value.Value : new int?();
}
public sealed class NullableNull
{
private NullableNull()
{ }
}
Unity 不仅不能在检查器中显示可为空的字段,也不能序列化它们。为了支持这一点,我们需要制作一个可序列化的 System.Nullable(正如@vmchar 解释的那样)的自定义版本,然后给它一个 属性 抽屉。对 System.Nullable 进行无缝替换不一定很明显,因此我提供了这个示例。它应该是 nullable 的替代品(int?
可以替换为 SN<int>
,由于隐式转换,其他所有东西都应该可以工作)以及基本的自定义 属性 抽屉。
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
/// <summary>
/// Serializable Nullable (SN) Does the same as C# System.Nullable, except it's an ordinary
/// serializable struct, allowing unity to serialize it and show it in the inspector.
/// </summary>
[System.Serializable]
public struct SN<T> where T : struct {
public T Value { get {
if (!HasValue)
throw new System.InvalidOperationException("Serializable nullable object must have a value.");
return v;
} }
public bool HasValue { get { return hasValue; } }
[SerializeField]
private T v;
[SerializeField]
private bool hasValue;
public SN(bool hasValue, T v) {
this.v = v;
this.hasValue = hasValue;
}
private SN(T v) {
this.v = v;
this.hasValue = true;
}
public static implicit operator SN<T>(T value) {
return new SN<T>(value);
}
public static implicit operator SN<T>(System.Nullable<T> value) {
return value.HasValue ? new SN<T>(value.Value) : new SN<T>();
}
public static implicit operator System.Nullable<T>(SN<T> value) {
return value.HasValue ? (T?)value.Value : null;
}
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(SN<>))]
internal class SNDrawer : PropertyDrawer {
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
EditorGUI.BeginProperty(position, label, property);
// Draw label
position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
// Don't make child fields be indented
var indent = EditorGUI.indentLevel;
EditorGUI.indentLevel = 0;
// Calculate rects
var setRect = new Rect(position.x, position.y, 15, position.height);
var consumed = setRect.width + 5;
var valueRect = new Rect(position.x + consumed, position.y, position.width - consumed, position.height);
// Draw fields - pass GUIContent.none to each so they are drawn without labels
var hasValueProp = property.FindPropertyRelative("hasValue");
EditorGUI.PropertyField(setRect, hasValueProp, GUIContent.none);
bool guiEnabled = GUI.enabled;
GUI.enabled = guiEnabled && hasValueProp.boolValue;
EditorGUI.PropertyField(valueRect, property.FindPropertyRelative("v"), GUIContent.none);
GUI.enabled = guiEnabled;
// Set indent back to what it was
EditorGUI.indentLevel = indent;
EditorGUI.EndProperty();
}
}
#endif
它的性能可能不如 System.Nullable,但对于大多数用途来说应该没问题。到目前为止,它在启用 C# 4 的 Unity 2021.1 中一直为我服务。