Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

426
Visualizações
Unity3D Display a UnityEvent Corrrectly in a ReorderableList

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?

enter image description here

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
    }
}
over 4 years ago · Santiago Trujillo
2 Respostas
Responde à pergunta

0

It's possible to access public fields of Sequence by casting the elements of serializedObject.targetObjects in your editor script. You can also use serializedObject.targetObject or Editor.target if you aren't using [CanEditMultipleObjects].

if(target is not SequenceManager manager)
{
    //target was not a SequenceManager, the manager variable will be null
    return;
}

and within ElementHeight():

var sequence = manager.Sequences[index];
var size = sequence.OnInit.GetPersistentEventCount()
         + sequence.OnDone.GetPersistentEventCount();

Edit - After trying out some things, I learned that you can get the array size through the serialized property. You can just use this in ElementHeight():

var element = list.serializedProperty.GetArrayElementAtIndex(index);
var size = element.FindPropertyRelative("OnInit.m_PersistentCalls.m_Calls").arraySize
         + element.FindPropertyRelative("OnDone.m_PersistentCalls.m_Calls").arraySize;
over 4 years ago · Santiago Trujillo Relatório

0

Instead of a hardcoded default element height of

X * EditorGUIUtility.singleLineHeight

you should rather use EditorUtility.GetPropertyHeight and get the actual height of the properties like

private float ElementHeight(int index)
{
    var property = list.serializedProperty.GetArrayElementAtIndex(index);
    return EditorUtility.GetPropertyHeight(property);
}

This returns the height the property would require using it's default property drawer - the one that is applied using PropertyField which you do ;)


Besides that: What is the purpose of your custom editor exactly?

Since Unity 2020 the default drawer for lists and arrays is the ReorderableList anyway so to be honest I don't really see your custom editor add anything that wouldn't be the default Inspector anyway :)

This is how it looks like without your custom editor:

enter image description here

If you rather want to change how a Sequence is drawn in the Inspector in general, you would be better implementing a custom PropertyDrawer instead and not have to deal with the list at all.

And the drawer dealing with the index is btw dangerous / not reliable!

Currently your index is only applied if this is actually opened in the Inspector and not in Debug mode. What if later you want to modify this via script?

In my opinion it would be better if your SequenceManager rather simply passes in the index into the Init method if it is really needed for anything except the log ;)


Also be very careful: In your Sequence script you have

using UnityEditor;

be aware that this namespace is only available within the Unity editor itself and completely stripped of during the build.

=> You want to make sure that nothing of this namespace is trying to be used in a built application so you will have to wrap any references to this namespace in according pre-processor tags like e.g.

#if UNITY_EDITOR
using UnityEditor;
#endif

and the same for any code related to this namespace (see also Platform Dependent Compilation)

over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda