Unity3D :使用逻辑封装 UXML 文档

推荐:将NSDT场景编辑器加入你的3D工具链
3D工具集:NSDT简石数字孪生
使用逻辑封装 UXML 文档
一个预 置是预制的游戏对象您可以在现场.预制件对于创建可重用组件非常有用。视觉元素在用户界面工具包不是游戏对象,因此预制件不适用。但是,可以将自定义控件创建为可重用的 UI 组件,该组件使用逻辑封装元素的特定层次结构。由于 UI 工具包鼓励您将 UI 与游戏或应用程序代码分开,因此可以使用 UXML 定义结构,使用 USS 定义外观,并使用 C# 定义自定义控件的逻辑。
创建可重用的 UI 组件
创建可重用的 UI 组件,例如为卡片调用的自定义控件,该控件显示角色的图像以及生命和攻击统计信息,如下所示:CardElement

以下是完成此操作的一般步骤:
- 在 C# 中,声明一个名为 CardElement 的自定义元素类型。
在 UXML 中,定义自定义控件的层次结构。可以使用两种方法。这两种方法都支持在 C# 和父 UXML 中实例化。CardElement
- UXML 优先方法在元素构造后添加子项。
- 元素优先方法在元素构造期间添加子项。
- 查找对自定义控件的子元素的引用。
- 公开属性和方法,并以与处理任何 C# 类相同的方式封装自定义控件中的逻辑。
- 将自定义控件与游戏或应用程序代码连接。您还可以注册事件回调以实现用户交互。
UXML优先方法
使用此方法,您可以在层次结构 UXML 文档中包含自定义元素 CardElement,并直接在下面声明其子元素,并使用层次结构 UXML 文档作为模板。此方法提供了一种更简单的解决方案,在层次结构 UXML 文档中具有固定的 UI 结构。
以下 C# 和 UXML 示例演示如何使用 UXML 优先方法创建可重用的 UI。
创建自定义控件类
创建定义 CardElement 自定义控件的 C# 脚本。自定义控件类将图像和锁屏提醒值分配给 CardElement。
using UnityEngine;
using UnityEngine.UIElements;
// Define the custom control type.
public class CardElement : VisualElement
{
// Expose the custom control to UXML and UI Builder.
public new class UxmlFactory : UxmlFactory<CardElement> {}
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() {}
}
定义自定义控件的层次结构
创建一个定义 CardElement 层次结构的 UXML 文档 ()。此示例使用 USS 文件设置卡元素的样式。CardElement.uxml
<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>
将自定义控件连接到游戏
可以通过以下命令将自定义控件连接到游戏:
- 在父 UXML 文档中实例化。您可以在 UI 生成器中的层次结构 UXML 和此 UXML 文档之间来回导航。
CardElement.uxml
- 从 MonoBehavior C# 脚本实例化包含。在将 CardElement 添加到场景中之前,必须使用 UQuery 查找 CardElement。
CardElement.uxmlCardElement
在将自定义控件添加到场景中后调用。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。
直接在 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 类定义中。此方法为自定义控件提供了灵活的 UI 结构。例如,您可以根据特定条件加载不同的层次结构 UXML 文档。
以下 C# 和 UXML 示例演示如何使用元素优先方法创建可重用的 UI。
创建自定义控件类
创建定义 CardElement 自定义控件的 C# 脚本。除了定义构造函数以将图像和锁屏提醒值分配给 CardElement 之外,自定义控件还在其类定义中加载层次结构 UXML 文档。
using UnityEngine;
using UnityEngine.UIElements;
// Define the custom control type.
public class CardElement : VisualElement
{
// Expose the custom control to UXML and UI Builder.
public new class UxmlFactory : UxmlFactory<CardElementA> {}
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 = health.ToString();
healthBadge.text = attack.ToString();
}
}
注意:如果您有性能问题,请使用延迟初始化来保留字段以缓存引用,并避免过于频繁地重新评估查询。
定义自定义控件的层次结构
创建一个 UXML 文档 (),用于定义 CardElement 的子元素的层次结构。该示例使用 USS 文件设置了 CardElement 的样式。CardElement.uxml
<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>
将自定义控件连接到游戏
可以通过执行以下操作将自定义控件连接到游戏:
- 在父 UXML 文档中实例化。在 UI 生成器中,无法在层次结构 UXML 和此 UXML 文档之间来回导航,因为子元素是从 C# 加载的。
CardElement.uxml
- 从 MonoBehavior C# 脚本实例化包含。
CardElement.uxmlCardElement
在将自定义控件添加到场景之前调用构造函数。

您还可以添加与游戏相关的操作,例如单击事件以与元素进行交互。
在父 UXML 中实例化
下面显示了 UXML 中的实例化示例:
<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
<CardElement />
<CardElement />
<CardElement />
</ui:UXML>
有关如何在游戏中呈现 UXML 文档的信息,请参阅在游戏视图中呈现 UI。
直接在 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。
您可以应用此页面上的概念,从更小、更通用的组件逐步构建专用组件。例如,要构建主标题屏幕,用户可以从中访问“选项”菜单和“关于”部分,您可以创建一个包含三个不同子 UXML 文档的 TitleScreenManager 元素。每个元素定义自己的元素:标题、选项和关于。

有关更多信息,请参阅此 YouTube 视频。
其他资源
- 使用 UQuery 查找视觉元素
- 使用 UXML 实例作为模板
- 重用 UXML 文件
- 支持运行时 UI
由3D建模学习工作室整理翻译,转载请注明出处!