Сегодня я решил поделиться опытом расширения редактора 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);
}
}
12 комментариев на «Расширение редактора Unity»
Ваш отзыв
Вы должны войти, чтобы оставлять комментарии.
01 июля 2011 в 19:25
После добавления BezierEditor.cs в папку Editor кривая в окне сцены все равно не перемещается.
http://imageshack.us/photo/my-images/8/bezierbug.jpg/
26 Сен 2011 в 13:51
Даже не знаю в чем у вас проблема… не забыли ли Вы атрибут [CustomEditor(typeof(Bezier))]
06 Окт 2011 в 10:47
Спасибо, хороший материал.
Как раз возникла необходимость сделать свой редактор вэйпоинтов (чтобы связи между точками можно было назначить) – данный материал стал для меня точкой опоры в расширении редактора Unity
08 Дек 2011 в 18:47
спс за инфу
…
09 Дек 2011 в 10:40
спс
…
09 Дек 2011 в 11:59
спс за инфу
…
09 Дек 2011 в 15:10
сэнкс за инфу
…
10 Дек 2011 в 0:52
…
спасибо за инфу
…
10 Дек 2011 в 10:12
спс за инфу
…
23 Дек 2013 в 13:44
lewisohn@dora.progandist” rel=”nofollow”>.…
спс….
24 Авг 2014 в 16:15
spat@plead.remarque” rel=”nofollow”>.…
спс!!…
26 Авг 2014 в 10:44
nagel@snaked.transposition” rel=”nofollow”>.…
спс….