Buscar..


Sintaxis

  • [MenuItem (string itemName)]
  • [MenuItem (string itemName, bool isValidateFunction)]
  • [MenuItem (string itemName, bool isValidateFunction, int prioridad)]
  • [ContextMenu (nombre de cadena)]
  • [ContextMenuItem (nombre de cadena, función de cadena)]
  • [DrawGizmo (GizmoType gizmo)]
  • [DrawGizmo (GizmoType gizmo, Type drawnGizmoType)]

Parámetros

Parámetro Detalles
MenuCommand MenuCommand se usa para extraer el contexto de un MenuItem
MenuCommand.context El objeto que es el objetivo del comando de menú.
MenuCommand.userData Un int para pasar información personalizada a un elemento de menú

Inspector personalizado

El uso de un inspector personalizado le permite cambiar la forma en que se dibuja un script en el Inspector. A veces desea agregar información adicional en el inspector para su script que no es posible hacer con un cajón de propiedades personalizadas.

A continuación se muestra un ejemplo simple de un objeto personalizado que al usar un inspector personalizado puede mostrar información más útil.

using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif

public class InspectorExample : MonoBehaviour {

    public int Level;
    public float BaseDamage;

    public float DamageBonus {
        get {
            return Level / 100f * 50;
        }
    }

    public float ActualDamage {
        get {
            return BaseDamage + DamageBonus;
        }
    }
}

#if UNITY_EDITOR
[CustomEditor( typeof( InspectorExample ) )]
public class CustomInspector : Editor {

    public override void OnInspectorGUI() {
        base.OnInspectorGUI();

        var ie = (InspectorExample)target;

        EditorGUILayout.LabelField( "Damage Bonus", ie.DamageBonus.ToString() );
        EditorGUILayout.LabelField( "Actual Damage", ie.ActualDamage.ToString() );
    }
}
#endif

Primero definimos nuestro comportamiento personalizado con algunos campos.

public class InspectorExample : MonoBehaviour {
    public int Level;
    public float BaseDamage;
}

Los campos que se muestran arriba se dibujan automáticamente (sin el inspector personalizado) cuando está viendo el script en la ventana del Inspector.

public float DamageBonus {
    get {
        return Level / 100f * 50;
    }
}

public float ActualDamage {
    get {
        return BaseDamage + DamageBonus;
    }
}

Estas propiedades no son dibujadas automáticamente por Unity. Para mostrar estas propiedades en la vista Inspector, tenemos que utilizar nuestro Inspector personalizado.

Primero tenemos que definir a nuestro inspector personalizado como este.

[CustomEditor( typeof( InspectorExample ) )]
public class CustomInspector : Editor {

El inspector personalizado tiene que derivar del Editor y necesita el atributo CustomEditor . El parámetro del atributo es el tipo de objeto para el que debe utilizarse este inspector personalizado.

El siguiente es el método OnInspectorGUI. Este método se llama siempre que el script se muestra en la ventana del inspector.

public override void OnInspectorGUI() {
    base.OnInspectorGUI();
}

Hacemos una llamada a base.OnInspectorGUI () para permitir que Unity maneje los otros campos que están en el script. Si no lo llamáramos, tendríamos que hacer más trabajo nosotros mismos.

A continuación están nuestras propiedades personalizadas que queremos mostrar

var ie = (InspectorExample)target;

EditorGUILayout.LabelField( "Damage Bonus", ie.DamageBonus.ToString() );
EditorGUILayout.LabelField( "Actual Damage", ie.ActualDamage.ToString() );

Tenemos que crear una variable temporal que contenga el destino convertido en nuestro tipo personalizado (el destino está disponible porque derivamos del Editor).

A continuación, podemos decidir cómo dibujar nuestras propiedades, en este caso, dos campos de trabajo son suficientes, ya que solo queremos mostrar los valores y no poder editarlos.

Resultado

antes de

resultado antes

Después

resultado después

Cajón de propiedad personalizada

A veces, tiene objetos personalizados que contienen datos pero no se derivan de MonoBehaviour. Agregar estos objetos como un campo en una clase que es MonoBehaviour no tendrá ningún efecto visual a menos que escriba su propio cajón de propiedades personalizadas para el tipo de objeto.

A continuación se muestra un ejemplo simple de un objeto personalizado, agregado a MonoBehaviour, y un cajón de propiedades personalizadas para el objeto personalizado.

public enum Gender {
    Male,
    Female,
    Other
}

// Needs the Serializable attribute otherwise the CustomPropertyDrawer wont be used
[Serializable]
public class UserInfo {
    public string Name;
    public int Age;
    public Gender Gender;
}

// The class that you can attach to a GameObject
public class PropertyDrawerExample : MonoBehaviour {
    public UserInfo UInfo;
}

[CustomPropertyDrawer( typeof( UserInfo ) )]
public class UserInfoDrawer : PropertyDrawer {

