Создание настраиваемого окна редактора с помощью скрипта C#
Версия: 2022.3+
В этом примере показано, как создать пользовательское окно Редактора с помощью скрипта C#, чтобы реагировать на ввод пользователя, изменять размер UI и обрабатывать перезагрузку в горячем режиме.
Пользовательское окно редактора — это класс, производный от EditorWindow класс. UI Toolkit использует CreateGUI метод для добавления элементов управления в Редактор UI, и Unity вызывает CreateGUI метод автоматически, когда окно должно быть показано. Этот метод работает так же, как методы, такие как Awake или Update.
При создании настраиваемого окна Редактора следуйте следующим рекомендациям:
- Поместите код, зависящий от загрузки UXML/USS в методе
CreateGUI, чтобы обеспечить наличие всех необходимых ассетов. - Храните код регистрации события внутри
CreateGUIили после вызоваCreateGUI.
На следующей диаграмме показан порядок выполнения окна Редактора:
Дополнительную информацию см. в EditorWindow class documentation.
Примерный обзор
Этот пример создает браузер спрайтов, который находит и отображает все спрайты внутри проекта, и отображает их в списке. Если вы выберете спрайта в списке, изображение спрайта отображается в правой части окна.
Вы можете найти завершенные файлы, созданные этим примером, в хранилище GitHub.
Предварительные условия
Это руководство предназначено для разработчиков, знакомых со скриптами Unity Editor, UI Toolkit и C#. Перед началом работы ознакомьтесь со следующим:
Создание скрипта окна редактора
Добавить UI элемента управления в отношении UI, добавить визуальные элементы в визуальное дерево. UI Toolkit использует VisualElement.Add() метод для добавления детей к существующему визуальному элементу, и доступ к визуальное дерево окна редактора с помощью rootvisualElement свойства.
Поместите скрипты для окон пользовательского редактора в папку Редактор, так как Unity требует этого для правильного управления компиляцией и сборкой. Дополнительные сведения см. в Ссылка на зарезервированную папку.
- Создайте проект в Unity с любым шаблоном.
- Если у вас нет папки с именем
Editor, создайте ее в окне Project. - Щелкните правой кнопкой мыши папку
Editorи выберите Создать > UI Toolkit > Окно редактора. - В поле C# введите
MyCustomEditor. - Снимите флажки UXML и USS.
- Выберите Подтвердите.
- В меню выберите Окно > UI Toolkit > MyCustomEditor окно. В окне отображается надпись с текстом “Hello World! От C#”.
Создание списка спрайтов
Для представления списка спрайтов в примере используются AssetDatabase для поиска всех спрайтов в проекте. Для браузера спрайтов добавьте TwoPaneSplitView, чтобы разделить доступное окно на две панели: одну с фиксированным размером и одну с гибким размером. При изменении размера окна гибкая панель изменяет размер, а панель с фиксированным размером остается без изменений.
-
В начале файла добавьте следующую директиву, требуемую перечнем:
using System.Collections.Generic; -
Замените код внутри
CreateGUI()следующим кодом. Это перечисляет все спрайты внутри проекта.public void CreateGUI() { // Get a list of all sprites in the project var allObjectGuids = AssetDatabase.FindAssets("t:Sprite"); var allObjects = new List<Sprite>(); foreach (var guid in allObjectGuids) { allObjects.Add(AssetDatabase.LoadAssetAtPath<Sprite>(AssetDatabase.GUIDToAssetPath(guid))); } } -
Внутри
CreateGUI()добавьте следующий код. Это создает TwoPaneSplitview и добавляет два дочерних элемента в качестве замены для различных элементов управления.// Create a two-pane view with the left pane being fixed. var splitView = new TwoPaneSplitView(0, 250, TwoPaneSplitViewOrientation.Horizontal); // Add the view to the visual tree by adding it as a child to the root element. rootVisualElement.Add(splitView); // A TwoPaneSplitView needs exactly two child elements. var leftPane = new VisualElement(); splitView.Add(leftPane); var rightPane = new VisualElement(); splitView.Add(rightPane); -
В меню выберите Окно > UI Toolkit > MyCustomEditor, чтобы открыть окно. Окно отображается в разделенном виде с двумя пустыми панелями. Переместите разделитель, чтобы увидеть его в действии.
Окно с двумя раздельными стеклами
Добавление представления списка
Для браузера спрайтов, левая панель будет списком, содержащим имена всех спрайтов в проекте. Контроль ListView происходит от VisualElement, так что легко изменить код, чтобы использовать ListView вместо VisualElement.
Контроллер ListView отображает список элементов, которые можно выбрать. Он оптимизирован для создания достаточного количества элементов, чтобы покрыть видимую область, и для объединения и повторного использования визуальных элементов при прокручивании списка. Это оптимизирует производительность и уменьшает потребление памяти, даже в списках, содержащих много элементов.
Чтобы воспользоваться этим, инициализируйте ListView следующим образом:
- Массив данных
- Функция обратного вызова для создания индивидуального визуального списка в списке
- Функция привязки, инициализирующая визуальный список с элементом из массива данных
Вы можете создавать сложные структуры UI для каждого элемента в списке. Для демонстрационных целей в этом примере используется простая текстовая метка для отображения имени спрайта.
-
В
CreateGUI()измените левую панель на ListView вместоVisualElement:public void CreateGUI() { ... var leftPane = new ListView(); splitView.Add(leftPane); ... } -
В нижней части
CreateGUI()добавьте следующий код для инициализации ListView:public void CreateGUI() { ... // Initialize the list view with all sprites' names leftPane.makeItem = () => new Label(); leftPane.bindItem = (item, index) => { (item as Label).text = allObjects[index].name; }; leftPane.itemsSource = allObjects; } -
В меню выберите Окно > UI Toolkit > MyCustomEditor, чтобы открыть окно пользовательского редактора. В окне отображается список с возможностью прокрутки и элементы для выбора, похожие на изображение ниже.
ListView с именами спрайтов
Добавление обратных вызовов
Чтобы отобразить изображение спрайта на правой панели при его выборе из списка, используйте свойство selectionChanged левой панели и добавьте функцию обратного вызова.
Чтобы отобразить изображение, создайте новый контроллер Image для выбранного спрайта и используйте VisualElement.Clear() для удаления всего предыдущего содержимого перед добавлением контроллера.
Совет: Если вы потеряли окно и меню не открывается, закройте все плавающие панели с помощью меню Окно > Панели > Закройте все плавающие панелиили сбросьте расположение окна.
-
Добавление функции обратного вызова при изменении выбора из списка в левой панели.
public void CreateGUI() { ... // React to the user's selection leftPane.selectionChanged += OnSpriteSelectionChange; } private void OnSpriteSelectionChange(IEnumerable<object> selectedItems) { } -
Функция обратного вызова должна получить доступ к правой панели TwoPaneSplitview. Для этого измените правую панель, созданную внутри
CreateGUI(), на членскую переменную:private VisualElement m_RightPane; public void CreateGUI() { ... m_RightPane = new VisualElement(); splitView.Add(m_RightPane); ... } -
Добавьте следующий код к функции
OnSpriteSelectionChange. Это очистит все предыдущее содержимое панели, получит выбранный спрайт и добавит новый элемент Image для отображения спрайта.private void OnSpriteSelectionChange(IEnumerable<object> selectedItems) { // Clear all previous content from the pane. m_RightPane.Clear(); // Get the selected sprite and display it. var enumerator = selectedItems.GetEnumerator(); if (enumerator.MoveNext()) { var selectedSprite = enumerator.Current as Sprite; if (selectedSprite != null) { // Add a new Image control and display the sprite. var spriteImage = new Image(); spriteImage.scaleMode = ScaleMode.ScaleToFit; spriteImage.sprite = selectedSprite; // Add the Image control to the right-hand pane. m_RightPane.Add(spriteImage); } } } -
Из меню выберите Окно > UI Toolkit > MyCustomEditor, чтобы открыть окно пользовательского редактора. Когда вы выбираете спрайт из списка слева, изображение спрайта отображается в правой части окна, как показано на изображении ниже.
Браузер Sprite в действии
Сделать UI изменяемым
Окна редактора могут изменять размер в пределах минимальных и максимальных разрешенных размеров. Чтобы установить эти размеры, записывайте в свойства EditorWindow.minSize и EditorWindow.maxSize. Чтобы предотвратить изменение размера окна, назначайте одинаковые размеры обоим свойствам.
Если размеры окна слишком малы для отображения всего UI, вы можете использовать элемент ScrollView для обеспечения прокрутки окна. ListView на левой панели использует ScrollView внутренне, но правая панель является обычной VisualElement. Чтобы сделать правую панель изменяемой, измените ее на ScrollView с двунаправленной прокруткой.
-
Для ограничения размера окна добавьте в нижнюю часть функции
ShowMyEditor()следующий код:public static void ShowMyEditor() { ... // Limit size of the window. wnd.minSize = new Vector2(450, 200); wnd.maxSize = new Vector2(1920, 720); } -
В
CreateGUI()измените правую панельVisualElementнаScrollViewс двунаправленной прокруткой:public void CreateGUI() { ... m_RightPane = new ScrollView(ScrollViewMode.VerticalAndHorizontal); splitView.Add(m_RightPane); ... } -
Из меню выберите Окно > UI Toolkit > MyCustomEditor, чтобы открыть окно пользовательского редактора. В окне браузера спрайтов теперь есть панели прокрутки. Измените размер окна, чтобы увидеть панели прокрутки в действии.
Окно редактора с панелями прокрутки
Поддержка горячей перезагрузки в окне редактора
Перезагрузка домена C# происходит при перекомпиляции скриптов или при переходе Редактора в режим воспроизведения. В только что созданном окне Редактора откройте браузер спрайтов, выберите спрайт и перейдите в режим воспроизведения. Окно перезагружается и выбор исчезает.
Правильное окно редактора должно работать с горячая перезагрузка рабочего процесса. VisualElement объекты не серийизуются, вы должны повторно создавать UI каждый раз, когда происходит перезагрузка. Это означает, что CreateGUI() метод вызывается после завершения перезагрузки. Это позволяет восстановить состояние интерфейса, каким оно было до перезагрузки, если сохранить нужные данные в вашем EditorWindow class.
-
Добавить переменный член к
MyCustomEditorкласс, чтобы сохранить индекс выбранного элемента в списке спрайтов. При выборе эта переменная-член хранит новый индекс выделения в ListView.public class MyCustomEditor : EditorWindow { [SerializeField] private int m_SelectedIndex = -1; .... } -
Добавьте следующий код в конец
CreateGUI()для хранения и восстановления выбранного индекса списка.public void CreateGUI() { ... // Restore the selection index from before the hot reload. leftPane.selectedIndex = m_SelectedIndex; // Store the selection index when the selection changes. leftPane.selectionChanged += (items) => { m_SelectedIndex = leftPane.selectedIndex; }; } Из меню выберите Окно > UI Toolkit > MyCustomEditor, чтобы открыть окно пользовательского редактора. Выберите спрайт из списка и перейдите в режим воспроизведения, чтобы проверить перезагрузку в горячем режиме.
Для справки, вот готовый скрипт:
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
public class MyCustomEditor : EditorWindow
{
[SerializeField] private int m_SelectedIndex = -1;
private VisualElement m_RightPane;
[MenuItem("Window/UI Toolkit/MyCustomEditor")]
public static void ShowMyEditor()
{
// This method is called when the user selects the menu item in the Editor.
EditorWindow wnd = GetWindow<MyCustomEditor>();
wnd.titleContent = new GUIContent("My Custom Editor");
// Limit size of the window.
wnd.minSize = new Vector2(450, 200);
wnd.maxSize = new Vector2(1920, 720);
}
public void CreateGUI()
{
// Get a list of all sprites in the project.
var allObjectGuids = AssetDatabase.FindAssets("t:Sprite");
var allObjects = new List<Sprite>();
foreach (var guid in allObjectGuids)
{
allObjects.Add(AssetDatabase.LoadAssetAtPath<Sprite>(AssetDatabase.GUIDToAssetPath(guid)));
}
// Create a two-pane view with the left pane being fixed.
var splitView = new TwoPaneSplitView(0, 250, TwoPaneSplitViewOrientation.Horizontal);
// Add the panel to the visual tree by adding it as a child to the root element.
rootVisualElement.Add(splitView);
// A TwoPaneSplitView always needs two child elements.
var leftPane = new ListView();
splitView.Add(leftPane);
m_RightPane = new ScrollView(ScrollViewMode.VerticalAndHorizontal);
splitView.Add(m_RightPane);
// Initialize the list view with all sprites' names.
leftPane.makeItem = () => new Label();
leftPane.bindItem = (item, index) => { (item as Label).text = allObjects[index].name; };
leftPane.itemsSource = allObjects;
// React to the user's selection.
leftPane.selectionChanged += OnSpriteSelectionChange;
// Restore the selection index from before the hot reload.
leftPane.selectedIndex = m_SelectedIndex;
// Store the selection index when the selection changes.
leftPane.selectionChanged += (items) => { m_SelectedIndex = leftPane.selectedIndex; };
}
private void OnSpriteSelectionChange(IEnumerable<object> selectedItems)
{
// Clear all previous content from the pane.
m_RightPane.Clear();
var enumerator = selectedItems.GetEnumerator();
if (enumerator.MoveNext())
{
var selectedSprite = enumerator.Current as Sprite;
if (selectedSprite != null)
{
// Add a new Image control and display the sprite.
var spriteImage = new Image();
spriteImage.scaleMode = ScaleMode.ScaleToFit;
spriteImage.sprite = selectedSprite;
// Add the Image control to the right-hand pane.
m_RightPane.Add(spriteImage);
}
}
}
}