I am trying to make a custom inspector for my sequence class. The idea is to allow the user to configure various UnityEvents that get called at the start or the end of a sequence.
I want to have a collection of sequences in a ReorderableList so that it is highly configurable inside the inspector.
I am very close to a solution but i am fairly inexperienced with editor scripting. My scripts are almost working correctly. But I still need to solve the best way to dynamically adjust the vertical height of each item, in the DrawListItem method, and the total vertical height in the ElementHeight Method.
I am considering trying to deserialize the unity events so that i can use the GetPersistentEventCount method to get an idea of the required vertical height, but this seems like it is probably overkill. I suspect that there must be a simpler way to retrieve this data.
Currently when i add multiple items to the sequence I am getting what is pictured below, where the event fields overlap each other and the add/remove buttons are beneath the lower Unity Event.
Does anyone know the best way to resolve this?
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using System;
using UnityEngine.Events;
[CustomEditor(typeof(SequenceManager))]
public class SequenceManagerEditor : Editor
{
SerializedProperty Sequences;
ReorderableList list;
private void OnEnable()
{
Sequences = serializedObject.FindProperty("Sequences");
list = new ReorderableList(serializedObject, Sequences, true, true, true, true);
list.drawElementCallback = DrawListItems;
list.drawHeaderCallback = DrawHeader;
list.elementHeightCallback = ElementHeight;
}
//Draws the elements in the list
void DrawListItems(Rect rect, int index, bool isActive, bool isFocused)
{
SerializedProperty element = list.serializedProperty.GetArrayElementAtIndex(index);
////NAME
EditorGUI.LabelField(new Rect(
rect.x,
rect.y + EditorGUIUtility.standardVerticalSpacing,
50,
EditorGUIUtility.singleLineHeight), "Name");
EditorGUI.PropertyField(
new Rect(
rect.x + 50,
rect.y + EditorGUIUtility.standardVerticalSpacing,
rect.width - 50,
EditorGUIUtility.singleLineHeight),
element.FindPropertyRelative("Name"),
GUIContent.none
);
//ON INIT
EditorGUI.LabelField(new Rect(
rect.x,
rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3,
50,
EditorGUIUtility.singleLineHeight), "OnInit");
EditorGUI.PropertyField(new Rect(
rect.x + 50,
rect.y + EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing * 3,
rect.width - 50,
3 * rect.y + 5 * EditorGUIUtility.singleLineHeight),
element.FindPropertyRelative("OnInit"),
GUIContent.none);
//ON DONE
EditorGUI.LabelField(new Rect(
rect.x,
rect.y + 7 * EditorGUIUtility.singleLineHeight,
50,
EditorGUIUtility.singleLineHeight), "OnDone");
EditorGUI.PropertyField(
new Rect(
rect.x + 50,
rect.y + 7 * EditorGUIUtility.singleLineHeight,
rect.width - 50,
3 * rect.y + 12 * EditorGUIUtility.singleLineHeight),
element.FindPropertyRelative("OnDone"),
GUIContent.none);
SerializedProperty indexProperty = element.FindPropertyRelative("index");
indexProperty.intValue = index;
}
private float ElementHeight(int index)
{
return (13 * EditorGUIUtility.singleLineHeight);
}
//Draws the header
void DrawHeader(Rect rect)
{
string name = "Sequences";
EditorGUI.LabelField(rect, name);
}
public override void OnInspectorGUI()
{
//base.OnInspectorGUI();
serializedObject.Update();
this.list.DoLayoutList();
serializedObject.ApplyModifiedProperties();
}
}
for the sake of completeness, I've also added the Sequence class and SequenceManager class below.
using UnityEngine;
using UnityEditor;
using System;
using UnityEngine.Events;
[Serializable]
public class Sequence
{
public string Name;
public UnityEvent OnInit;
public UnityEvent OnDone;
private Module _module;
public int index;
private bool active;
//Called By The Sequence Manager At the start of a sequence
internal void Init(Module p_module)
{
Debug.Log($"sequence: {Name} with index: {index} has started");
active = true;
_module = p_module;
if(OnInit.HasNoListners())
{
Done();
}
else
{
OnInit.Invoke();
}
}
//Called Manually to Trigger the End of the Sequence
internal void Done()
{
if (!OnDone.HasNoListners())
{
OnDone.Invoke();
}
active = false;
Debug.Log($"sequence: {Name} with index: {index} is done");
_module.FinishedSequence(index);
}
//Check if active
internal bool GetActive()
{
return active;
}
}
using System;
namespace UnityEngine
{
[Serializable]
public class SequenceManager: MonoBehaviour
{
#region Properties
public Sequence[] Sequences;
#endregion
}
}