You can use a PropertyDrawer to control how a field is rendered, based on its type or attributes you've attached to it. This lets you target your changes to individual fields, wherever they might show up in a bigger nested set of types, without writing a custom inspector to handle drawing the whole hierarchy.
Here, we could define a [CharacterName] attribute that serves to mark fields we want to treat as character names. We'll also give it a static variable to keep track of which names should be options for the current batch of fields it's drawing.
using UnityEngine;
public class CharacterNameAttribute : PropertyAttribute
{
public static string[] CurrentOptions;
}
Next, we define a custom property drawer (in an Editor folder) to draw fields marked with this attribute as a dropdown, using these options.
using UnityEngine;
using UnityEditor;
[CustomPropertyDrawer(typeof(CharacterNameAttribute))]
public class CharacterNameDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
if (property.propertyType != SerializedPropertyType.String) {
EditorGUI.LabelField(position,
"[CharacterName] can only be used on strings.",
(GUIStyle)"ErrorStyle");
return;
}
var names = CharacterNameAttribute.CurrentOptions;
if (names == null || names.Length == 0) {
EditorGUI.LabelField(position,
"No [CharacterName] options available.",
(GUIStyle)"ErrorStyle");
return;
}
int index = Mathf.Max(0, System.Array.IndexOf(names, property.stringValue));
index = EditorGUI.Popup(position, property.displayName, index, names);
property.stringValue = names[index];
}
}
Lastly, we need to somehow populate the list of items that are valid for the current entry that we're drawing. Let's say you have a Dialogue script that looks something like this...
(Note that we've used our shiny new [CharacterName] attribute to mark our speaker name string)
using UnityEngine;
public class DialogueExample : MonoBehaviour
{
[System.Serializable]
public struct DialogueLine {
[CharacterName]
public string speaker;
public Sprite expression;
[TextArea(1, 4)]
public string text;
}
[System.Serializable]
public struct DialogueSection {
public DialogueLine[] lines;
}
public string[] characterNames;
public DialogueSection[] dialogues;
}
We can use a custom editor for this type (again in an Editor folder) to set the available character names, then let Unity handle drawing the standard inspector fields for all the contents of this instance:
using UnityEditor;
[CustomEditor(typeof(DialogueExample))]
public class DialogueExampleEditor : Editor
{
public override void OnInspectorGUI() {
// Set the names we're allowed to use in this dialogue.
var dialogue = (DialogueExample)target;
CharacterNameAttribute.CurrentOptions = dialogue.characterNames;
// Ask Unity to draw all our inspector fields, handle saving/undo/etc...
DrawDefaultInspector();
// Clean up after ourselves, so we don't leak into the next use of [CharacterName]
CharacterNameAttribute.CurrentOptions = null;
}
}
All told, we get an inspector that looks like this:
enums
to be displayed, have you tried taking advantage of that in your custom inspector? \$\endgroup\$