Как создавать шаблоны UI Toolkit в пакетах Unity
Полное руководство по созданию и использованию шаблонов UI Toolkit в Unity пакетах. Узнайте стандартные подходы, оптимизацию для редактора и производительности.
Как создавать шаблоны элементов UI Toolkit в своем пакете (Packages) и использовать их в проекте?
Изучаю UI Toolkit и столкнулся с проблемой: хочу создать кнопку с определенным стилем (USS), которая будет представлять собой UXML файл, добавляемый через UI Builder/Library в любой интерфейс. В Assets я мог бы просто создать элемент, и он бы появился в Library/Project/Assets/“название”.uxml. Но в пакете это не работает.
Я обнаружил, что если создать класс, наследующийся от VisualElement и помеченный атрибутом [UxmlElement], он будет отображаться как нужно. Похоже на костыль, ведь это просто класс CustomButton : Button без переопределенной логики.
Вопрос: является ли этот способ единственным или есть более лаконичное решение? Или создание такой пустышки единственный вариант?
Дополнительно: если придется использовать этот подход, нужно ли реализовывать загрузку шаблона через AssetDataBase.LoadAssetAtPath() для редакторского интерфейса? Не будет ли это сильно нагружать Unity и ПК при кастомных окнах и инспекторах с элементами, созданными таким образом?
Чтобы создавать и использовать шаблоны UI Toolkit в своем пакете, нужно использовать комбинацию UXML, USS и C# классов с правильной регистрацией. Ваш подход с наследованием VisualElement и атрибутом [UxmlElement] — это базовый механизм, но есть нюансы для пакетов и оптимизации.
Краткий ответ
В пакетах Unity для создания шаблонов UI Toolkit необходимо:
- Создать C# класс с атрибутом
[UxmlElement]иUxmlFactory, - Добавить UXML-шаблон через
VisualTreeAsset, - Зарегистрировать namespace в UXML.
Этот подход не костыль, а стандартная практика. Для редакторского интерфейса используйтеAssetDatabase.LoadAssetAtPath()с кешированием для производительности.
Contents
- Базовый подход для пакетов
- Полный пример реализации
- Оптимизация для редактора
- Альтернативные решения
- Рекомендации по производительности
Базовый подход для пакетов
В пакетах нельзя просто создать UXML-файл и ожидать, что он появится в UI Builder. Unity требует явной регистрации через C# классы.
Почему это так работает?
Пакеты из PackageManager не автоматически сканируются на UXML-файлы, как Assets. Требуется объявить элемент через:
[UxmlElement]
public partial class CustomButton : Button
{
public CustomButton() { }
}
Затем добавить UXML-шаблон через VisualTreeAsset:
public VisualTreeAsset uxmlTemplate;
И зарегистрировать namespace в UXML:
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:custom="MyPackageNamespace">
<custom:CustomButton />
</ui:UXML>
Полный пример реализации
1. Структура пакета
MyPackage/
├── Editor/
│ └── CustomButtonEditor.cs
├── Runtime/
│ └── CustomButton.cs
└── Editor/
└── Resources/
└── CustomButton.uxml
2. C# класс элемента
// Runtime/CustomButton.cs
using UnityEngine.UIElements;
[UxmlElement]
public partial class CustomButton : Button
{
public new class UxmlFactory : UxmlFactory<CustomButton, UxmlTraits> { }
public CustomButton()
{
// Добавляем USS по умолчанию
styleSheets.Add(Resources.Load<StyleSheet>("CustomButtonStyles"));
}
}
3. UXML-шаблон
<!-- Editor/Resources/CustomButton.uxml -->
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:custom="MyPackageNamespace">
<ui:VisualElement class="button-container">
<ui:Label text="Custom Button" name="button-text" />
</ui:VisualElement>
</ui:UXML>
4. Регистрация для UI Builder
// Editor/CustomButtonEditor.cs
using UnityEditor.UIElements;
using UnityEngine.UIElements;
[InitializeOnLoad]
public static class CustomButtonRegistration
{
static CustomButtonRegistration()
{
// Регистрируем шаблон в UI Builder
var template = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
"Packages/com.my.package/Editor/Resources/CustomButton.uxml"
);
if (template != null)
{
UxmlDescriptionCache.RegisterDescription(template);
}
}
}
Оптимизация для редактора
Кеширование ассетов
private static VisualTreeAsset _cachedTemplate;
private VisualTreeAsset GetTemplate()
{
if (_cachedTemplate == null)
{
_cachedTemplate = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(
"Packages/com.my.package/Editor/Resources/CustomButton.uxml"
);
}
return _cachedTemplate;
}
Для инспекторов окон
public override VisualElement CreateInspectorGUI()
{
var template = GetTemplate();
var container = template.CloneTree();
// Дополнительная логика...
return container;
}
Почему это не нагружает систему:
AssetDatabase.LoadAssetAtPath()кеширует ассеты после первой загрузки,- В инспекторах шаблоны используются повторно,
- Для runtime UI загружайте шаблоны через
Resources.Load().
Альтернативные решения
1. UI Builder + Package Manager
Создайте UI-контрол через UI Builder и экспортируйте как C# класс:
// Автоматически сгенерированный класс
public partial class MyButton : VisualElement
{
private Label _label;
public MyButton()
{
var visualTree = Resources.Load<VisualTreeAsset>("MyButton");
visualTree.CloneTree(this);
_label = this.Q<Label>("label");
}
}
2. Runtime-only шаблоны
Для интерфейсов, не требующих UI Builder:
public void CreateUI()
{
var template = Resources.Load<VisualTreeAsset>("CustomButton");
var instance = template.CloneTree();
rootElement.Add(instance);
}
Рекомендации по производительности
-
Для редактора:
- Используйте статическое кеширование (
static VisualTreeAsset), - Загружайте шаблоны только при открытии окна,
- Избегайте повторной загрузки в
OnGUI().
- Используйте статическое кеширование (
-
Для runtime:
- Используйте
Resources.Load()илиAddressables, - Кешируйте экземпляры шаблонов, если они используются многократно.
- Используйте
-
Оптимизация USS:
- Минимизуйте вложенность селекторов,
- Используйте классы вместо инлайновых стилей.
Заключение
- Стандартный подход с
[UxmlElement]+UxmlFactory— это не костыль, а архитектурное требование Unity для пакетов. - Для UI Builder обязательно нужна регистрация через
UxmlDescriptionCache. - Производительность обеспечивается кешированием ассетов и избеганием повторных загрузок.
- Альтернативы: UI Builder для визуальной разработки или чистые runtime-шаблоны для динамических интерфейсов.
Совет: Для сложных элементов комбинируйте подходы — создавайте базовую логику в C# и визуальную часть через UI Builder, затем экспортируйте как шаблон. Это обеспечит и гибкость, и производительность.