【问题标题】:Unity - Custom drawing of a struct in the inspectorUnity - 检查器中结构的自定义绘图
【发布时间】:2019-02-18 13:59:49
【问题描述】:

我有一个自定义结构,代码如下:

[Serializable]
public struct HexPoint : IEquatable<HexPoint>
{
    public readonly int x;
    public readonly int y;
    public readonly int z;
    
    // Some custom methods for initializations and operators
}

如果我将 x、y 和 z 变量设为非只读,它们会很好地显示在统一检查器中。但是,我有一些规则需要他们满足(实际上是 x+y+z=0),所以我添加了 readonly 以防止人们乱用它。

但作为只读变量,它们不会显示(因为它们无法修改)! :(

我想知道它们是否是我在统一检查器中显示它们的一种方式,类似于 PropertyDrawer。我知道我可以将我的结构切换到一个类,因为 PropertyDrawer 是为类保留的,但我想将它保留为一个结构。

那么,有没有办法显示这些值?并最终使用自定义初始化器来修改它们?

非常感谢!

【问题讨论】:

    标签: c# unity3d struct unity3d-editor unity-editor


    【解决方案1】:

    readonly 使它们也成为non-serialized -> 不在检查器中显示

    注意PropertyDrawer仅限于class类型,但也可用于struct类型。


    实际上不需要CustomPropertyDrawer

    您可以拥有 public readonly properties 来访问私有字段并将它们显示在 Inspector 中使用 [SerializeField] 这使得它们只能通过 Inspector 而不能通过其他类进行编辑。

    [Serializable]
    public struct HexPoint : IEquatable<HexPoint>
    {
        // Those are not displayed in the inspector, 
        // readonly and accessible by other classes
        public int x { get { return _x; } }
        public int y { get { return _y; } }
        public int z { get { return _z; } }
    
        // if you prefer you can also use the expression body style instead
        //public int x => _x;
        //public int y => _y;
        //public int z => _z;
    
        // Those are displayed and editable in the Inspector
        // but private and therefor not changeable by other classes
        [SerializeField] private int _x;
        [SerializeField] private int _y;
        [SerializeField] private int _z;
    
        public bool Equals(HexPoint other)
        {
            return _x == other._x && _y == other._y && _z == other._z;
        }
    
        public override bool Equals(object obj)
        {
            return obj is HexPoint other && Equals(other);
        }
    
        public override int GetHashCode()
        {
            unchecked
            {
                var hashCode = _x;
                hashCode = (hashCode * 397) ^ _y;
                hashCode = (hashCode * 397) ^ _z;
                return hashCode;
            }
        }
    }
    

    如果您真的想使用PropertyDrawer 来另外也不允许在检查器中编辑这些值但仍然保存并查看它们,您可以添加一个,例如

    [Serializable]
    public struct HexPoint : IEquatable<HexPoint>
    {
        // Those are not displayed in the inspector, 
        // readonly and accessible by other classes
        public int x { get { return _x; } }
        public int y { get { return _y; } }
        public int z { get { return _z; } }
    
        // if you prefer you can also use the expression body style instead
        //public int x => _x;
        //public int y => _y;
        //public int z => _z;
    
        // Those are displayed and editable in the Inspector
        // but private and therefor not changeable by other classes
        [SerializeField] private int _x;
        [SerializeField] private int _y;
        [SerializeField] private int _z;
    
        public bool Equals(HexPoint other)
        {
            return _x == other._x && _y == other._y && _z == other._z;
        }
    
        public override bool Equals(object obj)
        {
            return obj is HexPoint other && Equals(other);
        }
    
        public override int GetHashCode()
        {
            unchecked
            {
                var hashCode = _x;
                hashCode = (hashCode * 397) ^ _y;
                hashCode = (hashCode * 397) ^ _z;
                return hashCode;
            }
        }
    
    #if UNITY_EDITOR
    
        [CustomPropertyDrawer(typeof(HexPoint))]
        public class HexPointDrawer : PropertyDrawer
        {
            public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
            {
                    return EditorGUIUtility.singleLineHeight * (EditorGUIUtility.wideMode ? 1 : 2);
            }
    
            public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
            {
                // Find the SerializedProperties by name
                var x = property.FindPropertyRelative(nameof(_x));
                var y = property.FindPropertyRelative(nameof(_y));
                var z = property.FindPropertyRelative(nameof(_z));
    
                // Using BeginProperty / EndProperty on the parent property means that
                // prefab override logic works on the entire property.
                EditorGUI.BeginProperty(position, label, property);
                {
                    // Makes the fields disabled / grayed out
                    EditorGUI.BeginDisabledGroup(true);
                    {
                        // In your case the best option would be a Vector3Field which handles the correct drawing
                        EditorGUI.Vector3IntField(position, label, new Vector3Int(x.intValue, y.intValue, z.intValue));
                    }
                    EditorGUI.EndDisabledGroup();
                }
                EditorGUI.EndProperty();
            }
        }
    
    #endif
    }
    

    提示用于检查更改后的值MonoBehaviour.OnValidate 可能对您来说很有趣

    【讨论】:

    • 关于 OnValidate() 的提示:它可能很有用,但它会在每个单一行为脚本中移动逻辑......因此可能会导致大量重复代码,因为该结构已被大量使用。
    • @Eyap 因此,这只是一个提示;)为了避免重复代码,您可以在结构中实现逻辑本身,并且只为 MonoBehaviours 的每个字段调用它;)否则您不会来围绕编写自己的 CustomPropertyDrawer
    • 这违背了只读的目的,因为变量将暴露在检查器中进行编辑。即使 OP 接受了这个答案,我认为他的意思是将变量显示为灰色,就像在调试检查器中一样。
    • @Bip901 为此,您肯定需要一个 PropertyDrawer ...因为 OP 接受了答案,但我会完全 not 假设这是 OP 想要的...否则为什么会如果它没有执行所需的操作,OP 会接受答案吗? ;) ... 不管怎样,我只为你添加了一个PropertyDrawer,它可以额外执行此操作
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多