用户界面(UI)

概述

学习目标

  • 理解 UI 系统的基本概念
  • 掌握按钮的创建和使用
  • 学会创建和更新文本
  • 了解 Flexbox 和 Grid 布局
  • 掌握 UI 样式和交互

前置知识要求

  • Bevy 快速入门
  • ECS 基础
  • 窗口管理基础

核心概念

什么是 UI 系统?

UI 系统是 Bevy 中用于创建用户界面的功能。Bevy 的 UI 系统支持按钮、文本、布局、样式等多种 UI 元素。

为什么需要 UI 系统?

  1. 用户交互:UI 系统提供用户交互界面
  2. 信息显示:UI 系统可以显示游戏信息
  3. 菜单系统:UI 系统可以创建菜单和设置界面
  4. HUD:UI 系统可以创建游戏 HUD

UI 系统的核心组件

Bevy UI 系统包含以下核心组件:

  • Node:UI 节点,用于布局和样式
  • Button:按钮组件,用于用户交互
  • Text:文本组件,用于显示文本
  • BackgroundColor:背景颜色组件
  • BorderColor:边框颜色组件
  • Interaction:交互状态组件

基础用法

创建按钮

创建和使用按钮。

源代码文件bevy/examples/ui/button.rs

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
use bevy::prelude::*;

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.init_resource::<InputFocus>()
.add_systems(Startup, setup)
.add_systems(Update, button_system)
.run();
}

const NORMAL_BUTTON: Color = Color::srgb(0.15, 0.15, 0.15);
const HOVERED_BUTTON: Color = Color::srgb(0.25, 0.25, 0.25);
const PRESSED_BUTTON: Color = Color::srgb(0.35, 0.75, 0.35);

fn button_system(
mut input_focus: ResMut<InputFocus>,
mut interaction_query: Query<
(
Entity,
&Interaction,
&mut BackgroundColor,
&mut BorderColor,
&mut Button,
&Children,
),
Changed<Interaction>,
>,
mut text_query: Query<&mut Text>,
) {
for (entity, interaction, mut color, mut border_color, mut button, children) in
&mut interaction_query
{
let mut text = text_query.get_mut(children[0]).unwrap();

match *interaction {
Interaction::Pressed => {
input_focus.set(entity);
**text = "Press".to_string();
*color = PRESSED_BUTTON.into();
*border_color = BorderColor::all(RED);
button.set_changed();
}
Interaction::Hovered => {
input_focus.set(entity);
**text = "Hover".to_string();
*color = HOVERED_BUTTON.into();
*border_color = BorderColor::all(Color::WHITE);
button.set_changed();
}
Interaction::None => {
input_focus.clear();
**text = "Button".to_string();
*color = NORMAL_BUTTON.into();
*border_color = BorderColor::all(Color::BLACK);
}
}
}
}

fn setup(mut commands: Commands, assets: Res<AssetServer>) {
commands.spawn(Camera2d);
commands.spawn(button(&assets));
}

fn button(asset_server: &AssetServer) -> impl Bundle {
(
Node {
width: percent(100),
height: percent(100),
align_items: AlignItems::Center,
justify_content: JustifyContent::Center,
..default()
},
children![(
Button,
Node {
width: px(150),
height: px(65),
border: UiRect::all(px(5)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
},
BorderColor::all(Color::WHITE),
BorderRadius::MAX,
BackgroundColor(Color::BLACK),
children![(
Text::new("Button"),
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 40.0,
..default()
},
)],
)],
)
}

关键要点

  • 使用 Button 组件创建按钮
  • 使用 Interaction 组件检测交互状态
  • 可以响应 PressedHoveredNone 三种交互状态
  • 需要设置 InputFocus 资源以支持无障碍功能

说明
按钮是 UI 系统中最常用的交互元素。通过检测交互状态,可以创建响应式的按钮效果。

创建文本

创建和更新文本。

源代码文件bevy/examples/ui/text.rs

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
use bevy::prelude::*;

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);

// 创建单个文本
commands.spawn((
Text::new("hello\nbevy!"),
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 67.0,
..default()
},
TextShadow::default(),
TextLayout::new_with_justify(Justify::Center),
Node {
position_type: PositionType::Absolute,
bottom: px(5),
right: px(5),
..default()
},
));

// 创建多段文本
commands
.spawn((
Text::new("FPS: "),
TextFont {
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
font_size: 42.0,
..default()
},
))
.with_child((
TextSpan::default(),
TextFont {
font_size: 33.0,
..default()
},
TextColor(GOLD.into()),
));
}

fn text_update_system(
diagnostics: Res<DiagnosticsStore>,
mut query: Query<&mut Text, With<FpsText>>,
) {
for mut text in &mut query {
if let Some(fps) = diagnostics.get(&FrameTimeDiagnosticsPlugin::FPS) {
if let Some(value) = fps.smoothed() {
text.0 = format!("{value:.2}");
}
}
}
}

关键要点

  • 使用 Text 组件创建文本
  • 可以使用 TextFont 设置字体和大小
  • 可以使用 TextColor 设置文本颜色
  • 可以使用 TextSpan 创建多段文本
  • 可以在系统中更新文本内容

说明
文本是 UI 系统中用于显示信息的重要元素。通过更新文本内容,可以显示游戏状态、分数等信息。

Flexbox 布局

使用 Flexbox 布局 UI 元素。

源代码文件bevy/examples/ui/flex_layout.rs

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
use bevy::prelude::*;

fn spawn_layout(mut commands: Commands, asset_server: Res<AssetServer>) {
let font = asset_server.load("fonts/FiraSans-Bold.ttf");
commands.spawn(Camera2d);

commands
.spawn((
Node {
width: percent(100),
height: percent(100),
flex_direction: FlexDirection::Column,
align_items: AlignItems::Center,
padding: UiRect::all(Val::Px(12.0)),
row_gap: Val::Px(12.0),
..Default::default()
},
BackgroundColor(Color::BLACK),
))
.with_children(|builder| {
builder
.spawn(Node {
flex_direction: FlexDirection::Row,
..default()
})
.with_children(|builder| {
// 添加子元素
});
});
}

关键要点

  • 使用 Node 组件创建布局容器
  • 使用 flex_direction 设置布局方向(ColumnRow
  • 使用 align_items 设置对齐方式
  • 使用 justify_content 设置内容分布
  • 使用 paddingrow_gapcolumn_gap 设置间距

说明
Flexbox 布局是创建响应式 UI 的重要工具。通过设置不同的布局属性,可以创建各种 UI 布局。

进阶用法

Grid 布局

使用 Grid 布局 UI 元素。

源代码文件bevy/examples/ui/grid.rs

关键信息

  • 可以使用 Grid 组件创建网格布局
  • 可以设置网格的行和列
  • 可以设置网格项的跨行和跨列
  • 可以设置网格间距和对齐方式

说明
Grid 布局适合创建表格、仪表板等复杂的 UI 布局。

UI 样式

设置 UI 元素的样式。

关键信息

  • 可以使用 BackgroundColor 设置背景颜色
  • 可以使用 BorderColor 设置边框颜色
  • 可以使用 BorderRadius 设置圆角
  • 可以使用 TextShadow 设置文本阴影
  • 可以使用 TextLayout 设置文本布局

说明
UI 样式可以让界面更加美观和易用。通过设置不同的样式属性,可以创建各种视觉效果。

UI 交互

处理 UI 元素的交互。

关键信息

  • 使用 Interaction 组件检测交互状态
  • 可以响应 PressedHoveredNone 三种状态
  • 可以使用 InputFocus 资源支持无障碍功能
  • 可以使用 Button 组件的 set_changed() 方法更新状态

说明
UI 交互是创建响应式界面的关键。通过检测交互状态,可以创建各种交互效果。

实际应用

在游戏开发中的应用场景

UI 系统在游戏开发中有广泛的应用:

  1. 游戏菜单:创建主菜单、设置菜单等
  2. HUD:创建游戏 HUD,显示分数、生命值等
  3. 对话框:创建对话和提示界面
  4. 设置界面:创建设置和配置界面

常见问题

问题 1:如何创建响应式布局?

解决方案:使用 Flexbox 或 Grid 布局,并使用百分比或相对单位设置大小。

问题 2:如何处理 UI 点击事件?

解决方案:使用 Interaction 组件检测交互状态,并在系统中处理交互逻辑。

问题 3:如何更新 UI 文本?

解决方案:在系统中查询文本组件,并使用 Textset() 方法更新内容。

性能考虑

  1. UI 元素数量:尽量减少 UI 元素数量以提高性能
  2. 文本更新频率:避免频繁更新文本内容
  3. 布局计算:合理使用布局属性以减少计算开销
  4. 交互检测:只在需要时检测交互状态

相关资源

相关源代码文件

  • bevy/examples/ui/button.rs - 按钮示例
  • bevy/examples/ui/text.rs - 文本示例
  • bevy/examples/ui/flex_layout.rs - Flexbox 布局示例
  • bevy/examples/ui/grid.rs - Grid 布局示例
  • bevy/examples/ui/directional_navigation.rs - 方向导航示例

官方文档链接

进一步学习建议

  • 学习窗口管理,了解如何在窗口中创建 UI
  • 学习输入处理,了解如何处理 UI 输入事件
  • 学习动画系统,了解如何对 UI 元素进行动画处理

索引返回上级目录