Поиск…


Синтаксис

  • [MenuItem (string itemName)]
  • [MenuItem (string itemName, bool isValidateFunction)]
  • [MenuItem (string itemName, bool isValidateFunction, int priority)]
  • [ContextMenu (имя строки)]
  • [ContextMenuItem (имя строки, строковая функция)]
  • [DrawGizmo (GizmoType gizmo)]
  • [DrawGizmo (GizmoType gizmo, Тип drawGizmoType)]

параметры

параметр подробности
MenuCommand MenuCommand используется для извлечения контекста для MenuItem
MenuCommand.context Объект, являющийся целью команды меню
MenuCommand.userData Int для передачи пользовательской информации в пункт меню

Пользовательский инспектор

Использование пользовательского инспектора позволяет изменить способ рисования сценария в инспекторе. Иногда вы хотите добавить дополнительную информацию в инспекторе для своего скрипта, что невозможно сделать с помощью специализированного ящика свойств.

Ниже приведен простой пример пользовательского объекта, который с помощью пользовательского инспектора может отображать более полезную информацию.

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

Сначала мы определяем наше поведение с некоторыми полями

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;
    }
}

Эти свойства автоматически не создаются Unity. Чтобы показать эти свойства в представлении «Инспектор», мы должны использовать наш пользовательский инспектор.

Сначала мы должны определить наш пользовательский инспектор, как это

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

Пользовательский инспектор должен получить от редактора и нуждается в атрибуте CustomEditor . Параметр атрибута - это тип объекта, для которого должен использоваться пользовательский инспектор.

Далее следует метод OnInspectorGUI. Этот метод вызывается, когда скрипт отображается в окне инспектора.

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

Мы вызываем base.OnInspectorGUI (), чтобы позволить Unity обрабатывать другие поля, которые находятся в скрипте. Если бы мы не назвали это, нам пришлось бы делать больше работы самостоятельно.

Ниже приведены наши пользовательские свойства, которые мы хотим показать

var ie = (InspectorExample)target;

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

Мы должны создать временную переменную, которая содержит цель, выбранную для нашего пользовательского типа (цель доступна, потому что мы получаем редактор).

Затем мы можем решить, как нарисовать наши свойства, в этом случае достаточно двух лабораторных окон, так как мы просто хотим показать значения и не в состоянии их редактировать.

Результат

До

результат до

После

результат после

Пользовательский ящик свойств

Иногда у вас есть пользовательские объекты, которые содержат данные, но не выводятся из MonoBehaviour. Добавление этих объектов в качестве поля в классе, который является MonoBehaviour, не будет иметь визуального эффекта, если вы не напишите свой собственный пользовательский ящик свойств для типа объекта.

Ниже приведен простой пример пользовательского объекта, добавленного в MonoBehaviour, и пользовательский ящик свойств для настраиваемого объекта.

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();
    }
}

Сначала мы определяем пользовательский объект со всеми его требованиями. Просто простой класс, описывающий пользователя. Этот класс используется в нашем классе PropertyDrawerExample, который мы можем добавить в 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;
}

Пользовательский класс нуждается в атрибуте Serializable, иначе CustomPropertyDrawer не будет использоваться

Далее следует CustomPropertyDrawer

Сначала мы должны определить класс, который происходит из PropertyDrawer. Для определения класса также требуется атрибут CustomPropertyDrawer. Прошедший параметр - это тип объекта, для которого вы хотите использовать этот ящик.

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

Затем мы переопределим функцию GetPropertyHeight. Это позволяет нам определить пользовательскую высоту для нашего свойства. В этом случае мы знаем, что у нашего имущества будет четыре части: ярлык, имя, возраст и пол. Поэтому мы используем EditorGUIUtility.singleLineHeight * 4 , добавляем еще 6 пикселей, потому что мы хотим разместить каждое поле с двумя пикселями между ними.

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

Далее - это метод OnGUI. Мы начинаем с EditorGUI.BeginProperty ([...]) и заканчиваем функцию EditorGUI.EndProperty () . Мы делаем это так, чтобы, если бы это свойство было частью сборника, фактическая предикатная логика prefab будет работать для всего между этими двумя методами.

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 );

Каждое поле разнесено на 16 + 2 пикселя, а высота равна 16 (это то же самое, что и EditorGUIUtility.singleLineHeight)

Затем мы отступаем от пользовательского интерфейса с одной вкладкой для более красивого макета, отображаем свойства, отступаем от графического интерфейса и заканчиваем 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();

