Извините, ничего не найдено.

Не расстраивайся! Лучше выпей чайку!
Регистрация
Справка
Календарь

Вернуться   forum.boolean.name > Программирование игр для компьютеров > Unity > Уроки

Ответ
 
Опции темы
Старый 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)
Старый 16.01.2011, 15:06   #2
pax
Unity/C# кодер
 
Аватар для pax
 
Регистрация: 03.10.2005
Адрес: Россия, Рязань
Сообщений: 7,568
Написано 3,006 полезных сообщений
(для 5,323 пользователей)
Ответ: Расширение редактора Unity

И на javascript:
Bezier.js
// опорные точки кривой
var P0 Vector3 Vector3.zero;
var 
P1 Vector3 Vector3(001);
var 
P2 Vector3 Vector3(101);
var 
P3 Vector3 Vector3(100);

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

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

            
// Для каждой контрольной точки создаемманипулятор в виде сферы
            
var rot Quaternion Quaternion.identity;
            var 
size float  HandleUtility.GetHandleSize(bezier.P0) * 0.2;
            
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);
    }


(Offline)
 
Ответить с цитированием
Эти 7 пользователя(ей) сказали Спасибо pax за это полезное сообщение:
Amatsu (21.11.2011), Fatalix3d (23.02.2011), Harter (15.01.2012), Illidan (19.01.2011), Lestar (28.01.2012), maxturbo (09.03.2011), Nuprahtor (16.01.2011)
Старый 16.01.2011, 17:33   #3
pax
Unity/C# кодер
 
Аватар для pax
 
Регистрация: 03.10.2005
Адрес: Россия, Рязань
Сообщений: 7,568
Написано 3,006 полезных сообщений
(для 5,323 пользователей)
Ответ: Расширение редактора Unity

Продолжаем расширять редактор.

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

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

Итак что мы должны сделать, чтобы создать собственное окошко:
  1. Создать класс унаследованный от класса EditorWindow, который находится в пространстве имен UnityEditor.
  2. Добавить статический метод создания окна.
  3. Пометить этот метод атрибутом MenuItem, чтобы зарегистрировать его в контекстном меню редактора.
  4. Реализовать у класса метод OnGUI, выполняющий отрисовку содержимого.
  5. Не забыть положить свой созданный класс в папку с названием Editor, т.к. это расширение редактора.

Итак реализация:
LookAtWindow.cs
using UnityEngine;
using UnityEditor;

public class 
LookAtWindow EditorWindow
{

    
// Регистрируем пункт меню и функцию, выполняющую открытие окна
    
[MenuItem("Window/Look At Window")]
    public static 
void CreateWindow()
    {
        
LookAtWindow window GetWindow<LookAtWindow>();
        
window.title "Look At Window";
    }

    
// объект для поворота
    
public Transform source;
    
// цель
    
public Transform target;

    
// Функция отрисовки окна
    
public void OnGUI()
    {
        
// поле для указания исходного объекта
        
GUILayout.BeginHorizontal();
        
GUILayout.Label("Source:"GUILayout.Width(120));
        
source = (Transform)EditorGUILayout.ObjectField(sourcetypeof(Transform));
        
GUILayout.EndHorizontal();

        
// поле для указания цели
        
GUILayout.BeginHorizontal();
        
GUILayout.Label("Target:"GUILayout.Width(120));
        
target = (Transform)EditorGUILayout.ObjectField(targettypeof(Transform));
        
GUILayout.EndHorizontal();

        
// запоминаем текущий цвет GUI для восстановления
        
Color oldColor GUI.color;

        
// проверяем наричие объекта для поворота и цели
        
bool flag false;
        if (
source == null || target == null)
        {
            
GUI.color Color.red;
            
GUILayout.Label("Selcect Source and Target!");
            
flag true;
            
GUI.color oldColor;
        }

        
// если цель является объектом
        
if ((source == target) && !flag)
        {
            
GUI.color Color.red;
            
GUILayout.Label("Source equals Target!");
            
GUI.color oldColor;
        }

        
// Делаем кнопку неактивной если не выполняется условие
        
GUI.enabled = !flag && (source != target);

        
// свободное место
        
GUILayout.FlexibleSpace();

        
// кнопка, выполняющая действие
        
if (GUILayout.Button("Look At"))
        {
            
source.LookAt(target);
        }

        
// снова активируем GUI
        
GUI.enabled true;
        
GUILayout.Space(8);

    }

Результаты.
Состав проекта:


Контекстное меню:


Само окно встроенное в оболочку:


Вот в принципе и все, о чем хотел рассказать. Удачи!
(Offline)
 
Ответить с цитированием
Эти 6 пользователя(ей) сказали Спасибо pax за это полезное сообщение:
Amatsu (21.11.2011), Fatalix3d (23.02.2011), Harter (15.01.2012), Illidan (19.01.2011), maxturbo (09.03.2011), Nuprahtor (16.01.2011)
Старый 17.01.2011, 00:27   #4
ffinder
Дэвелопер
 
Аватар для ffinder
 
Регистрация: 10.09.2007
Сообщений: 1,442
Написано 793 полезных сообщений
(для 1,460 пользователей)
Ответ: Расширение редактора Unity

Ну вот, отличный годный урок.
Прочитал с большим удовольствием.

Есть пара мелких моментов, на которые еще стоит обратить внимание.
1) target необходимо явно приводить к типу объекта, который мы хотим "инспектировать". По умолчанию он имеет тип object.
2) прекратить делать версии скриптов для UnityScript, чтобы не провоцировать людей изучать этот появившийся по недоразумению язык.
(Offline)
 
Ответить с цитированием
Эти 4 пользователя(ей) сказали Спасибо ffinder за это полезное сообщение:
Harter (15.01.2012), HolyDel (24.11.2011), Illidan (19.01.2011), pax (17.01.2011)
Старый 23.02.2011, 15:23   #5
pax
Unity/C# кодер
 
Аватар для pax
 
Регистрация: 03.10.2005
Адрес: Россия, Рязань
Сообщений: 7,568
Написано 3,006 полезных сообщений
(для 5,323 пользователей)
Ответ: Расширение редактора Unity

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

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

Итак разбираем код:
using UnityEditor;
using UnityEngine;

public class 
LookAtWizard ScriptableWizard 
{
    
// две публичные переменные, которые будут параметрами визарда
    
public Transform source// объект для поворота
    
public Transform target// цель

    // регистрация функции создания визарда в меню Unity
    
[MenuItem("Window/Look At Wizard")]
    static 
void CreateWizard()
    {
        
// создаем визард с одной кнопкой "Look At!"
        
ScriptableWizard.DisplayWizard<LookAtWizard>("Look At""Look At!");

        
// если необходимо реализовать вторую 
        // кнопку, то можно воспользоваться
        // другой перегрузкой метода 
        // ScriptableWizard.DisplayWizard<> 
        // и реализовать функцию 
        // void OnWizardOtherButton ()
    
}

    
// флаг успешной настройки визарда
    
private bool _wizardOk false;

    
// выполнение функции визарда - OnWizardCreate()
    
void OnWizardCreate()
    {
        
// выполняем только когда исходные данные верны
        
if (_wizardOk)
        {
            
source.LookAt(target);
        }
    }

    
// функция выполняется при создании визарда 
    // и при изменении исходных данных пользователем
    
void OnWizardUpdate()
    {
        
_wizardOk false;
        
// один из параметров не указан
        
if (source == null || target == null)
        {
            
// helpString выдает подсказки пользователю
            
helpString "Selcect Source and Target!";
        }
        
// если цель и объект совпадают
        
else if ((source == target) && (source != null) )
        {
            
helpString "Source equals Target!";
        }
        else
        {
            
// все проверки пройдены
            
helpString "Press \"Look At!\"!";
            
_wizardOk true;
        }
    }

Не забываем называть скрипт правильно (LookAtWizard.cs) и положить его в папку с названием Editor.

Вот и результат:
(Offline)
 
Ответить с цитированием
Эти 5 пользователя(ей) сказали Спасибо pax за это полезное сообщение:
Amatsu (21.11.2011), Fatalix3d (23.02.2011), Harter (15.01.2012), maxturbo (09.03.2011), Nuprahtor (23.02.2011)
Старый 21.11.2011, 21:15   #6
Amatsu
Дэвелопер
 
Аватар для Amatsu
 
Регистрация: 24.07.2008
Сообщений: 1,544
Написано 1,095 полезных сообщений
(для 2,706 пользователей)
Ответ: Расширение редактора Unity

Как я понял, данный тип расширения редактора работает только в случае, если соответствующий скрипт положен в папку Editor конкретного проекта? То есть если загрузить другой проект - надстройки не будет? А нельзя ли реализовать постоянный апгрейд редактора, не принадлежащий к конкретному проекту?
з.ы. может я тупой и тут уже был ответ на этот вопрос. Но я не заметил
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
Wegox (08.04.2012)
Старый 21.11.2011, 21:32   #7
Nuprahtor
Элита
 
Аватар для Nuprahtor
 
Регистрация: 26.07.2008
Сообщений: 1,972
Написано 1,095 полезных сообщений
(для 3,923 пользователей)
Ответ: Расширение редактора Unity

Можно все скрипты редактора загрузить в специальный пэккедж, который потом легко добавляется во все создаваемые проекты.
(Offline)
 
Ответить с цитированием
Сообщение было полезно следующим пользователям:
Amatsu (21.11.2011)
Старый 21.11.2011, 22:13   #8
pax
Unity/C# кодер
 
Аватар для pax
 
Регистрация: 03.10.2005
Адрес: Россия, Рязань
Сообщений: 7,568
Написано 3,006 полезных сообщений
(для 5,323 пользователей)
Ответ: Расширение редактора Unity

Официально такое не поддерживается.
__________________
Blitz3d to Unity Wiki
(Offline)
 
Ответить с цитированием
Старый 15.01.2012, 21:14   #9
Nazgul
Нуждающийся
 
Регистрация: 14.01.2011
Сообщений: 72
Написано 9 полезных сообщений
(для 44 пользователей)
Ответ: Расширение редактора Unity

Подскажите, а каким образом можно отследить событие нажатия правой кнопкой в окне сцены? Нужно, чтобы при нажатии отображался список - что-то типа контекстного меню.
(Offline)
 
Ответить с цитированием
Старый 15.01.2012, 21:45   #10
pax
Unity/C# кодер
 
Аватар для pax
 
Регистрация: 03.10.2005
Адрес: Россия, Рязань
Сообщений: 7,568
Написано 3,006 полезных сообщений
(для 5,323 пользователей)
Ответ: Расширение редактора Unity

http://unity3d.com/support/documenta....BeginGUI.html а там эвентами GUI.
__________________
Blitz3d to Unity Wiki
(Offline)
 
Ответить с цитированием
Старый 11.03.2012, 20:27   #11
WISHMASTER35
Бывалый
 
Аватар для WISHMASTER35
 
Регистрация: 21.12.2008
Адрес: UA
Сообщений: 878
Написано 105 полезных сообщений
(для 357 пользователей)
Ответ: Расширение редактора Unity

А если делать кривую из нескольких бизье или замкнутую фигуру, то как лучше кранить такую кривую?
По 4 вершины на один отрезок(как здесь) не очень удобно т.к. они будут связаны между собой.
Я так понимаю лучше хранить в виде списка таких вершин:
public class Vertex {
	public Vector3 p = Vector3.zero;
	public Vector3 dirA = Vector3.zero;
	public Vector3 dirB = Vector3.zero;
}
Но все формулы для такого вида представления бизье p0, p1, p2, p3.
Я из-за этого даже разделить одну бизье на две не могу)
Конечно можно все время переводить из одного формата в другой, но это не очень удобно.
Да и не пойму как из p0,p1,p2,p3 создать две моих вершины.
(Offline)
 
Ответить с цитированием
Старый 11.03.2012, 21:11   #12
pax
Unity/C# кодер
 
Аватар для pax
 
Регистрация: 03.10.2005
Адрес: Россия, Рязань
Сообщений: 7,568
Написано 3,006 полезных сообщений
(для 5,323 пользователей)
Ответ: Расширение редактора Unity

Очевидно же
v1 = new Vertex{
dirA  Vector3.zero,
p0,
dirB p1
}

v2 = new Vertex{
dirA  p2,
p3,
dirB Vector3.zero

__________________
Blitz3d to Unity Wiki
(Offline)
 
Ответить с цитированием
Старый 11.03.2012, 22:03   #13
WISHMASTER35
Бывалый
 
Аватар для WISHMASTER35
 
Регистрация: 21.12.2008
Адрес: UA
Сообщений: 878
Написано 105 полезных сообщений
(для 357 пользователей)
Ответ: Расширение редактора Unity

Только dirA и dirB это координаты относительно p.
Так что так будет:
a.p = p0;
a.dirA = p1-p0;

b.p = p3;
b.dirB = p2-p3;
a.dirB и b.dirA не восстановить из этих данных.
А как сделать, что бы при нажатии ctrl+z происходила отмена последнего изменение?
(Offline)
 
Ответить с цитированием
Старый 11.03.2012, 23:52   #14
pax
Unity/C# кодер
 
Аватар для pax
 
Регистрация: 03.10.2005
Адрес: Россия, Рязань
Сообщений: 7,568
Написано 3,006 полезных сообщений
(для 5,323 пользователей)
Ответ: Расширение редактора Unity

Сообщение от WISHMASTER35 Посмотреть сообщение
А как сделать, что бы при нажатии ctrl+z происходила отмена последнего изменение?
http://unity3d.com/support/documenta...ence/Undo.html
__________________
Blitz3d to Unity Wiki
(Offline)
 
Ответить с цитированием
Старый 13.03.2012, 00:17   #15
WISHMASTER35
Бывалый
 
Аватар для WISHMASTER35
 
Регистрация: 21.12.2008
Адрес: UA
Сообщений: 878
Написано 105 полезных сообщений
(для 357 пользователей)
Ответ: Расширение редактора Unity

Написал свой вариант редактирования замкнутых кривых.
т.к. в виде массива Vertex хранить кривую не очень удобно, то вернулся к классу Bezier)
using UnityEngine;
using System.Collections;

[System.Serializable]
public class Bezier {
	
	public Vector3 p0;
	public Vector3 p1;
	public Vector3 p2;
	public Vector3 p3;
	
	public Bezier() {
	}
	
	public Bezier(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) {
		this.p0 = p0;
		this.p1 = p1;
		this.p2 = p2;
		this.p3 = p3;
	}
	
	public Vector3 Evaluate(float t) {
        t = Mathf.Clamp01(t);
        float t1 = 1 - t;
        return t1 * t1 * t1 * p0 + 
			   3 * t * t1 * t1 * p1 +
               3 * t * t * t1 * p2 +
			   t * t * t * p3;
    }
	
}
using UnityEngine;
using System.Collections;

public class BezierFigure : MonoBehaviour {
	
	public Bezier[] curves = new Bezier[0];
	
	void OnDrawGizmos() {
		Gizmos.color = Color.green;
		for(int i=0; i<curves.Length; i++) {
			Bezier a = curves[i];
			DrawBezier(a);
		}
	}
	
	private void DrawBezier(Bezier bezier) {
		for(int i=0; i<30; i++) {
			float t1 = i/30.0f;
            float t2 = (i+1)/30.0f;
			Vector3 a = bezier.Evaluate(t1);
			Vector3 b = bezier.Evaluate(t2);
			a = transform.TransformPoint(a);
			b = transform.TransformPoint(b);
			Gizmos.DrawLine(a, b);
		}
	}
	
	public Bezier[] GetCurves() {
		return curves;
	}
	
	public void SetCurves(Bezier[] curves) {
		this.curves = curves;
	}
	
}
using UnityEditor;
using UnityEngine;
using System.Collections.Generic;
using System.Collections;

[CustomEditor(typeof(BezierFigure))]
public class BezierEditor : Editor {
	
	public override void OnInspectorGUI() {
        DrawDefaultInspector();
		EditorGUIUtility.LookLikeControls();
		BezierFigure figure = target as BezierFigure;
		
		if( GUILayout.Button("Subdivide") ) {
			Subdivide(figure);
			Undo.RegisterUndo(target, "Bezier");
			EditorUtility.SetDirty(figure);
		}
	}
	
	private static void Subdivide(BezierFigure figure) {
		List<Bezier> list = new List<Bezier>();
		foreach( Bezier bezier in figure.GetCurves() ) {
			Subdivide(list, bezier.p0, bezier.p1, bezier.p2, bezier.p3);
		}
		figure.SetCurves(list.ToArray());
	}
	
	private static void Subdivide(List<Bezier> list, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3) {
		Vector3 p00 = p0;
   		Vector3 p01 = (p0+p1)/2;
    	Vector3 p02 = (p0+2*p1+p2)/4;
    	Vector3 p03 = (p0+3*(p1+p2)+p3)/8;
		
		Vector3 p10 = (p0+3*(p1+p2)+p3)/8;
    	Vector3 p11 = (p3+2*p2+p1)/4;
    	Vector3 p12 = (p2+p3)/2;
    	Vector3 p13 = p3;
		
		Bezier b1 = new Bezier(p00, p01, p02, p03);
		Bezier b2 = new Bezier(p10, p11, p12, p13);
		
		list.Add(b1);
		list.Add(b2);
	}

	public void OnSceneGUI() {
        BezierFigure figure = target as BezierFigure;
		
		bool changed = false;
		Bezier[] curves = figure.GetCurves();
		for( int i=0; i<curves.Length; i++ ) {
			Bezier a = curves[i];
			Bezier b = curves[(i+1)%curves.Length];
			changed |= BezierMoveHandle(figure.transform, a, b);
		}
		
		if(changed) Undo.RegisterUndo(target, "Bezier");
		
        if (GUI.changed) EditorUtility.SetDirty(target);
    }
	
	private static bool BezierMoveHandle(Transform transform, Bezier b1, Bezier b2) {
		Vector3 pos  = transform.TransformPoint(b1.p3);
		Vector3 dir1 = transform.TransformPoint(b1.p2);
		Vector3 dir2 = transform.TransformPoint(b2.p1);
		
		bool changed = BezierMoveHandle(ref pos, ref dir1, ref dir2);
		
		b2.p0 = b1.p3 = transform.InverseTransformPoint(pos);
		b1.p2 = transform.InverseTransformPoint(dir1);
		b2.p1 = transform.InverseTransformPoint(dir2);
		
		return changed;
	}
	
	private static bool BezierMoveHandle(ref Vector3 pos, ref Vector3 dir1, ref Vector3 dir2) {
		Handles.DrawLine(pos, dir1);
        Handles.DrawLine(pos, dir2);
		
		Vector3 oldPos = pos;
		Vector3 oldDir1 = dir1;
		Vector3 oldDir2 = dir2;
		
		float size = HandleUtility.GetHandleSize(pos) * 0.2f;
		pos  = Handles.FreeMoveHandle(pos,  Quaternion.identity, size, Vector3.zero, Handles.SphereCap);
		dir1 = Handles.FreeMoveHandle(dir1, Quaternion.identity, size, Vector3.zero, Handles.SphereCap);
		dir2 = Handles.FreeMoveHandle(dir2, Quaternion.identity, size, Vector3.zero, Handles.SphereCap);
		
		dir1 += pos-oldPos;
		dir2 += pos-oldPos;
		
		if(oldDir1 != dir1) {
			dir1 -= pos;
			dir2 -= pos;
			dir2 = -dir1.normalized * dir2.magnitude;
			dir1 += pos;
			dir2 += pos;
		} else if(oldDir2 != dir2) {
			dir1 -= pos;
			dir2 -= pos;
			dir1 = -dir2.normalized * dir1.magnitude;
			dir1 += pos;
			dir2 += pos;
		}
		
		return pos != oldPos || 
				dir1 != oldDir1 || 
				dir2 != oldDir2;
	}
	
}
Вот только из-за сериализациикласса Bezier при попытки вручную изменить ее координаты выскакивает ошибка: Using a SerializedProperty after the object has been deleted is not possible.
UnityEditor.Editor:DrawDefaultInspector()
BezierEditor:OnInspectorGUI() (at Assets/BezierLevel/Editor/BezierEditor.cs:10)
UnityEditor.DockArea:OnGUI() не пойму что не так. И кстате, на структуру сериализация не действовала(

И с Undo не все ясно. Если быстро пару раз нажать Subdivide, то не все изменения запоминаются(
Еще просто не пойму как оно запоминает изменения только тогда, когда я перестаю перетягивать вершину. Хотя RegisterUndo вызываю все время когда перетягиваю вершину.

Еще хорошо бы добавить возможность удалять и добавлять бизье в кривой, а то Subdevide много безье создает.
Миниатюры
Нажмите на изображение для увеличения
Название: Безымянный.png
Просмотров: 1811
Размер:	132.4 Кб
ID:	16345  
(Offline)
 
Ответить с цитированием
Ответ


Опции темы

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


Часовой пояс GMT +4, время: 12:18.


vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot
Style crйe par Allan - vBulletin-Ressources.com