Сегодня я решил поделиться опытом расширения редактора Unity.

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

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

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

Имеется 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));
        }
    }
}

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

Второй шаг
Скрипты для редактора 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);
    }
 
}

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

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

Длина кривой в инспекторе:

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

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

В дополнение хочу привести реализацию на javascript:

Bezier.js

// опорные точки кривой
var P0 : Vector3 = Vector3.zero;
var P1 : Vector3 = Vector3(0, 0, 1);
var P2 : Vector3 = Vector3(1, 0, 1);
var P3 : Vector3 = Vector3(1, 0, 0);
 
// интерполяция по кривой используя формулу кривой Bezier третьего порядка
function Evaluate(t : float) : Vector3
{
    var t1 : float = 1 - t;
    return t1 * t1 * t1 * P0 + 3 * t * t1 * t1 * P1 +
            3 * t * t * t1 * P2 + t * t * t * P3;
}
 
// существует два события, которые можно использовать для отображения
// "не визуального" объекта - OnDrawGizmos и OnDrawGizmosSelected
// первый выполняется всегда, второй - когда объект выбран
// функции рисования находятся в классе Gizmos
function OnDrawGizmosSelected()
{
    // отобразим кривую как 50 сегментов
    Gizmos.color = Color.green;
    for (var i = 1; i < 50; i++)
    {
        var t : float = (i - 1f) / 49f;
        var t1 : float = i / 49f;
        Gizmos.DrawLine(Evaluate(t), Evaluate(t1));
    }
}

Editor/BezierEditor.js

@CustomEditor(Bezier)
class BezierEditor extends Editor
{
    // данная функция выполняет отрисовку инспектора компонента
    override function OnInspectorGUI()
    {
        // выполняем отрисовку инспектора по умолчанию
        DrawDefaultInspector();
 
        // ссылка на компонент
        var bezier : Bezier = target;
        if (bezier)
        {
            // вычисляем длину кривой так же по 50-ти отрезкам
            var length : float = 0;
            for (var i = 1; i < 50; i++)
            {
                var t : float = (i - 1f) / 49f;
                var t1 : float = i / 49f;
                length += (bezier.Evaluate(t) - bezier.Evaluate(t1)).magnitude;
            }
 
            // отображаем длину кривой в инспекторе
            GUILayout.Label(System.String.Format("Curve length: {0}", length));
        }
    }
 
    // отрисовка в сцене, здесь в отличии от компонента, где мы использовали
    // для отрисовки класс Gizmos используется клас Handles (манипуляторы)
    function OnSceneGUI()
    {
 
        var bezier : Bezier = target;
        if (bezier)
        {
            //Нарисуем линии манипуляторов
            Handles.DrawLine(bezier.P0, bezier.P1);
            Handles.DrawLine(bezier.P2, bezier.P3);
 
            // Для каждой контрольной точки создаемманипулятор в виде сферы
            var rot : Quaternion = Quaternion.identity;
            var size : float  = HandleUtility.GetHandleSize(bezier.P0) * 0.2;
            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);
    }
 
}
VN:F [1.9.3_1094]
Rating: 9.6/10 (23 votes cast)
VN:F [1.9.3_1094]
Rating: +11 (from 11 votes)
Расширение редактора Unity, 9.6 out of 10 based on 23 ratings

12 комментариев на «Расширение редактора Unity»

  1. AndreyMust19
    AndreyMust19 пишет:

    После добавления BezierEditor.cs в папку Editor кривая в окне сцены все равно не перемещается.
    http://imageshack.us/photo/my-images/8/bezierbug.jpg/

    VN:F [1.9.3_1094]
    Rating: 3.4/5 (5 votes cast)
    VN:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  2. PAX
    PAX пишет:

    Даже не знаю в чем у вас проблема… не забыли ли Вы атрибут [CustomEditor(typeof(Bezier))]

    VN:F [1.9.3_1094]
    Rating: 3.7/5 (3 votes cast)
    VN:F [1.9.3_1094]
    Rating: +1 (from 1 vote)
  3. Savalin
    Savalin пишет:

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

    VN:F [1.9.3_1094]
    Rating: 3.7/5 (3 votes cast)
    VN:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  4. alberto

    :x

    спс за инфу :idea:

    VA:F [1.9.3_1094]
    Rating: 3.0/5 (2 votes cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  5. terrance

    :oops:

    спс :grin:

    VA:F [1.9.3_1094]
    Rating: 1.0/5 (1 vote cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  6. willard

    :neutral:

    спс за инфу :P

    VA:F [1.9.3_1094]
    Rating: 1.0/5 (1 vote cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  7. tony

    :oops:

    сэнкс за инфу :razz:

    VA:F [1.9.3_1094]
    Rating: 1.0/5 (1 vote cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  8. steve

    :lol:

    спасибо за инфу :?:

    VA:F [1.9.3_1094]
    Rating: 1.0/5 (1 vote cast)
    VA:F [1.9.3_1094]
    Rating: +1 (from 1 vote)
  9. lawrence

    :o

    спс за инфу :P

    VA:F [1.9.3_1094]
    Rating: 3.0/5 (2 votes cast)
    VA:F [1.9.3_1094]
    Rating: -1 (from 1 vote)
  10. Armando

    lewisohn@dora.progandist” rel=”nofollow”>.…

    спс….

    VA:F [1.9.3_1094]
    Rating: 1.0/5 (1 vote cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  11. Don

    spat@plead.remarque” rel=”nofollow”>.…

    спс!!…

    VA:F [1.9.3_1094]
    Rating: 1.0/5 (1 vote cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)
  12. Chester

    nagel@snaked.transposition” rel=”nofollow”>.…

    спс….

    VA:F [1.9.3_1094]
    Rating: 1.0/5 (1 vote cast)
    VA:F [1.9.3_1094]
    Rating: 0 (from 0 votes)

Ваш отзыв

Вы должны войти, чтобы оставлять комментарии.



Страница 1 of 0