主题
UI系统
星火编辑器2.0版本中,UI控件能以三种不同的形式来创建。本文档详细介绍这三种创建方式及其使用场景。
目录
方式一:通过数编定义UI模板
类似于单位、技能和Buff,UI控件也拥有对应的数编表类型。用户可以在数编中定义UI模板,并在运行时创建UI控件实例。
1. 定义模板链接
首先,定义一个 GameLink,作为UI模板的标识符:
csharp
// 在 ScopeData.Control.cs 中定义
public static class Control
{
public static readonly GameLink<GameDataControl, GameDataControlButton> MyButton = new("MyButton");
}2. 创建数编模板
然后,使用这个链接创建UI模板数据:
csharp
// 在 ScopeDataLoader 中初始化模板
_ = new GameDataControlButton(Control.MyButton)
{
Layout = new() // 布局属性在 Layout 对象中
{
Width = 200,
Height = 50,
HorizontalAlignment = HorizontalAlignment.Center,
},
Background = new SolidColorBrush(Color.Blue),
Children = [
new GameDataControlLabel(new("MyButtonLabel"))
{
Text = "点击我",
TextColor = Color.White,
}.Link
]
};3. 从模板创建实例
最后,通过模板链接的 CreateButton() 方法创建UI控件实例:
csharp
#if CLIENT
// Control.MyButton 是在第1步中定义的 GameLink 实例
// CreateButton() 是 GameDataControlButton 的实例方法
var button = Control.MyButton.CreateButton();
// 可以进一步修改实例的属性(覆盖模板设置)
button.Width = 250; // 覆盖模板的 Width
button.AddToVisualTree(); // 将按钮添加到可视化树中
#endif适用场景
- 静态UI - 界面结构固定,不需要频繁改变
- 复杂布局 - 嵌套层次多,通过数编定义更清晰
- 可重用组件 - 需要在多处使用相同的UI模板
方式二:运行时动态创建UI
有时候你可能只想创建一个按钮,但不希望事先为它定义数编,那么你可以直接使用 Button 类来创建UI控件实例。
基本创建
csharp
#if CLIENT
// 在客户端代码中动态创建
var button = new Button();
button.Width = 200; // 直接属性
button.Height = 50; // 直接属性
button.HorizontalAlignment = HorizontalAlignment.Center; // 直接属性
button.Background = new SolidColorBrush(Color.Blue);
button.AddToVisualTree(); // 将按钮添加到可视化树中
#endif添加子控件
csharp
#if CLIENT
var button = new Button
{
Width = 200,
Height = 50,
HorizontalAlignment = HorizontalAlignment.Center,
Background = new SolidColorBrush(Color.Blue)
};
var label = new Label
{
Text = "点击我",
TextColor = Color.White,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
label.Parent = button;
// 直接订阅事件
button.OnPointerClicked += (sender, e) =>
{
Game.Logger.LogInformation("按钮被点击了!");
};
// 添加到视觉树
button.AddToVisualTree();
#endif模板方式 vs 运行时方式的关键差异
| 特性 | 模板方式(数编) | 运行时方式 |
|---|---|---|
| 布局属性 | Layout.Width | Width |
| 事件订阅 | Event.OnPointerClicked | OnPointerClicked |
| 数据结构 | 预定义结构化数据 | 动态创建 |
| 适用场景 | 静态UI、复杂布局、可重用组件 | 动态UI、游戏内UI、响应式界面 |
适用场景
- 动态UI - 需要根据游戏状态动态创建或修改
- 游戏内UI - 临时显示的提示、通知等
- 响应式界面 - 需要对用户操作做出即时反应
方式三:流式扩展语法
流式扩展语法是星火编辑器2.0版本中引入的一种新的UI创建方式。它使用链式调用的方式来创建UI控件,使代码更加简洁直观。
基本用法
csharp
var button = Button()
.Size(200, 50)
.Center()
.Background(Color.Blue);
button.AddToRoot(); // 将按钮添加到UI根节点当你要创建一个完整界面、菜单页或 HUD 页面时,优先使用 UI.Page() 作为页面根容器:
csharp
var page = UI.Page(
UI.VStack(16,
UI.Title("设置"),
UI.Button("开始游戏")
).FillParent()
);
page.AddToRoot();链式调用
流式扩展语法支持链式调用多个方法,每个方法都返回控件本身,支持连续调用多个方法:
csharp
var button = Button()
.Size(200, 50)
.Center()
.Background(Color.Blue)
.Padding(10)
.Margin(5);优势
- 简洁 - 代码更短,更易读
- 直观 - 方法名清晰表达意图
- 灵活 - 可以随意组合各种设置方法
深入学习
更多流式UI语法的详细信息,请参阅:
- 流式UI布局指南 - 完整的流式UI语法文档
Canvas与路径绘制
星火编辑器2.0版本还增加了全新的Canvas渲染控件,使星火编辑器正式获得了2D游戏开发的能力。
Canvas控件
Canvas控件提供了:
- 2D绘图API - 绘制线条、矩形、圆形等基本图形
- 路径绘制 - 复杂的矢量图形绘制
- 性能优化 - 针对2D游戏的渲染优化
适用场景
- 2D游戏 - 完整的2D游戏开发
- 自定义图形 - 需要绘制特殊图形的场景
- 数据可视化 - 图表、统计信息等
深入学习
更多Canvas绘图的详细信息,请参阅:
- Canvas绘图系统文档 - 完整的Canvas API文档
客户端组件信息
在1.0版本中,物品、物品栏、Buff、技能的客户端实现以及UI界面的设计相当糟糕。用户在客户端触发器中无法获取许多重要的信息。
2.0版本的改进
在2.0版本中,这些对象的客户端版本遵循组件的设计理念,组件拥有自己的客户端版本,拥有许多和服务器版本一致的API。
用户可以以非常直观方便的方法获取它们的信息,比如:
- 物品的属性加成
- Buff的属性加成
- 技能的冷却时间
- 技能的公式值
遍历物品属性加成示例
以下遍历物品动态加成属性的代码在服务端和客户端都有效:
csharp
ItemMod itemMod = ...; // ItemMod是有加成效果的物品类型
// 遍历物品的动态属性修改
foreach (var dynamicMod in itemMod.EnumerateDynamicModifications(slotType))
{
Game.Logger.LogInformation("动态属性加成: {SlotType}, {PropertyLink}, {SubType}, {Value}",
dynamicMod.SlotType, dynamicMod.PropertyLink, dynamicMod.SubType, dynamicMod.Value);
}物品动态属性
"物品动态加成属性"即1.0中的"物品额外属性"概念。现在它被重新设计,命名为"物品动态属性"。
参考实现
可以参考当前 SDK 的官方内置 UI 组件和 code_sample 中的 GameUITest、UIFrameworkTest、ModernUITest 等示例。实现物品栏、技能摇杆、Buff 列表或商城 UI 时,优先查当前文档和示例,再决定复用内置组件或自定义界面。
UI系统最佳实践
选择合适的创建方式
根据不同的使用场景选择合适的UI创建方式:
- 数编模板方式 - 适合静态、复杂、可重用的UI
- 运行时创建方式 - 适合动态、临时、响应式的UI
- 流式语法方式 - 适合简单、快速创建的UI
客户端代码标记
UI代码通常运行在客户端,记得使用 #if CLIENT 标记:
csharp
#if CLIENT
var button = new Button();
// ... UI 创建代码
#endif事件订阅与清理
记得在适当的时候取消事件订阅,避免内存泄漏:
csharp
#if CLIENT
button.OnPointerClicked += OnButtonClick;
// 清理时
button.OnPointerClicked -= OnButtonClick;
#endif如果子控件只是从属于某个父控件,推荐直接设置 Parent 属性。AddChild(...) 仍可使用,但在复杂布局中显式 Parent = panel 更容易和流式布局文档、当前示例保持一致。
下一步
了解了UI系统后,建议继续阅读:
- 当前版本限制 - 了解当前版本的限制和未来展望
- UI 尺寸与适配入口
- 流式UI布局指南 - 深入学习流式UI语法