Статьи:Расширение редактора

Материал из Blitz3D to Unity3D Wiki Project
Перейти к: навигация, поиск

Что-то нет настроя на серьезное программирование сегодня... так что решил поделиться опытом расширения редактора Unity.

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

Теория по кривым Безье находится тут

А нам понадобится одна формула:

345cb15e836677e949b72ace996ac052.png

Выглядит эта кривая таким вот образом:

300px-Bezier_curve.svg.png

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


Первый шаг.

Подготовим компонент, выполняющий расчет и отображение кривой когда объект выбран:

Bezier.cs

using UnityEngine;

public class Bezier : MonoBehaviour
{
    // опорные точки кривой
    public Vector3 P0 = Vector3.zero;
    public Vector3 P1 = new Vector3(0, 0, 1);
    public Vector3 P2 = new Vector3(1, 0, 1);
    public Vector3 P3 = new Vector3(1, 0, 0);

    // интерполяция по кривой используя формулу кривой Bezier третьего порядка
    public Vector3 Evaluate(float t)
    {
        float t1 = 1 - t;
        return t1 * t1 * t1 * P0 + 3 * t * t1 * t1 * P1 +
               3 * t * t * t1 * P2 + t * t * t * P3;
    }

    // существует два события, которые можно использовать для отображения
    // "не визуального" объекта - OnDrawGizmos и OnDrawGizmosSelected
    // первый выполняется всегда, второй - когда объект выбран
    // функции рисования находятся в классе Gizmos
    public void OnDrawGizmosSelected()
    {
        // отобразим кривую как 50 сегментов
        Gizmos.color = Color.green;
        for (int i = 1; i < 50; i++)
        {
            float t = (i - 1f) / 49f;
            float t1 = i / 49f;
            Gizmos.DrawLine(Evaluate(t), Evaluate(t1));
        }
    }
}

Добавив созданный компонент какому-либо объекту сцены и выбрав его мы увидим результат:

ffe88995d3058b946fb103841c738847.png


Второй шаг


Скрипты для редактора Unity должны быть помещены в отдельную папку с названием Editor. Где создавать такую папку выбирайте сами. Папок с именем Editor может быть сколько угодно в проекте. При компиляции проекта Unity создает отдельную сборку для Editor-скриптов и они не попадут в построенное приложение.

Итак что мы хотим добавить нашему компоненту, чтобы работа с ним была удобнее? Мне на ум приходят следующие возможности:

  1. Перемещать точки кривой в сцене.
  2. Отобразить дополнительно длину кривой во время редактирования в инспекторе.

Чтобы создать расширение компонента, надо создать класс, унаследованный от класса Editor и добавить ему атрибут CustomEditor в котором указать тип нашего компонента. Все эти классы находятся в пространстве имен UnityEditor.

Для того чтобы добавить функционал в сцену мы воспользуемся событием OnSceneGUI, а для отображения длины кривой - OnInspectorGUI (эта функция является перегружаемой):


Editor/BezierEditor.cs

using UnityEngine;
using UnityEditor;

[CustomEditor(typeof(Bezier))]
public class BezierEditor : Editor
{
    // данная функция выполняет отрисовку инспектора компонента
    public override void OnInspectorGUI()
    {
        // выполняем отрисовку инспектора по умолчанию
        DrawDefaultInspector();

        // ссылка на компонент
        Bezier bezier = target as Bezier;
        if (bezier)
        {
            // вычисляем длину кривой так же по 50-ти отрезкам
            float length = 0;
            for (int i = 1; i < 50; i++)
            {
                float t = (i - 1f) / 49f;
                float t1 = i / 49f;
                length += (bezier.Evaluate(t) - bezier.Evaluate(t1)).magnitude;
            }

            // отображаем длину кривой в инспекторе
            GUILayout.Label(string.Format("Curve length: {0}", length));
        }
    }

    // отрисовка в сцене, здесь в отличии от компонента, где мы использовали
    // для отрисовки класс Gizmos используется клас Handles (манипуляторы)
    public void OnSceneGUI()
    {
        
        Bezier bezier = target as Bezier;
        if (bezier)
        {
            //Нарисуем линии манипуляторов
            Handles.DrawLine(bezier.P0, bezier.P1);
            Handles.DrawLine(bezier.P2, bezier.P3);

            // Для каждой контрольной точки создаем манипулятор в виде сферы
            Quaternion rot = Quaternion.identity;
            float size = HandleUtility.GetHandleSize(bezier.P0) * 0.2f;
            bezier.P0 = Handles.FreeMoveHandle(bezier.P0, rot, size, Vector3.zero, Handles.SphereCap);
            bezier.P1 = Handles.FreeMoveHandle(bezier.P1, rot, size, Vector3.zero, Handles.SphereCap);
            bezier.P2 = Handles.FreeMoveHandle(bezier.P2, rot, size, Vector3.zero, Handles.SphereCap);
            bezier.P3 = Handles.FreeMoveHandle(bezier.P3, rot, size, Vector3.zero, Handles.SphereCap);
        }


        // если мы двигали контрольные точки, то мы должны указать редактору, 
        // что объект изменился (стал "грязным")
        if (GUI.changed)
            EditorUtility.SetDirty(target);
    }

}

Вот теперь мы должны быть довольны результатом.

Манипуляторы в сцене: 4425108462e573d85350ad7ae8258262.png

Длина кривой в инспекторе: 4e8d9d8a033fed19e66498c330ac5739.png

Конечная структура проекта:

d06d192b590b90c48c285abc417c80cf.png


Желаю всем удачи в исследовании Unity!