Мы показываем поля с помощью EditorGUI.PropertyField, для которого требуется прямоугольник для позиции и SerializedProperty для отображаемого свойства. Мы приобретаем свойство, вызывая FindPropertyRelative ("...") для свойства, переданного в функции OnGUI . Обратите внимание, что это чувствительные к регистру и не публичные свойства не могут быть найдены!

В этом примере я не сохраняю свойства return from property.FindPropertyRelative ("..."). Вы должны сохранить их в закрытых полях в классе, чтобы предотвратить ненужные вызовы

Результат

До

Результат перед

После

Результат после

Пункты меню

Элементы меню - отличный способ добавить пользовательские действия в редактор. Вы можете добавлять элементы меню в панель меню, использовать их как контекстные клики для определенных компонентов или даже в контекстных кликах по полям ваших сценариев.

Ниже приведен пример того, как вы можете применять пункты меню.

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();
    }
}

Что выглядит так

меню панели инструментов

Перейдем к основному пункту меню. Как вы можете видеть ниже, вам нужно определить статическую функцию с атрибутом MenuItem , который вы передаете в качестве заголовка для элемента меню. Вы можете поместить свой пункт меню на несколько уровней, добавив a / в имя.

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

У вас не может быть пункта меню на верхнем уровне. Ваши пункты меню должны находиться в подменю!

Специальные символы в конце имени MenuItem предназначены для сочетаний клавиш, это не является обязательным требованием.

Существуют специальные символы, которые вы можете использовать для ваших сочетаний клавиш:

  • % - Ctrl на Windows, Cmd на OS X
  • # - Сдвиг
  • & - Alt

Это означает, что ярлык % # & d означает ctrl + shift + alt + D в Windows и cmd + shift + alt + D в OS X.

Если вы хотите использовать ярлык без каких-либо специальных клавиш, так, например, только клавишу «D», вы можете добавить символ _ (подчеркивание) к клавише быстрого доступа, которую вы хотите использовать.

Существуют и другие специальные клавиши, которые поддерживаются:

  • LEFT, RIGHT, UP, DOWN - для клавиш со стрелками
  • F1..F12 - для функциональных клавиш
  • HOME, END, PGUP, PGDN - для клавиш навигации

Клавиши быстрого вызова должны быть отделены от любого другого текста пространством

Далее перечислены пункты меню валидатора. Элементы меню «Валидатор» позволяют отключать пункты меню (недоступно, не кликается), когда условие не выполняется. Примером этого может быть то, что ваш пункт меню действует на текущий выбор GameObjects, который вы можете проверить в элементе меню проверки.

[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, так и с одним и тем же именем (клавиша быстрого доступа не имеет значения). Разница между ними заключается в том, что вы отмечаете их как функцию валидатора или нет, передавая логический параметр.

Вы также можете определить порядок пунктов меню, добавив приоритет. Приоритет определяется целым числом, которое вы передаете в качестве третьего параметра. Чем меньше число, тем выше число в списке, тем больше число ниже в списке. Вы можете добавить разделитель между двумя пунктами меню, убедившись, что между приоритетом пунктов меню находится не менее 10 цифр.

[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 (с учетом регистра), и ваша функция принимает параметр MenuCommand.

Следующий фрагмент добавит элемент контекстного меню в компонент «Камера».

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

Что выглядит так

Пункт контекстного меню камеры

Параметр MenuCommand предоставляет вам доступ к значению компонента и любым пользовательским данным, которые отправляются с ним.

Вы также можете добавить элемент контекстного меню к своим собственным компонентам, используя атрибут ContextMenu. Этот атрибут принимает только имя, валидность или приоритет и должен быть частью нестатического метода.

[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();
}

Что выглядит так

введите описание изображения здесь

Gizmos

Gizmos используются для рисования фигур в сцене. Вы можете использовать эти фигуры для получения дополнительной информации о ваших GameObjects, например, усеченной косой или диапазона обнаружения.

Ниже приведены два примера, как это сделать.

Пример 1

В этом примере используются методы OnDrawGizmos и OnDrawGizmosSelected (магия).

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() );
    }
}

В этом примере у нас есть два метода рисования гизмосов: тот, который рисует, когда объект активен (OnDrawGizmos), и один, когда объект выбран в иерархии (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;
}

