likuku
likuku

Reputation: 358

unity conditional fields custom editor

my goal: i have a script

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

I need B to be visible only if A is TRUE

i'de done an extention to the script and added UnityEditor in the title

[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");
        }
    }
}

but nothing really changed. what did i do wrong?

Upvotes: 3

Views: 11833

Answers (1)

derHugo
derHugo

Reputation: 90659

First of all your class definition is wrong. You either need [Serializable] or the class should inherit from MonoBehaviour if it shall be attached to a GameObject. Either way remove the ()

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

or

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

Then note that a Custom Editor is only for classes inherting from either a MonoBehaviour or a ScriptableObject. In other cases you will rather have to implement a Custom PropertyDrawer.

You should always try to not directly make changes in the target. You would have to handle a lot of things like marking as dirty, undo/redo etc by yourself...

Rather always go through SerializedPropertys.

Also note that base.OnInspectorGUI(); will draw the default inspector


So assuming MyScript is a MonoBehaviour class

[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();
    }
}

Or if MyScript is actually not a MonoBehaviour then as PropertyDrawer which works basically very similar except you have to use the EditorGUI versions of the fields always requiring a position Rect as parameter:

[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;
    }
}

Upvotes: 5

Related Questions