Инкапсулировать UXML документы с логикой
A Префаб является готовым GameObject что вы можете инстанционировать несколько раз в сцене. Префабы полезны для создания многоразовых компонентов. Визуальные элементы UI Toolkit не GameObjects и поэтому Префабы не применяются. Однако вы можете создать пользовательский контроллер в качестве многоразового UI компонент, который инкапсулирует определенную иерархию элементов с логикой. Потому что UI Toolkit поощряет вас разделить UI из вашего кода игры или приложения, вы можете использовать UXML для определения структуры, использования USS чтобы определить внешний вид, и использовать C# для определения логики вашего пользовательского контроля.
Создание многоразовых компонентов UI
В качестве примера, давайте предположим, что вы хотите создать карточную игру. Вы хотите отобразить карты с различными статистическими данными, такими как жизнь и атака.
Вы можете создать пользовательский контроллер под названием CardElement, который отображает изображение, жизнь и статистику атаки персонажа, а затем повторно использовать этот пользовательский контроллер для каждой карты в вашей игре.
Ниже приводятся общие шаги для достижения этой цели:
В C#, объявляет пользовательский элемент тип вызванный CardElement.
-
В UXML определите иерархию пользовательского контроля. Можно использовать два подхода. Оба подхода поддерживают инстанционирование
CardElementв C# и в родительском UXML.- UXML-первый подход добавляет детей после построения элемента.
- Подход, основанный на элементах добавляет дочерей во время построения элемента.
Найдите ссылки на дочерние элементы настраиваемого контроля.
Выставляйте свойства и методы, а также инкапсулируйте логику в свой пользовательский контроллер так же, как вы делаете это с любыми классами C#.
Подключите свой пользовательский контроллер к коду игры или приложения. Вы также можете зарегистрировать обратные вызова событий для реализации взаимодействия пользователя.
UXML — первый подход
При этом подходе вы включаете свой пользовательский элемент CardElement в иерархии UXML документ и объявить его дочерние элементы непосредственно под ним, и использовать иерархию UXML документа в качестве шаблонЭтот подход позволяет получить более простое решение с фиксированным UI структура в рамках иерархии UXML документ.
Следующие примеры C# и UXML демонстрируют, как использовать подход UXML-первый для создания многоразового UI.
Создание класса пользовательского элемента управления
Создайте скрипт C#, описывающий пользовательский элемент управления CardElement. Класс этого элемента задаёт изображение и значения значка для CardElement.
using UnityEngine;
using UnityEngine.UIElements;
// Define the custom control type.
[UxmlElement]
public partial class CardElement : VisualElement
{
private VisualElement portraitImage => this.Q("image");
private Label attackBadge => this.Q<Label>("attack-badge");
private Label healthBadge => this.Q<Label>("health-badge");
// Use the Init() approach instead of a constructor because
// we don't have children yet.
public void Init(Texture2D image, int health, int attack)
{
portraitImage.style.backgroundImage = image;
attackBadge.text = health;
healthBadge.text = attack;
}
// Custom controls need a default constructor.
public CardElement() {}
}
Определение иерархии пользовательского элемента управления
Создайте документ UXML (CardElement.uxml), который определяет иерархию CardElement. В этом примере CardElement стилизуется файлом USS.
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<Style src="CardElementUI.uss" />
<CardElement>
<ui:VisualElement name="image" />
<ui:VisualElement name="stats">
<ui:Label name="attack-badge" class="badge" />
<ui:Label name="health-badge" class="badge" />
</ui:VisualElement>
</CardElement>
</ui:UXML>
Подключите к игре настраиваемые элементы управления
Вы можете подключить свой пользовательский контроллер к игре следующим образом:
- Создайте экземпляр
CardElement.uxmlвнутри родительского документа UXML. Вы можете перемещаться туда и обратно между иерархией UXML и этим документом UXML в UI Builder. - Создайте экземпляр
CardElement.uxml, содержащийCardElementиз скрипта MonoBehaviour C#. Вы должны использовать UQuery, чтобы найти CardElement перед добавлением его в сцену.
Вы вызываете Init() после добавления пользовательского контроля в сцену.
Вы также можете добавить действия, связанные с игрой, такие как событие щелчка для взаимодействия с элементами.
Создание экземпляра внутри родительского UXML
Ниже показан пример экземплярирования в UXML:
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<ui:Template name="CardElement" src="CardElement.uxml"/>
<ui:Instance template="CardElement"/>
<ui:Instance template="CardElement"/>
<ui:Instance template="CardElement"/>
</ui:UXML>
Информацию о том, как отобразить документ UXML в вашей игре, см. в Отображение UI в представлении Game.
Создание экземпляра непосредственно в C#
Примечание: Для целей обучения, пример кода на этой странице использует папку Ресурсы метод для загрузки UXML файлов, что удобно. Однако, этот метод не масштабируется хорошо. Рекомендуется использовать другие методы для загрузки ссылок для ваших производственных проектов.
Ниже показан пример экземплярирования в C#:
using UnityEngine;
using UnityEngine.UIElements;
public class UIManager : MonoBehaviour
{
public void Start()
{
UIDocument document = GetComponent<UIDocument>();
// Load the UXML document that defines the hierarchy of CardElement.
// It assumes the UXML file is placed at the "Resources" folder.
VisualTreeAsset template = Resources.Load<VisualTreeAsset>("CardElement");
// Create a loop to modify properties and perform interactions
// for each card. It assumes that you have created a function
// called `GetCards()` to get all the cards in your game.
foreach(Card card in GetCards())
{
// Instantiate a template container.
var templateContainer = template.Instantiate();
// Find the custom element inside the template container.
var cardElement = templateContainer.Q<CardElement>();
// Add the custom element into the scene.
document.rootVisualElement.Add(cardElement);
// Initialize the card.
cardElement.Init(card.image, card.health, card.attack);
// Register an event callback for additional interaction.
cardElement.RegisterCallback<ClickEvent>(SomeInteraction);
}
}
private void SomeInteraction(ClickEvent evt)
{
// Interact with the elements here.
}
}
Подход, основанный на элементах
При этом подходе вы включаете только дочерние элементы в иерархию документа UXML и используете C# для загрузить документ иерархии UXML в определение класса CardElement. Такой подход даёт гибкую структуру интерфейса для пользовательских элементов управления. Например, в зависимости от условий можно загружать разные UXML-документы иерархии.
Следующие примеры C# и UXML демонстрируют, как использовать подход элемент-первый для создания многоразовых UI.
Создание класса пользовательского элемента управления
Создайте скрипт C#, который определяет пользовательский контроллер CardElement. определение конструктора для назначения значений изображения и значка CardElement, пользовательский элемент управления загружает UXML-документ иерархии в своём определении класса.
using UnityEngine;
using UnityEngine.UIElements;
// Define the custom control type.
[UxmlElement]
public partial class CardElement : VisualElement
{
private VisualElement portraitImage => this.Q("image");
private Label attackBadge => this.Q<Label>("attack-badge");
private Label healthBadge => this.Q<Label>("health-badge");
// Custom controls need a default constructor. This default constructor
// calls the other constructor in this class.
public CardElement() {}
// Define a constructor that loads the UXML document that defines
// the hierarchy of CardElement and assigns an image and badge values.
public CardElement(Texture2D image, int health, int attack)
{
// It assumes the UXML file is called "CardElement.uxml" and
// is placed at the "Resources" folder.
var asset = Resources.Load<VisualTreeAsset>("CardElement");
asset.CloneTree(this);
portraitImage.style.backgroundImage = image;
attackBadge.text = attack.ToString();
healthBadge.text = health.ToString();
}
}
Примечание: Если у вас есть проблемы с производительностью, используйте ленивую инициализацию, чтобы сохранить поля для кэширования ссылок и избежать слишком частого переоценки запросов.
Определение иерархии пользовательского элемента управления
Создайте документ UXML (CardElement.uxml), который определяет иерархию дочерних элементов CardElement. В примере стили CardElement с файлом USS.
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<Style src="CardElementUI.uss" />
<ui:VisualElement name="image" />
<ui:VisualElement name="stats">
<ui:Label name="attack-badge" class="badge" />
<ui:Label name="health-badge" class="badge" />
</ui:VisualElement>
</ui:UXML>
Подключите к игре настраиваемые элементы управления
Для подключения настраиваемого контроллера к игре выполните следующие действия:
- Создайте экземпляр
CardElement.uxmlвнутри родительского документа UXML. В UI Builder нельзя перемещаться между иерархией UXML и этим документом UXML, поскольку дочерние элементы загружаются из C#. - Создать экземпляр
CardElement.uxml, содержащийCardElementиз скрипта MonoBehaviour C#.
Вызов конструктора перед добавлением пользовательского контроля в сцену.
Вы также можете добавить действия, связанные с игрой, такие как событие щелчка для взаимодействия с элементами.
Создание экземпляра внутри родительского UXML
Ниже показан пример экземплярирования в UXML:
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<CardElement />
<CardElement />
<CardElement />
</ui:UXML>
Информацию о том, как отобразить документ UXML в вашей игре, см. в Отображение UI в представлении Game.
Создание экземпляра непосредственно в C#
Ниже показан пример экземплярирования в C#:
using UnityEngine;
using UnityEngine.UIElements;
public class UIManager : MonoBehaviour
{
public void Start()
{
UIDocument document = GetComponent<UIDocument>();
// Create a loop to modify properties and perform interactions
// for each card. It assumes that you have created a function
// called `GetCards()` to get all the cards in your game.
foreach(Card card in GetCards())
{
var cardElement = new CardElement(card.image, card.health, card.attack);
// Register an event callback for additional interaction.
cardElement.RegisterCallback<ClickEvent>(SomeInteraction);
// Add the custom element into the scene.
document.rootVisualElement.Add(cardElement);
}
}
private void SomeInteraction(ClickEvent evt)
{
// Interact with the elements here.
}
}
Создание более сложных элементов
По мере того, как UI вашего проекта становится более сложным, лучше изолировать логику в компоненты более высокого уровня. Это облегчает оркестровку UI для остальной части игры или приложения.
Вы можете применять концепции, описанные на этой странице, для постепенного создания специализированных компонентов из меньших, более общих компонентов. Например, для создания главного экрана заголовка, с которого пользователь может получить доступ к меню Настройки и разделу О, вы можете создать элемент TitleScreenManager с тремя различными дочерними документами UXML. Каждый определяет свои собственные элементы: Title, Options и About.