Need help with some custom inspector

Hey guys,
I’m trying to create a custom inspector element for a class. What I want to achieve is something like in this photo.

On the same Object if I choose Peasant to have some variables while if I choose warrior to get other variables.
If someone could point me to the right tutorial or give me a short script of how this could be possible it would be great. I searched the internet for a few hours, but to be honest I’m not sure what to search for.

Thank you

First, depending on your needs, would it be better to use subclasses or composition?

Subclasses
For example, you could use these subclasses to distinguish between Peasants and Warriors without having to write any custom editors:

public class ConversationFirstNode : MonoBehaviour {
    public string name;
    public int age;
}

public class PeasantNode : ConversationFirstNode {
    [Header("Peasant-Specific")]
    public int numberOfSheep;
}

public class WarriorNode : ConversationFirstNode {
    [Header("Warrior-Specific")]
    public int level;
    public string profession;
}

Composition
Or you could add another class that indicates the character’s station:

public class ConversationFirstNode : MonoBehaviour {
    public string name;
    public int age;
    public Station station;
}

public class Station : MonoBehaviour {}

public class Peasant : Station {
    public int numberOfSheep;
}

public class Warrior : Station {
    public int level;
    public string profession;
}

Custom Editors
But if you want to use a custom editor, these two links will help:

Briefly, you’ll create a new class inside an Editor folder. Its OnInspectorGUI method will:

  • Copy the component’s current data into a serialized object representation.
  • Show editor GUI elements for each field of serialized data.
  • Copy the serialized object representation back to the component.

For example:

using UnityEngine;
using UnityEditor;
[CustomEditor(typeof(ConversationFirstNode))]
[CanEditMultipleObjects]
public class ConversationFirstNodeEditor : Editor {

    public override void OnInspectorGUI() {
        serializedObject.Update();
        EditorGUILayout.PropertyField(serializedObject.FindProperty("name"));
        EditorGUILayout.PropertyField(serializedObject.FindProperty("age"));
        var classProperty = serializedObject.FindProperty("characterClass");
        if (classProperty.enumValueIndex == (int)CharacterClass.Warrior) {
            // Show warrior-specific info:
            EditorGUILayout.PropertyField(serializedObject.FindProperty("level"));
            EditorGUILayout.PropertyField(serializedObject.FindProperty("profession"));
        }
        serializedObject.ApplyModifiedProperties();
    }
}
1 Like

One more question… Is there any way for me to create a List of this kind of types. For example to create a GameObject which have a public list. And when I set it to 10 to have 10 objects of the ConversationFirstNode?

Also I’m trying now to use the subclasses way but I’m not sure how can I set the class? I got age, name, but I don’t understand how can I choose what kind of person it is so that I have those attributes only for a certain class

Sure. It’s easiest if FirstConversationNode is plain serialized class and not a MonoBehaviour. For example:

[System.Serializable]
public class ConversationFirstNode { // Note: not derived from MonoBehaviour
    public string name;
    public int age;
    public CharacterClass characterClass;
    public int level;
    public string profession;
}

public class ConversationList : MonoBehaviour {
    public ConversationFirstNode[] firstNodes;
}

When you add ConversationList to a GameObject and set the size of its firstNodes array to 10, it will create 10 ConversationFirstNode objects.

MonoBehaviours, however, are serialized by reference. If ConversationFirstNode is a MonoBehaviour (public class ConversationFirstNode : MonoBehaviour), then if you set the size of firstNodes to 10, it will create 10 empty slots to which you’ll need to manually assign references to ConversationFirstNode components. You can write an editor script to do that, something like:

