在检视面板内显示只读结构体中的只读字段

4

我有以下不可变结构体。

[Serializable]
public readonly struct Wind
{
    /// <param name="windSpeed">The speed of the wind</param>
    /// <param name="rho">The density of the air, if not provided 15 degrees assumed aka 1.225f</param>
    public Wind(Vector3 windSpeed, float rho = 1.225f)
    {
        Speed = windSpeed;
        Rho = rho;
    }

    /// <summary>
    /// The speed of the wind [m/s]
    /// </summary>
    [DisplayReadOnly]
    public readonly Vector3 Speed;

    /// <summary>
    /// Density of the air [kg/m^3]
    /// </summary>
    [DisplayReadOnly]
    public readonly float Rho;
}

这样做的问题在于,Unity无法序列化只读字段,所以我按照Anton Semchenko的指导创建了一个属性,在检视面板中仅显示这些字段以方便调试。
以下是我创建的脚本:
自定义属性绘制器
[CustomPropertyDrawer(typeof(DisplayReadOnlyAttribute))]
public class DisplayReadOnlyAttributeDrawer : PropertyDrawer
{
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        DisplayReadOnlyAttribute att = (DisplayReadOnlyAttribute)attribute;
        object obj = property.serializedObject.targetObject;
        Type type = obj.GetType();
        FieldInfo field = type.GetField(property.propertyPath, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
        object val = field?.GetValue(obj);

        if (att.warningIfNull && (val == null || val.ToString().Equals("null")))
            val += " <-This value should NOT be NULL!";

        EditorGUI.LabelField(position, string.Format("{0}: {1}", label.text, val));
    }
}

显示只读属性在检查器中的属性信号。
public class DisplayReadOnlyAttribute : PropertyAttribute
{
    /// <summary>
    /// Writes a warning if the value of this field is null
    /// </summary>
    public readonly bool warningIfNull = false;

    public DisplayReadOnlyAttribute(bool _warningIfNull = false)
    {
        warningIfNull = _warningIfNull;
    }
}

问题在于,如果将此属性应用于只读字段,它将不起作用。它仅在应用于非只读字段时才被调用。 public float Rho; 我不仅对上述解决方案感兴趣,如果您知道以另一种方式显示只读字段,请不要独自保留。

1个回答

1

因为Unity有限制,无法将PropertyAttribute钩接到只读字段上,所以最终我选择了自定义检查器GUI。

[Serializable]
public readonly struct Wind
{
    /// <param name="windSpeed">The speed of the wind</param>
    /// <param name="rho">The density of the air, if not provided 15 degrees assumed aka 1.225f</param>
    public Wind(Vector3 windSpeed, float rho = 1.225f)
    {
        Speed = windSpeed;
        Rho = rho;
    }

    /// <summary>
    /// The speed of the wind [m/s]
    /// </summary>
    public readonly Vector3 Speed;

    /// <summary>
    /// Density of the air [kg/m^3]
    /// </summary>
    public readonly float Rho;

#if UNITY_EDITOR
    [CustomPropertyDrawer(typeof(Wind))]
    public class WindDrawer : PropertyDrawer
    {
        public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
            return EditorGUIUtility.singleLineHeight * 4 + 6;
        }

        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            // Readonly fields are initialized at construction time so we need to wait for them
            bool readonlyInitialized = GameManager.LevelController;
            // Getting the readonly fields values
            var rho = typeof(Wind).GetField(nameof(Rho));
            var speed = typeof(Wind).GetField(nameof(Speed));

            var windRect = new Rect(position.x, position.y + 18, position.width, 16);
            var rhoRect = new Rect(position.x, position.y + 42, position.width, 16);
            var speedRect = new Rect(position.x, position.y + 66, position.width, 16);

            EditorGUI.BeginProperty(position, label, property);
            {
                EditorGUI.LabelField(windRect, label);
                EditorGUI.indentLevel++;

                EditorGUI.BeginDisabledGroup(true);
                {
                    EditorGUI.Vector3Field(rhoRect, nameof(Speed),
                    // Default value is Vector3.zero when the readonly field cannot be defined
                        readonlyInitialized ? (Vector3)speed.GetValue(GameManager.LevelController.WeatherController.Wind) : Vector3.zero);
                }
                EditorGUI.EndDisabledGroup();
            }
            EditorGUI.EndProperty();

            EditorGUI.BeginProperty(position, label, property);
            {
                EditorGUI.BeginDisabledGroup(true);
                {
                    EditorGUI.FloatField(speedRect, nameof(Rho),
                       // Default value is zero when the readonly field cannot be defined
                       readonlyInitialized ? (float)rho.GetValue(GameManager.LevelController.WeatherController.Wind) : 0.0f);
                }
                EditorGUI.EndDisabledGroup();
            }
            EditorGUI.indentLevel--;
            EditorGUI.EndProperty();
        }
    }
#endif
}

这将产生以下编辑器GUI: 输入图像描述 瞧!你的只读结构出现在编辑器窗口中。
我认为这并不值得努力,除非你真的需要性能,否则只需使用此方法,并将只读属性附加到支持字段即可。
[Serializable]
public struct Wind
{
    /// <param name="windSpeed">The speed of the wind</param>
    /// <param name="rho">The density of the air, if not provided 15 degrees assumed aka 1.225f</param>
    public Wind(Vector3 windSpeed, float rho = 1.225f)
    {
        Speed = windSpeed;
        Rho = rho;
    }

    /// <summary>
    /// The speed of the wind [m/s]
    /// </summary>
    public Vector3 Speed { get; }

    /// <summary>
    /// Density of the air [kg/m^3]
    /// </summary>
    public float Rho { get; }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接