    public override float GetPropertyHeight( SerializedProperty property, GUIContent label ) {
        // The 6 comes from extra spacing between the fields (2px each)
        return EditorGUIUtility.singleLineHeight * 4 + 6;
    }

    public override void OnGUI( Rect position, SerializedProperty property, GUIContent label ) {
        EditorGUI.BeginProperty( position, label, property );

        EditorGUI.LabelField( position, label );

        var nameRect = new Rect( position.x, position.y + 18, position.width, 16 );
        var ageRect = new Rect( position.x, position.y + 36, position.width, 16 );
        var genderRect = new Rect( position.x, position.y + 54, position.width, 16 );

        EditorGUI.indentLevel++;

        EditorGUI.PropertyField( nameRect, property.FindPropertyRelative( "Name" ) );
        EditorGUI.PropertyField( ageRect, property.FindPropertyRelative( "Age" ) );
        EditorGUI.PropertyField( genderRect, property.FindPropertyRelative( "Gender" ) );

        EditorGUI.indentLevel--;

        EditorGUI.EndProperty();
    }
}

En primer lugar definimos el objeto personalizado con todos sus requisitos. Solo una clase simple que describe a un usuario. Esta clase se usa en nuestra clase PropertyDrawerExample que podemos agregar a un GameObject.

public enum Gender {
    Male,
    Female,
    Other
}

[Serializable]
public class UserInfo {
    public string Name;
    public int Age;
    public Gender Gender;
}

public class PropertyDrawerExample : MonoBehaviour {
    public UserInfo UInfo;
}

La clase personalizada necesita el atributo Serializable; de ​​lo contrario, no se utilizará CustomPropertyDrawer

El siguiente es el CustomPropertyDrawer

Primero tenemos que definir una clase que derive de PropertyDrawer. La definición de clase también necesita el atributo CustomPropertyDrawer. El parámetro pasado es el tipo de objeto para el que desea utilizar este cajón.

[CustomPropertyDrawer( typeof( UserInfo ) )]
public class UserInfoDrawer : PropertyDrawer {

A continuación, anulamos la función GetPropertyHeight. Esto nos permite definir una altura personalizada para nuestra propiedad. En este caso, sabemos que nuestra propiedad tendrá cuatro partes: etiqueta, nombre, edad y género. Por lo tanto, usamos EditorGUIUtility.singleLineHeight * 4 , agregamos otros 6 píxeles porque queremos espaciar cada campo con dos píxeles entre ellos.

public override float GetPropertyHeight( SerializedProperty property, GUIContent label ) {
    return EditorGUIUtility.singleLineHeight * 4 + 6;
}

El siguiente es el método de OnGUI real. Comenzamos con EditorGUI.BeginProperty ([...]) y terminamos la función con EditorGUI.EndProperty () . Hacemos esto de modo que si esta propiedad formara parte de una prefabulación, la lógica de reemplazo de prefabricación real funcionaría para todo lo que se encuentre entre esos dos métodos.

public override void OnGUI( Rect position, SerializedProperty property, GUIContent label ) {
    EditorGUI.BeginProperty( position, label, property );

Después de eso mostramos una etiqueta que contiene el nombre del campo y ya definimos los rectángulos para nuestros campos.

EditorGUI.LabelField( position, label );

var nameRect = new Rect( position.x, position.y + 18, position.width, 16 );
var ageRect = new Rect( position.x, position.y + 36, position.width, 16 );
var genderRect = new Rect( position.x, position.y + 54, position.width, 16 );

Cada campo está espaciado por 16 + 2 píxeles y la altura es 16 (que es lo mismo que EditorGUIUtility.singleLineHeight)

A continuación, sangramos la interfaz de usuario con una pestaña para un diseño un poco más agradable, mostramos las propiedades, deshicimos la GUI y terminamos con EditorGUI.EndProperty .

EditorGUI.indentLevel++;

EditorGUI.PropertyField( nameRect, property.FindPropertyRelative( "Name" ) );
EditorGUI.PropertyField( ageRect, property.FindPropertyRelative( "Age" ) );
EditorGUI.PropertyField( genderRect, property.FindPropertyRelative( "Gender" ) );

EditorGUI.indentLevel--;

EditorGUI.EndProperty();

Mostramos los campos usando EditorGUI.PropertyField que requiere un rectángulo para la posición y una propiedad Serialized para que la propiedad se muestre. Adquirimos la propiedad llamando a FindPropertyRelative ("...") en la propiedad aprobada en la función OnGUI . Tenga en cuenta que estas propiedades no distinguen entre mayúsculas y minúsculas y no se pueden encontrar.

Para este ejemplo no estoy guardando el retorno de propiedades de property.FindPropertyRelative ("..."). Debe guardar estos en campos privados en la clase para evitar llamadas innecesarias

Resultado

antes de

Resultado antes

Después

Resultado después

Elementos de menú

Los elementos del menú son una excelente manera de agregar acciones personalizadas al editor. Puede agregar elementos de menú a la barra de menús, tenerlos como clics de contexto en componentes específicos, o incluso como clics de contexto en los campos de sus scripts.

A continuación se muestra un ejemplo de cómo puede aplicar elementos de menú.

public class MenuItemsExample : MonoBehaviour {

    [MenuItem( "Example/DoSomething %#&d" )]
    private static void DoSomething() {
        // Execute some code
    }

    [MenuItem( "Example/DoAnotherThing", true )]
    private static bool DoAnotherThingValidator() {
        return Selection.gameObjects.Length > 0;
    }

    [MenuItem( "Example/DoAnotherThing _PGUP", false )]
    private static void DoAnotherThing() {
        // Execute some code
    }

    [MenuItem( "Example/DoOne %a", false, 1 )]
    private static void DoOne() {
        // Execute some code
    }

    [MenuItem( "Example/DoTwo #b", false, 2 )]
    private static void DoTwo() {
        // Execute some code
    }

    [MenuItem( "Example/DoFurther &c", false, 13 )]
    private static void DoFurther() {
        // Execute some code
    }

    [MenuItem( "CONTEXT/Camera/DoCameraThing" )]
    private static void DoCameraThing( MenuCommand cmd ) {
        // Execute some code
    }

    [ContextMenu( "ContextSomething" )]
    private void ContentSomething() {
        // Execute some code
    }

    [ContextMenuItem( "Reset", "ResetDate" )]
    [ContextMenuItem( "Set to Now", "SetDateToNow" )]
    public string Date = "";

    public void ResetDate() {
        Date = "";
    }

    public void SetDateToNow() {
        Date = DateTime.Now.ToString();
    }
}

Que se parece a esto

menú de la barra de herramientas

Vamos a repasar el elemento básico del menú. Como puede ver a continuación, debe definir una función estática con un atributo MenuItem , que pasa una cadena como título para el elemento del menú. Puede poner su elemento de menú en varios niveles de profundidad agregando un / en el nombre.

[MenuItem( "Example/DoSomething %#&d" )]
private static void DoSomething() {
    // Execute some code
}

No puede tener un elemento de menú en el nivel superior. ¡Sus elementos de menú deben estar en un submenú!

Los caracteres especiales al final del nombre de MenuItem son para las teclas de acceso directo, no son un requisito.

Hay caracteres especiales que puedes usar para tus teclas de acceso directo, estos son:

  • % - Ctrl en Windows, Cmd en OS X
  • # - Cambio
  • & - Alt

Eso significa que el acceso directo % # & d significa ctrl + shift + alt + D en Windows, y cmd + shift + alt + D en OS X.

Si desea usar un atajo sin ninguna tecla especial, por ejemplo, solo la tecla 'D', puede anteponer el carácter _ (guión bajo) a la tecla de atajo que desea usar.

Hay algunas otras teclas especiales que son compatibles, que son:

  • IZQUIERDA, DERECHA, ARRIBA, ABAJO - para las teclas de flecha
  • F1..F12 - para las teclas de función
  • INICIO, FIN, PGUP, PGDN - para las teclas de navegación

Las teclas de acceso directo deben separarse de cualquier otro texto con un espacio

A continuación están los elementos del menú validador. Los elementos del menú del validador permiten que los elementos del menú se deshabiliten (en gris, no se puede hacer clic) cuando no se cumple la condición. Un ejemplo de esto podría ser que su elemento de menú actúa en la selección actual de GameObjects, que puede verificar en el elemento de menú del validador.

[MenuItem( "Example/DoAnotherThing", true )]
private static bool DoAnotherThingValidator() {
    return Selection.gameObjects.Length > 0;
}

[MenuItem( "Example/DoAnotherThing _PGUP", false )]
private static void DoAnotherThing() {
    // Execute some code
}

Para que un elemento del menú del validador funcione, necesita crear dos funciones estáticas, tanto con el atributo MenuItem como con el mismo nombre (la tecla de acceso directo no importa). La diferencia entre ellos es que los está marcando como una función de validación o no al pasar un parámetro booleano.

También puede definir el orden de los elementos del menú agregando una prioridad. La prioridad está definida por un entero que se pasa como el tercer parámetro. Cuanto más pequeño sea el número, más arriba en la lista, más grande será el número más bajo en la lista. Puede agregar un separador entre dos elementos del menú asegurándose de que haya al menos 10 dígitos entre la prioridad de los elementos del menú.

[MenuItem( "Example/DoOne %a", false, 1 )]
private static void DoOne() {
    // Execute some code
}

[MenuItem( "Example/DoTwo #b", false, 2 )]
private static void DoTwo() {
    // Execute some code
}

[MenuItem( "Example/DoFurther &c", false, 13 )]
private static void DoFurther() {
    // Execute some code
}

Si tiene una lista de menú que tiene una combinación de elementos priorizados y no priorizados, los no priorizados se separarán de los elementos priorizados.

Lo siguiente es agregar un elemento de menú al menú contextual de un componente ya existente. Debe iniciar el nombre de MenuItem con CONTEXT (distingue entre mayúsculas y minúsculas) y hacer que su función tome un parámetro MenuCommand.

El siguiente fragmento de código agregará un elemento del menú contextual al componente Cámara.

[MenuItem( "CONTEXT/Camera/DoCameraThing" )]
private static void DoCameraThing( MenuCommand cmd ) {
    // Execute some code
}

Que se parece a esto

Elemento del menú contextual de la cámara

El parámetro MenuCommand le da acceso al valor del componente y cualquier dato de usuario que se envíe con él.

También puede agregar un elemento del menú contextual a sus propios componentes utilizando el atributo ContextMenu. Este atributo solo toma un nombre, ninguna validación o prioridad, y tiene que ser parte de un método no estático.

[ContextMenu( "ContextSomething" )]
private void ContentSomething() {
    // Execute some code
}

Que se parece a esto

Elemento de menú contextual personalizado

También puede agregar elementos del menú de contexto a los campos en su propio componente. Estos elementos del menú aparecerán cuando haga clic contextual en el campo al que pertenecen y pueda ejecutar los métodos que haya definido en ese componente. De esta manera puede agregar, por ejemplo, valores predeterminados, o la fecha actual, como se muestra a continuación.

[ContextMenuItem( "Reset", "ResetDate" )]
[ContextMenuItem( "Set to Now", "SetDateToNow" )]
public string Date = "";

public void ResetDate() {
    Date = "";
}

public void SetDateToNow() {
    Date = DateTime.Now.ToString();
}

Que se parece a esto

introduzca la descripción de la imagen aquí

Gizmos

Los gizmos se utilizan para dibujar formas en la vista de escena. Puede utilizar estas formas para obtener información adicional sobre sus GameObjects, por ejemplo, el frustum que tienen o el rango de detección.

A continuación hay dos ejemplos de cómo hacer esto.

Ejemplo uno

Este ejemplo utiliza los métodos OnDrawGizmos y OnDrawGizmosSelected (magic).

public class GizmoExample : MonoBehaviour {

    public float GetDetectionRadius() {
        return 12.5f;
    }

    public float GetFOV() {
        return 25f;
    }

    public float GetMaxRange() {
        return 6.5f;
    }

    public float GetMinRange() {
        return 0;
    }

    public float GetAspect() {
        return 2.5f;
    }

    public void OnDrawGizmos() {
        var gizmoMatrix = Gizmos.matrix;
        var gizmoColor = Gizmos.color;

        Gizmos.matrix = Matrix4x4.TRS( transform.position, transform.rotation, transform.lossyScale );
        Gizmos.color = Color.red;
        Gizmos.DrawFrustum( Vector3.zero, GetFOV(), GetMaxRange(), GetMinRange(), GetAspect() );

        Gizmos.matrix = gizmoMatrix;
        Gizmos.color = gizmoColor;
    }

    public void OnDrawGizmosSelected() {
        Handles.DrawWireDisc( transform.position, Vector3.up, GetDetectionRadius() );
    }
}

En este ejemplo tenemos dos métodos para dibujar artilugios, uno que dibuja cuando el objeto está activo (OnDrawGizmos) y uno para cuando el objeto está seleccionado en la jerarquía (OnDrawGizmosSelected).

public void OnDrawGizmos() {
    var gizmoMatrix = Gizmos.matrix;
    var gizmoColor = Gizmos.color;

    Gizmos.matrix = Matrix4x4.TRS( transform.position, transform.rotation, transform.lossyScale );
    Gizmos.color = Color.red;
    Gizmos.DrawFrustum( Vector3.zero, GetFOV(), GetMaxRange(), GetMinRange(), GetAspect() );

    Gizmos.matrix = gizmoMatrix;
    Gizmos.color = gizmoColor;
}

Primero guardamos la matriz de gizmo y el color porque lo vamos a cambiar y queremos revertirlo cuando hayamos terminado para no afectar a ningún otro dibujo de gizmo.

A continuación, queremos dibujar el frustum que tiene nuestro objeto, sin embargo, necesitamos cambiar la matriz de Gizmos para que coincida con la posición, la rotación y la escala. También establecemos el color de Gizmos en rojo para enfatizar el frustum. Cuando se hace esto, podemos llamar a Gizmos.DrawFrustum para dibujar el frustum en la vista de escena.

Cuando hayamos terminado de dibujar lo que queremos dibujar, restablecemos la matriz y el color de Gizmos.

public void OnDrawGizmosSelected() {
    Handles.DrawWireDisc( transform.position, Vector3.up, GetDetectionRadius() );
}

También queremos dibujar un rango de detección cuando seleccionamos nuestro GameObject. Esto se hace a través de la clase Handles ya que la clase Gizmos no tiene ningún método para discos.

Usando esta forma de dibujar los resultados de gizmos en la salida que se muestra a continuación.

Ejemplo dos

Este ejemplo utiliza el atributo DrawGizmo .

public class GizmoDrawerExample {

    [DrawGizmo( GizmoType.Selected | GizmoType.NonSelected, typeof( GizmoExample ) )]
    public static void DrawGizmo( GizmoExample obj, GizmoType type ) {
        var gizmoMatrix = Gizmos.matrix;
        var gizmoColor = Gizmos.color;

        Gizmos.matrix = Matrix4x4.TRS( obj.transform.position, obj.transform.rotation, obj.transform.lossyScale );
        Gizmos.color = Color.red;
        Gizmos.DrawFrustum( Vector3.zero, obj.GetFOV(), obj.GetMaxRange(), obj.GetMinRange(), obj.GetAspect() );

        Gizmos.matrix = gizmoMatrix;
        Gizmos.color = gizmoColor;

        if ( ( type & GizmoType.Selected ) == GizmoType.Selected ) {
            Handles.DrawWireDisc( obj.transform.position, Vector3.up, obj.GetDetectionRadius() );
        }
    }
}

De esta manera le permite separar las llamadas de gizmo de su script. La mayoría de esto usa el mismo código que el otro ejemplo, excepto por dos cosas.

[DrawGizmo( GizmoType.Selected | GizmoType.NonSelected, typeof( GizmoExample ) )]
public static void DrawGizmo( GizmoExample obj, GizmoType type ) {

Debe usar el atributo DrawGizmo, que toma la enumeración GizmoType como primer parámetro y un Tipo como segundo parámetro. El Tipo debe ser el tipo que desea utilizar para dibujar el gizmo.

El método para dibujar el gizmo debe ser estático, público o no público, y se puede nombrar como se desee. El primer parámetro es el tipo, que debe coincidir con el tipo pasado como el segundo parámetro en el atributo, y el segundo parámetro es la enumeración GizmoType que describe el estado actual de su objeto.

if ( ( type & GizmoType.Selected ) == GizmoType.Selected ) {
    Handles.DrawWireDisc( obj.transform.position, Vector3.up, obj.GetDetectionRadius() );
}

La otra diferencia es que para verificar cuál es el GizmoType del objeto, debe hacer un AND en el parámetro y el tipo que desea.

Resultado

No seleccionado

ejemplo uno no seleccionado

Seleccionado

ejemplo uno seleccionado

Ventana del editor

¿Por qué una ventana de editor?

Como puede haber visto, puede hacer muchas cosas en un inspector personalizado (si no sabe qué es un inspector personalizado, consulte el ejemplo aquí: http://www.riptutorial.com/unity3d/topic/2506 / extender-el-editor . Pero en un momento es posible que desee implementar un panel de configuración o una paleta de recursos personalizada. En esos casos, va a utilizar una ventana de Editor . La interfaz de usuario de Unity se compone de Editor de Windows; puede abrirlos (generalmente a través de la barra superior), tabúelos, etc.

Crear un Editor de Windows básico

Ejemplo simple

Crear una ventana de editor personalizada es bastante simple. Todo lo que necesita hacer es extender la clase EditorWindow y usar los métodos Init () y OnGUI (). Aquí hay un ejemplo simple:

using UnityEngine;
using UnityEditor;

public class CustomWindow : EditorWindow
{
    // Add menu named "Custom Window" to the Window menu
    [MenuItem("Window/Custom Window")]
    static void Init()
    {
        // Get existing open window or if none, make a new one:
        CustomWindow window = (CustomWindow) EditorWindow.GetWindow(typeof(CustomWindow));
        window.Show();
    }

    void OnGUI()
    {
        GUILayout.Label("This is a custom Editor Window", EditorStyles.boldLabel);
    }
}

Los 3 puntos importantes son:

  1. No te olvides de extender EditorWindow
  2. Utilice el Init () como se proporciona en el ejemplo. EditorWindow.GetWindow está comprobando si ya se ha creado una CustomWindow. Si no, creará una nueva instancia. Al usar esto, se asegura de no tener varias instancias de su ventana al mismo tiempo
  3. Use OnGUI () como de costumbre para mostrar información en su ventana

El resultado final se verá así:

Simple Editor personalizado de Windows

Yendo mas profundo

Por supuesto, probablemente querrá administrar o modificar algunos activos usando esta ventana de Editor. Aquí hay un ejemplo que usa la clase de Selección (para obtener la Selección activa) y la modificación de las propiedades del activo seleccionado a través de SerializedObject y SerializedProperty .

    using System.Linq;
    using UnityEngine;
    using UnityEditor;
    
    public class CustomWindow : EditorWindow
    {
        private AnimationClip _animationClip;
        private SerializedObject _serializedClip;
        private SerializedProperty _events;
    
        private string _text = "Hello World";
    
        // Add menu named "Custom Window" to the Window menu
        [MenuItem("Window/Custom Window")]
        static void Init()
        {
            // Get existing open window or if none, make a new one:
            CustomWindow window = (CustomWindow) EditorWindow.GetWindow(typeof(CustomWindow));
            window.Show();
        }
    
        void OnGUI()
        {
            GUILayout.Label("This is a custom Editor Window", EditorStyles.boldLabel);
    
            // You can use EditorGUI, EditorGUILayout and GUILayout classes to display anything you want
            // A TextField example
            _text = EditorGUILayout.TextField("Text Field", _text);
    
            // Note that you can modify an asset or a gameobject using an EditorWindow. Here is a quick example with an AnimationClip asset
            // The _animationClip, _serializedClip and _events are set in OnSelectionChange()
    
            if (_animationClip == null || _serializedClip == null || _events == null) return;
    
            // We can modify our serializedClip like we would do in a Custom Inspector. For example we can grab its events and display their information
    
            GUILayout.Label(_animationClip.name, EditorStyles.boldLabel);
    
            for (var i = 0; i < _events.arraySize; i++)
            {
                EditorGUILayout.BeginVertical();
    
                EditorGUILayout.LabelField(
                    "Event : " + _events.GetArrayElementAtIndex(i).FindPropertyRelative("functionName").stringValue,
                    EditorStyles.boldLabel);
                EditorGUILayout.PropertyField(_events.GetArrayElementAtIndex(i).FindPropertyRelative("time"), true,
                    GUILayout.ExpandWidth(true));
                EditorGUILayout.PropertyField(_events.GetArrayElementAtIndex(i).FindPropertyRelative("functionName"),
                    true, GUILayout.ExpandWidth(true));
                EditorGUILayout.PropertyField(_events.GetArrayElementAtIndex(i).FindPropertyRelative("floatParameter"),
                    true, GUILayout.ExpandWidth(true));
                EditorGUILayout.PropertyField(_events.GetArrayElementAtIndex(i).FindPropertyRelative("intParameter"),
                    true, GUILayout.ExpandWidth(true));
                EditorGUILayout.PropertyField(
                    _events.GetArrayElementAtIndex(i).FindPropertyRelative("objectReferenceParameter"), true,
                    GUILayout.ExpandWidth(true));
    
                EditorGUILayout.Separator();
                EditorGUILayout.EndVertical();
            }
    
            // Of course we need to Apply the modified properties. We don't our changes won't be saved
            _serializedClip.ApplyModifiedProperties();
        }
    
        /// This Message is triggered when the user selection in the editor changes. That's when we should tell our Window to Repaint() if the user selected another AnimationClip
        private void OnSelectionChange()
        {
            _animationClip =
                Selection.GetFiltered(typeof(AnimationClip), SelectionMode.Assets).FirstOrDefault() as AnimationClip;
            if (_animationClip == null) return;
    
            _serializedClip = new SerializedObject(_animationClip);
            _events = _serializedClip.FindProperty("m_Events");
            Repaint();
        }
    }

Aquí está el resultado:
Editor personalizado ventana AnimationClip

Temas avanzados

Puede hacer algunas cosas realmente avanzadas en el editor, y la clase EditorWindow es perfecta para mostrar gran cantidad de información. La mayoría de los activos avanzados en Unity Asset Store (como NodeCanvas o PlayMaker) usan EditorWindow para mostrar vistas personalizadas.

Dibujando en el SceneView

Una cosa interesante que hacer con un EditorWindow es mostrar información directamente en su SceneView. De esta manera, puede crear un editor de mapas / mundos totalmente personalizado, por ejemplo, utilizando su EditorWindow personalizado como una paleta de activos y escuchando los clics en SceneView para crear una instancia de nuevos objetos. Aquí hay un ejemplo :

using UnityEngine;
using System;
using UnityEditor;

public class CustomWindow : EditorWindow {

    private enum Mode {
        View = 0,
        Paint = 1,
        Erase = 2
    }

    private Mode CurrentMode = Mode.View;

    [MenuItem ("Window/Custom Window")]
    static void Init () {
        // Get existing open window or if none, make a new one:
        CustomWindow window = (CustomWindow)EditorWindow.GetWindow (typeof (CustomWindow));
        window.Show();
    }

    void OnGUI () {
        GUILayout.Label ("This is a custom Editor Window", EditorStyles.boldLabel);
    }

    void OnEnable() {
        SceneView.onSceneGUIDelegate = SceneViewGUI;
        if (SceneView.lastActiveSceneView) SceneView.lastActiveSceneView.Repaint();
    }

    void SceneViewGUI(SceneView sceneView) {
        Handles.BeginGUI();
        // We define the toolbars' rects here
        var ToolBarRect = new Rect((SceneView.lastActiveSceneView.camera.pixelRect.width / 6), 10, (SceneView.lastActiveSceneView.camera.pixelRect.width * 4 / 6) , SceneView.lastActiveSceneView.camera.pixelRect.height / 5);
        GUILayout.BeginArea(ToolBarRect);
        GUILayout.BeginHorizontal();
        GUILayout.FlexibleSpace();
         CurrentMode = (Mode) GUILayout.Toolbar(
            (int) CurrentMode,
            Enum.GetNames(typeof(Mode)),
            GUILayout.Height(ToolBarRect.height));
        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();
        GUILayout.EndArea();
        Handles.EndGUI();
    }
}

Esto mostrará la barra de herramientas directamente en tu SceneView SceneView UI de EditorWindow

Aquí tiene un vistazo rápido de lo lejos que puede llegar:

Editor de mapas Editor de ventanas



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow