Показать сообщение отдельно
Старый 16.01.2011, 14:06   #1
pax
Unity/C# кодер
 
Аватар для pax
 
Регистрация: 03.10.2005
Адрес: Россия, Рязань
Сообщений: 7,568
Написано 3,006 полезных сообщений
(для 5,323 пользователей)
Расширение редактора Unity

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

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

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

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

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

Первый шаг.
Подготовим компонент, выполняющий расчет и отображение кривой когда объект выбран:
Bezier.cs
using UnityEngine;

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

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

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

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


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


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

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


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


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



Желаю всем удачи в исследовании Unity!
(Offline)
 
Ответить с цитированием
Эти 11 пользователя(ей) сказали Спасибо pax за это полезное сообщение:
Amatsu (21.11.2011), baton4ik (16.01.2011), Fatalix3d (23.02.2011), ffinder (17.01.2011), FireOwl (15.01.2012), Harter (15.01.2012), HolyDel (16.01.2011), Lestar (28.01.2012), maxturbo (09.03.2011), Nuprahtor (16.01.2011), Randomize (11.03.2012)