Сначала мы сохраняем матрицу и цвет gizmo, потому что мы собираемся изменить ее и хотим вернуть ее обратно, когда мы закончим, чтобы не повлиять на какой-либо другой рисунок гизмо.

Затем мы хотим нарисовать усечку, что наш объект, однако, нам нужно изменить матрицу Gizmos, чтобы она соответствовала положению, вращению и масштабу. Мы также устанавливаем цвет Gizmos на красный, чтобы подчеркнуть усечение. Когда это будет сделано, мы можем вызвать Gizmos.DrawFrustum, чтобы нарисовать усечку в сцене.

Когда мы закончим рисовать то, что хотим рисовать, мы возвращаем матрицу и цвет Gizmos.

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

Мы также хотим нарисовать диапазон обнаружения, когда мы выберем наш GameObject. Это делается с помощью класса Handles , так как класс вещицы не имеет каких - либо методов для дисков.

Использование этой формы рисования gizmos приводит к результату, показанному ниже.

Пример два

В этом примере используется атрибут 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() );
        }
    }
}

Этот способ позволяет отделить вызовы gizmo от вашего скрипта. Большинство из них использует тот же код, что и другой пример, за исключением двух вещей.

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

Вам нужно использовать атрибут DrawGizmo, который переводит перечисление GizmoType в качестве первого параметра и Type как второй параметр. Тип должен быть типом, который вы хотите использовать для рисования гизмо.

Метод рисования gizmo должен быть статичным, общедоступным или непубличным, и его можно назвать любым, что вы хотите. Первым параметром является тип, который должен соответствовать типу, переданному в качестве второго параметра в атрибуте, а вторым параметром является перечисление GizmoType, которое описывает текущее состояние вашего объекта.

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

Другое отличие состоит в том, что для проверки того, что такое объект GizmoType, вам нужно выполнить AND и проверить параметр и тип, который вы хотите.

Результат

Не выбран

пример не выбран

выбранный

пример одного выбранного

Окно редактора

Почему окно редактора?

Как вы могли видеть, вы можете делать много вещей в обычном инспекторе (если вы не знаете, что такое пользовательский инспектор, посмотрите пример здесь: http://www.riptutorial.com/unity3d/topic/2506 / extension-the-editor . Но в какой-то момент вы можете захотеть реализовать панель конфигурации или настраиваемую палитру свойств . В этих случаях вы будете использовать редактор WindowWindow . Пользовательский интерфейс Unity состоит из редактора Windows, вы можете открыть их (обычно через верхний бар), вставлять их и т. д.

Создайте базовый редактор WindowWindow

Простой пример

Создание пользовательского окна редактора довольно просто. Все, что вам нужно сделать, это расширить класс EditorWindow и использовать методы Init () и OnGUI (). Вот простой пример:

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);
    }
}

3 важных момента:

  1. Не забудьте расширить EditorWindow
  2. Используйте Init (), как показано в примере. EditorWindow.GetWindow проверяет, уже создан ли CustomWindow. Если нет, он создаст новый экземпляр. Используя это, вы убедитесь, что у вас нет нескольких экземпляров вашего окна одновременно
  3. Использовать OnGUI (), как обычно, для отображения информации в вашем окне

Окончательный результат будет выглядеть так:

Простой пользовательский редакторWindow

Идти глубже

Конечно, вы, вероятно, захотите управлять или модифицировать некоторые активы, используя этот редактор. Ниже приведен пример использования класса Selection (для получения активного выбора) и изменения выбранных свойств актива через SerializedObject и 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();
        }
    }

Вот результат:
Окно пользовательского редактора AnimationClip

Расширенные темы

Вы можете сделать некоторые действительно продвинутые вещи в редакторе, а класс EditorWindow идеально подходит для отображения большого объема информации. Большинство продвинутых ресурсов в Unity Asset Store (например, NodeCanvas или PlayMaker) используют EditorWindow для отображения пользовательских представлений.

Рисование в SceneView

Одна интересная вещь, связанная с EditorWindow, заключается в отображении информации непосредственно в вашем SceneView. Таким образом, вы можете создать полностью настроенный редактор карт / мира, например, используя свой собственный EditorWindow в качестве палитры свойств и прослушивание кликов в SceneView для создания новых объектов. Вот пример:

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();
    }
}

Это отобразит панель инструментов непосредственно в вашем SceneView Пользовательский интерфейс SceneView от EditorWindow

Вот краткий обзор того, как далеко вы можете пойти:

Редактор редактора редакторовWindow



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow