Comments 8
Проблему небольших отличий лучше решать наследованием, интерфейсами и прочим, ну да ладно.
А много у вас классов? Есть подозрение, что на большом количестве классов и переменных редактор будет подтормаживать при переключении между объектами: сначала будет залипать в OnEnable, а потом в OnInspectorGUI.
Я бы лучше сделал либо компонент-пустышку, который себя будет заменять на нужный, либо свою менюшку с попапом для добавления компонента. Сомневаюсь, что вам часто придётся менять тип компонента, когда он уже висит на объекте.
А много у вас классов? Есть подозрение, что на большом количестве классов и переменных редактор будет подтормаживать при переключении между объектами: сначала будет залипать в OnEnable, а потом в OnInspectorGUI.
Я бы лучше сделал либо компонент-пустышку, который себя будет заменять на нужный, либо свою менюшку с попапом для добавления компонента. Сомневаюсь, что вам часто придётся менять тип компонента, когда он уже висит на объекте.
Editor script можно сильно упростить:
Хм, правда при этом он теряет возможность одновременного редактирования множества объектов даже при указанном [CanEditMultipleObjects]
[CustomEditor(typeof(TestComponent))]
[CanEditMultipleObjects]
public class TestComponentEditor : Editor
{
public override void OnInspectorGUI()
{
TestComponent testComponent = (TestComponent)target;
testComponent.component = (TestComponent.ComponentType) EditorGUILayout.EnumPopup("Choose", testComponent.component);
switch (testComponent.component)
{
case TestComponent.ComponentType.First:
testComponent.variableComponentFirst = EditorGUILayout.IntSlider(testComponent.variableComponentFirst, 0, 100);
break;
case TestComponent.ComponentType.Second:
testComponent.variableComponentSecond = EditorGUILayout.IntSlider(testComponent.variableComponentSecond, 0, 100);
break;
}
}
}
Хм, правда при этом он теряет возможность одновременного редактирования множества объектов даже при указанном [CanEditMultipleObjects]
Да здравствует God object
Первое с чем я столкнулся — как реализовать выпадающий список в редакторе. Находя первые темы на форумах в интернете, казалось, что это не так то просто, но терпение привело меня к использованию обычного перечисления
Вы можете овверайдить отрисовку проперти атрибутами, т. е. использовать CustomPropertyDrawer.
Код:
Scripts/TestCompAttribute.cs
Scripts/TestComponent.cs
Scripts/Editor/TestComponentEditor.cs:
Получится такой же выпадающий список, только его значения можно брать от куда угодно без модификации кода
using System;
using UnityEngine;
[AttributeUsage(AttributeTargets.Field)]
public class TestCompAttribute : PropertyAttribute
{
//Запишем значения здесь чтоб было в одном месте
//Но эти значения можно черпать от куда угодно
public static string[] AttributeValues = new string[]
{
"None",
"First",
"Second"
};
}
Scripts/TestComponent.cs
using UnityEngine;
public class TestComponent : MonoBehaviour
{
[TestComp] //Атрибутом указываем, что у нас кастомная отрисовка этой проперти. (PS. TestComp вышло от TestCompAttribute без слова Attribute)
public string componentName;
public int variableComponentFirst;
public int variableComponentSecond;
}
Scripts/Editor/TestComponentEditor.cs:
using UnityEditor;
using UnityEngine;
//Указываем что будем оверрайдить стандартые отрисовки проперти помеченных TestComp атрибутом
[CustomPropertyDrawer(typeof(TestCompAttribute))]
public class TestCompAttribute_Drawer : PropertyDrawer
{
//Храним значение
private int _selected = -1;
//Оверрайдим стандартную отрисовку проперти
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
//Запускаем для будущей проверки изменений редактирования проперти
EditorGUI.BeginChangeCheck();
//Ищем значение в списке вариантов
_selected = GetItemIndex(property.stringValue);
//Выпадающий список
_selected = EditorGUI.Popup(position, label.text.Replace("Component Name", "Select component type:"), _selected, TestCompAttribute.AttributeValues);
//Проверяем был ли изменено проперти
if (EditorGUI.EndChangeCheck())
{
//Устанавливаем в переменную скрипта componentName новое значение
property.stringValue = TestCompAttribute.AttributeValues[_selected];
property.serializedObject.ApplyModifiedProperties();
//EditorUtility.SetDirty(property.serializedObject.targetObject);
}
}
private int GetItemIndex(string id)
{
for (int i = 0; i < TestCompAttribute.AttributeValues.Length; ++i)
{
if (Equals(TestCompAttribute.AttributeValues[i], id))
return i;
}
return 0;
}
}
//Этим атрибутом мы объявляем какой компонент подвергнется редактированию
[CustomEditor(typeof(TestComponent))]
[CanEditMultipleObjects]
public class TestComponentEditor : Editor
{
TestComponent subject;
SerializedProperty compName;
SerializedProperty varCompFirst;
SerializedProperty varCompSecond;
//Передаём этому скрипту компонент и необходимые в редакторе поля
void OnEnable()
{
subject = target as TestComponent;
compName = serializedObject.FindProperty("componentName");
varCompFirst = serializedObject.FindProperty("variableComponentFirst");
varCompSecond = serializedObject.FindProperty("variableComponentSecond");
}
//Переопределяем событие отрисовки компонента
public override void OnInspectorGUI()
{
//Метод обязателен в начале. После него редактор компонента станет пустым и
//далее мы с нуля отрисовываем его интерфейс.
serializedObject.Update();
//Вывод в редактор текстового поля (который при отрисовке будет оверрайдится нашим TestCompAttribute_Drawer
EditorGUILayout.PropertyField(compName);
//Проверка выбранного пункта в выпадающем меню,
if (subject.componentName == TestCompAttribute.AttributeValues[1])
{
//Вывод в редактор слайдера
EditorGUILayout.IntSlider(varCompFirst, 0, 100, new GUIContent("Variable First"));
}
else if (subject.componentName == TestCompAttribute.AttributeValues[2])
{
EditorGUILayout.IntSlider(varCompSecond, 0, 100, new GUIContent("Variable Second"));
}
//Метод обязателен в конце
serializedObject.ApplyModifiedProperties();
}
}
Получится такой же выпадающий список, только его значения можно брать от куда угодно без модификации кода
Простите, но статья даже не бесполезная, а вредная ибо новички сейчас начитаются этого и начнут делать также.
То, что у вас возникла ситуация необходимости такого подхода говорит о фиговости архитектуры и надо эти проблемы решать так, как описал BasmanovDaniil выше т.е. через возможности, которые нам щедро предоставляет ООП — интерфейсы, наследование и пр.
И да, почему минусанули gturk'а вверху, ведь он в чём-то прав по поводу God object'а — подход автора — это прямой путь к нему.
Всё, что описал автор можно понять прочитав доку по эдитор-скриптам
То, что у вас возникла ситуация необходимости такого подхода говорит о фиговости архитектуры и надо эти проблемы решать так, как описал BasmanovDaniil выше т.е. через возможности, которые нам щедро предоставляет ООП — интерфейсы, наследование и пр.
И да, почему минусанули gturk'а вверху, ведь он в чём-то прав по поводу God object'а — подход автора — это прямой путь к нему.
Всё, что описал автор можно понять прочитав доку по эдитор-скриптам
Ваши доводы мне понятны и верны. В статье я не призываю читателей решать проблемы таким образом. Просто данный способ может оказаться полезным в некоторых ситуациях. А статью я написал, потому что на русском языке не нашёл никакой информации, плюс если у кого-то возникнут проблемы с доком по эдитор-скриптам, то ему не придётся всё испытывать на личном опыте, а только потом решать надо ему это или нет.
Динамический UI — вещь полезная, но пример, в который она обернута далёк от того, в каких случаях стоит использовать его. Правильный пример работы можно подсмотреть в RectTransform'е (посложнее), LayoutElement'е (попроще) и т.д., которые входит в Unity UI. Если не понятно как это сделано у них (хотя там всё в общем-то очевидно), благо есть исходники, которые можно посмотреть здесь bitbucket.org/Unity-Technologies/ui или же декомпилировать Visual Studio (если включена настройка декомпайла) (я пользуюсь этим вариантом ибо быстро).
Sign up to leave a comment.
UnityEditor, динамическое содержимое редактора компонента