[CustomEditor(typeof(ConversationList))]
public class ConversationListEditor : Editor {
    public override void OnInspectorGUI() {
        DrawDefaultInspector();
        if (GUILayout.Button("Create missing components")) {
            serializedObject.Update();
            var firstNodesProperty = serializedObject.FindProperty("firstNodes");
            for (int i = 0; i < firstNodesProperty.arraySize; i++) {
                var element = firstNodesProperty.GetArrayElementAtIndex(i);
                if (element.objectReferenceValue == null) {
                    // Array element is null. Create & assign a component:
                    var gameObject = (target as ConversationList).gameObject;
                    var component = gaeObject.AddComponent<ConversationFirstNode>();
                    element.objectReferenceValue = component;
                }
            }
            serializedObject.ApplyModifiedProperties();
        }
    }
}

Thank you very much. I appreciate your fast response. I’m going to try it now.
Best regards

Happy to help! A lot of info is packed into these replies. If you get stuck, those two documentation links in my first reply should help clarify things.

Sorry to bother you, but I’m a bit stucked while creating the list and you seem like a guy who knows this things.
I created my objects and made the Editor as I wished, but now I’m not sure how can I create a list of those objects.

This is the type of objects I want to see in my list

public class ConversationOption{

    public OptionTypes optionType;

    public StatBool boolVariable;
    public bool boolNeedsToBe;

    public StatsList statVariable;
    public Comparison statNeedsToBe;
    public int value;

    public GameObject target;
}

And this is the Editor I made for the ConversationOption

[CustomEditor(typeof(ConversationOption))]
[CanEditMultipleObjects]
public class ConversationOptionEditor : Editor{

    public override void OnInspectorGUI(){

        serializedObject.Update();

        EditorGUILayout.PropertyField(serializedObject.FindProperty("optionType"));

        OptionTypes optionType = (OptionTypes)serializedObject.FindProperty("optionType").enumValueIndex;

        switch(optionType){

        case OptionTypes.booleanType:
            EditorGUILayout.Space();
            EditorGUILayout.PropertyField(serializedObject.FindProperty("boolVariable"));
            EditorGUILayout.PropertyField(serializedObject.FindProperty("boolNeedsToBe"));

            break;
       
        case OptionTypes.integerType:
            EditorGUILayout.Space();
            EditorGUILayout.PropertyField(serializedObject.FindProperty("statVariable"));
            EditorGUILayout.PropertyField(serializedObject.FindProperty("statNeedsToBe"));
            EditorGUILayout.PropertyField(serializedObject.FindProperty("value"));
            break;
        }

        EditorGUILayout.Space();
        EditorGUILayout.PropertyField(serializedObject.FindProperty("target"));

        serializedObject.ApplyModifiedProperties();

    }

}

Thank you

Where do you want the list? What’s the overall purpose and structure that you want?

For a game that I’m working, I made a poorly conversation system with different nodes based. And one of them should be the list GameObject.
So what I’m trying to achieve here is a new node, is having a GameObject with the ConversationOptionList Script where I can create a list of ConversationOptions elements.
I already made a node like this which is working, but it’s a bit annoying, because I have to create other GameObjects and then drag/drop into my ConversationOptionList Object.

This is how my ConversationOptionScript looks like

And this is one ConversationOption Object

And the other one

So the point of this thing is having different conversations based on some stats of the character, like money, fighting skill, progress in quest. The player could always talk to the NPC but only some of the options will be available based on some stats.
Here is an example of what I get out of this.

So the thing that annoys me is the fact that I have to create another objects with the ConversationOption script instead of having those options on the ConversationOptionList.

You could add a “New Option” button in your custom editor that creates a child GameObject with a new ConversationOption:

public override OnInspectorGUI() {
    serializedObject.Update();
    ...
    serializedObject.ApplyModifiedProperties();
    if (GUILayout.Button("New Option")) {
        Undo.RecordObject(conversationOptionList, "New Option");
        var child = new GameObject("Option", typeof(ConversationOption));
        child.transform.setParent(conversationOptionList.transform);
        conversationOptionList.options.Add(child.GetComponent<ConversationOption>());
    }
}

This assumes you have a reference to your ConversationOptionList component named conversationOptionList, and it has a List named options.