Unity不序列化int?领域

时间:2018-10-17 11:45:51

标签: unity3d serialization editor nullable public

我有一个要在编辑器中更改其属性的类。因此,我将班级设为System.Serializable,并公开了我希望能够更改的变量。
 像这样:

[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?也显示出来?

3 个答案:

答案 0 :(得分:4)

可空类型未在Unity Editor中进行序列化,因为它的序列化器不支持null。 如果您不打算使用JsonUtility将此类序列化为json,则有一个小的解决方法。 关键思想是您必须创建自己的可为null的int。

public class IntNullable 
{
     public int Value;
     public bool HasValue;
 }

就像在.NET中完成一样。然后,您可以为IntNullable或您的UIOptionsRing创建一个Custom Editor。在此编辑器中,您可以创建一个int值字段和一个“ Set Null”按钮,这将更改HasValue变量的值。而且,您还需要在代码中使用此自定义IntNullable

答案 1 :(得分:0)

对 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()
    { }
}

答案 2 :(得分:0)

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 中一直为我服务。