首页 > 解决方案 > unity 条件字段自定义编辑器

问题描述

我的目标:我有一个脚本

public class MyScript: MonoBehaviour
{
   public bool A;
   public bool B;
}

仅当 A 为 TRUE 时,我才需要 B 可见

我对脚本进行了扩展并在标题中添加了 UnityEditor

[CustomEditor(typeof(MyScript))]
public class MyEditor : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        MyScript tool = (MyScript) target;

        tool.A = GUILayout.Toggle(tool.A, "Flag");

        if(tool.A)
        {
            tool.B= EditorGUILayout.Toggle(tool.B, "Flag");
        }
    }
}

但没有什么真正改变。我做错了什么?

标签: unity3dunity3d-editorunity-editor

解决方案


首先你的类定义是错误的。如果它应该附加到游戏对象,您需要[Serializable]或者该类应该继承自。MonoBehaviour无论哪种方式删除()

[Serializable]
public class MyScript
{
   public bool A;
   public bool B;
}

或者

public class MyScript : MonoBehaviour
{
   public bool A;
   public bool B;
}

然后请注意, aCustom Editor适用于从 aMonoBehaviour或 a继承的类ScriptableObject。在其他情况下,您将不得不实现CustomPropertyDrawer

您应该始终尽量不要直接在target. 您将不得不自己处理很多事情,例如标记为脏,撤消/重做等......

而是总是通过SerializedPropertys。

另请注意,base.OnInspectorGUI();将绘制默认检查器


所以假设MyScript是一个MonoBehaviour

[CustomEditor(typeof(MyScript))]
public class MyEditor : Editor
{
    SerializedProperty a;
    SerializedProperty b;

    // is called once when according object gains focus in the hierachy
    private void OnEnable()
    {
        // link serialized properties to the target's fields
        // more efficient doing this only once
        a = serializedObject.FindProperty("A");
        b = serializedObject.FindProperty("B");
    }

    public override void OnInspectorGUI()
    {
        // fetch current values from the real instance into the serialized "clone"
        serializedObject.Update();

        // Draw field for A
        EditorGUILayout.PropertyField(a);

        if(a.boolValue)
        {
            // Draw field for B
            EditorGUILayout.PropertyField(b);
        }

        // write back serialized values to the real instance
        // automatically handles all marking dirty and undo/redo
        serializedObject.ApplyModifiedProperties();
    }
}

或者 ifMyScript实际上不是 a MonoBehaviourthen asPropertyDrawer它的工作原理基本上非常相似,除非您必须使用EditorGUI始终需要位置Rect作为参数的字段版本:

[CustomPropertyDrawer(typeof(MyScript), true)]
public class MyEditor : PropertyDrawer
{
    private bool isUnFolded;

    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {
        // draw folder for the entire class
        isUnFolded = EditorGUI.Foldout(new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), isUnFolded, label);
        // go to the next line
        position.y += EditorGUIUtility.singleLineHeight;

        // only draw the rest if unfolded
        if (isUnFolded)
        {
            // draw fields indented
            EditorGUI.indentLevel++;

            // similar to before get the according serialized properties for the fields
            var a = property.FindPropertyRelative("A");
            var b = property.FindPropertyRelative("B");

            // Draw A field
            EditorGUI.PropertyField(new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), a);
            position.y += EditorGUIUtility.singleLineHeight;

            if (a.boolValue)
            {
                // Draw B field
                EditorGUI.PropertyField(new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), b);
            }

            // reset indentation
            EditorGUI.indentLevel--;
        }
    }

    // IMPORTANT you have to implement this since your new property is
    // higher then 1 single line
    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        // default is 1 single line
        var height = 1;
        // if unfolded at least 1 line more, if a is true 2 lines more
        if(isUnFolded) height += (property.FindPropertyRelative("A").boolValue ? 2 : 1);

        return height * EditorGUIUtility.singleLineHeight;
    }
}

推荐阅读