35个版本 (破坏性)
0.33.1 | 2023年11月15日 |
---|---|
0.32.0 | 2023年10月11日 |
0.28.0 | 2023年7月20日 |
0.24.0 | 2023年3月6日 |
0.14.0 | 2022年3月21日 |
#85 in 游戏开发
145KB
2K SLoC
Bevy UI导航
Bevy引擎默认UI库的通用UI导航算法。
[dependencies]
bevy-ui-navigation = "0.33.1"
详细设计规范可在此处找到。
示例
查看examples
目录中的Bevy示例。
Cargo功能
此包公开了cuicui_dsl
功能。默认禁用。启用它将添加dsl
模块,并定义可使用dsl!
宏的NavigationDsl
。
此包公开了bevy_ui
功能。默认启用。关闭此功能可以让您在没有需要Bevy render
功能的情况下编译此包,但是它需要您实现自己的输入处理。查看systems
模块以获取实现您自己的输入处理的提示。
此包公开了pointer_focus
功能。默认启用。禁用它将删除鼠标支持,并删除对bevy_mod_picking
的依赖。
用法
查看此示例以获取快速入门指南。
包文档详尽,但出于实际原因不包括许多示例。此页面包含大多数文档示例,您应查看示例目录以查看展示此包所有功能的示例。
简单案例
要创建一个简单的菜单,在按钮之间进行导航,只需将ButtonBundle
的使用替换为FocusableButtonBundle
。
您需要创建自己的系统来更改聚焦元素的颜色,并手动添加输入系统,但有了这个设置,您将获得:基于物理位置的完整导航,支持控制器、鼠标和键盘。包括可重绑定的映射。
use bevy::prelude::*;
use bevy_ui_navigation::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(DefaultNavigationPlugins)
.run();
}
使用 InputMapping
资源来更改键盘和游戏手柄按钮的映射。
如果您想完全更改输入的处理方式,请按照以下步骤操作。所有与导航引擎的交互都是通过 EventWriter<NavRequest>
实现的。
use bevy::prelude::*;
use bevy_ui_navigation::prelude::*;
fn custom_input_system_emitting_nav_requests(mut events: EventWriter<NavRequest>) {
// handle input and events.send(NavRequest::FooBar)
}
fn main() {
App::new()
.add_plugins((DefaultPlugins, NavigationPlugin::new()))
.add_systems(Update, custom_input_system_emitting_nav_requests)
.run();
}
查看 examples 目录
以获取更多示例代码。
bevy-ui-navigation
提供了多种处理导航操作的方法。查看 NavEventReaderExt
特性(以及 NavEventReader
结构方法)以了解您可以做什么。
use bevy::{app::AppExit, prelude::*};
use bevy_ui_navigation::prelude::*;
#[derive(Component)]
enum MenuButton {
StartGame,
ToggleFullscreen,
ExitGame,
Counter(i32),
//.. etc.
}
fn handle_nav_events(
mut buttons: Query<&mut MenuButton>,
mut events: EventReader<NavEvent>,
mut exit: EventWriter<AppExit>
) {
// Note: we have a closure here because the `buttons` query is mutable.
// for immutable queries, you can use `.activated_in_query` which returns an iterator.
// Do something when player activates (click, press "A" etc.) a `Focusable` button.
events.nav_iter().activated_in_query_foreach_mut(&mut buttons, |mut button| match &mut *button {
MenuButton::StartGame => {
// start the game
}
MenuButton::ToggleFullscreen => {
// toggle fullscreen here
}
MenuButton::ExitGame => {
exit.send(AppExit);
}
MenuButton::Counter(count) => {
*count += 1;
}
//.. etc.
})
}
焦点导航在整个 UI 树中工作,无论您将可聚焦实体放在何处或如何。您只需朝您想要的方向移动,就可以到达那里。
任何 Entity
都可以通过添加 Focusable
组件来转换为可聚焦实体。要做到这一点,只需
# use bevy::prelude::*;
# use bevy_ui_navigation::prelude::Focusable;
fn system(mut cmds: Commands, my_entity: Entity) {
cmds.entity(my_entity).insert(Focusable::default());
}
就是这样!现在 my_entity
是导航树的一部分。玩家可以使用控制器像选择其他任何 Focusable
元素一样选择它。
您可能希望将聚焦按钮以不同于其他按钮的方式渲染,这可以通过以下方式使用 Changed<Focusable>
查询参数实现
use bevy::prelude::*;
use bevy_ui_navigation::prelude::{FocusState, Focusable};
fn button_system(
mut focusables: Query<(&Focusable, &mut BackgroundColor), Changed<Focusable>>,
) {
for (focus, mut color) in focusables.iter_mut() {
let new_color = if matches!(focus.state(), FocusState::Focused) {
Color::RED
} else {
Color::BLACK
};
*color = new_color.into();
}
}
快速反馈
您希望交互反馈是快速的。这意味着交互反馈应该与焦点更改在同一个帧中运行。为了使这每帧发生,您应该在您的应用中使用 NavRequestSystem
标签将 button_system
添加到您的应用中
use bevy::prelude::*;
use bevy_ui_navigation::prelude::{NavRequestSystem, NavRequest, NavigationPlugin};
fn custom_mouse_input(mut events: EventWriter<NavRequest>) {
// handle input and events.send(NavRequest::FooBar)
}
fn main() {
App::new()
.add_plugins((DefaultPlugins, NavigationPlugin::new()))
// ...
.add_systems(Update, (
// Add input systems before the focus update system
custom_mouse_input.before(NavRequestSystem),
// Add the button color update system after the focus update system
button_system.after(NavRequestSystem),
))
// ...
.run();
}
// Implementation from earlier
fn button_system() {}
更复杂的使用案例
锁定
如果您需要暂时抑制导航算法,可以将 Focusable
声明为 Focusable::lock
。
例如,如果您想实现具有自己控制的自定义小部件,或者您想在游戏中禁用菜单导航,这将非常有用。要恢复导航系统,您需要发送 NavRequest::Free
。
NavRequest::FocusOn
您不能直接操作哪个实体被聚焦,因为我们需要在后台跟踪很多事务以使导航按预期工作。但是,您可以使用 NavRequest::FocusOn
将聚焦元素设置为任何任意的 Focusable
实体。
use bevy::prelude::*;
use bevy_ui_navigation::prelude::NavRequest;
fn set_focus_to_arbitrary_focusable(
entity: Entity,
mut requests: EventWriter<NavRequest>,
) {
requests.send(NavRequest::FocusOn(entity));
}
设置第一个聚焦元素
您可能希望能够选择哪个元素首先获得焦点。默认情况下,系统会选择它找到的第一个Focusable
。要改变这种行为,使用Focusable
的Focusable::prioritized
方法创建一个优先级更高的Focusable
。
MenuBuilder
假设您有一个更复杂的游戏,其中包含菜单、子菜单和子子菜单等。例如,在您的日常2021年AAA游戏中,要更改抗锯齿,您需要通过几个菜单进行操作
game menu → options menu → graphics menu → custom graphics menu → AA
在这种情况下,您需要能够指定前一个菜单中的哪个按钮可以跳转到下一个菜单(例如,您需要按游戏菜单中的“选项”按钮才能访问选项菜单)。
为此,您需要使用MenuBuilder
。
MenuBuilder
的高级使用方法如下
- 首先,您需要使用
MenuBuilder::Root
创建一个“根”菜单。 - 您需要在ECS中创建一个带有
Focusable
组件的“选项”按钮。要将按钮链接到您的选项菜单,您需要执行以下操作之一- 除了将
Focusable
组件添加到您的选项按钮外,还要添加一个Name("opt_btn_name")
组件。 - 预先创建选项按钮并将它的
Entity
id(例如:let opt_btn = commands.spawn(FocusableButtonBundle).id();
)存储起来
- 除了将
- 到包含所有选项菜单
Focusable
实体的NodeBundle
中,您需要添加以下组件- 如果您选择了添加
Name
组件,请使用MenuBuilder::from_named("opt_btn_name")
。 - 如果您有一个
Entity
id,请使用MenuBuilder::EntityParent(opt_btn)
。
- 如果您选择了添加
在代码中,这看起来像这样
use bevy::prelude::*;
use bevy_ui_navigation::prelude::{Focusable, MenuSetting, MenuBuilder};
use bevy_ui_navigation::components::FocusableButtonBundle;
struct SaveFile;
impl SaveFile {
fn bundle(&self) -> impl Bundle {
// UI bundle to show this in game
NodeBundle::default()
}
}
fn spawn_menu(mut cmds: Commands, save_files: Vec<SaveFile>) {
let menu_node = NodeBundle {
style: Style { flex_direction: FlexDirection::Column, ..Default::default()},
..Default::default()
};
let button = FocusableButtonBundle::from(ButtonBundle {
background_color: Color::rgb(1.0, 0.3, 1.0).into(),
..Default::default()
});
let mut spawn = |bundle: &FocusableButtonBundle, name: &'static str| {
cmds.spawn(bundle.clone()).insert(Name::new(name)).id()
};
let options = spawn(&button, "options");
let graphics_option = spawn(&button, "graphics");
let audio_options = spawn(&button, "audio");
let input_options = spawn(&button, "input");
let game = spawn(&button, "game");
let quit = spawn(&button, "quit");
let load = spawn(&button, "load");
// Spawn the game menu
cmds.spawn(menu_node.clone())
// Root Menu vvvvvvvvvvvvvvvvv
.insert((MenuSetting::new(), MenuBuilder::Root))
.push_children(&[options, game, quit, load]);
// Spawn the load menu
cmds.spawn(menu_node.clone())
// Sub menu accessible through the load button
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
.insert((MenuSetting::new(), MenuBuilder::EntityParent(load)))
.with_children(|cmds| {
// can only access the save file UI nodes from the load menu
for file in save_files.iter() {
cmds.spawn(file.bundle()).insert(Focusable::default());
}
});
// Spawn the options menu
cmds.spawn(menu_node)
// Sub menu accessible through the "options" button
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
.insert((MenuSetting::new(), MenuBuilder::from_named("options")))
.push_children(&[graphics_option, audio_options, input_options]);
}
使用此功能,您的游戏菜单将与选项菜单隔离,您只能通过发送 NavRequest::Action
来访问它,当 options_button
被聚焦时,或者通过发送一个 NavRequest::FocusOn(entity)
,其中 entity
是 graphics_option
、audio_options
或 input_options
中的任何一个。
注意,如果您使用的是 systems
模块 提供的默认输入系统之一,您无需手动发送 NavRequest
。
具体来说,Focusable
实体之间的导航将被限制在同一个 MenuSetting
的子 Focusable
中。这创建了一个自包含的菜单。
MenuSetting
类型
要定义一个菜单,您需要 MenuBuilder
和 MenuSetting
组件。
MenuSetting
让您可以精细控制菜单内的导航方式
MenuSetting::new().wrapping()
启用循环导航,在屏幕边缘超出时“循环”到另一边。MenuSetting::new().scope()
创建一个“范围”菜单,可以捕获即使聚焦实体在可从该菜单访问的另一个子菜单中时也会触发的NavRequest::ScopeMove
请求。这就像您期望的选项卡菜单的行为。
有关详细信息,请参阅 MenuSetting
文档或“终极”菜单导航示例 "ultimate" menu navigation example。
标记
如果您需要知道 NavEvent::FocusChanged
的来源是哪个菜单,您可以在 mark
模块中使用 NavMarker
。
在 "marking.rs" 示例 中有一个使用演示。
使用键盘回车键(Enter)进行菜单操作
触发菜单操作的默认 InputMapping
键是空格键。要使用回车键,更改 key_action
属性。
否则,如果您没有使用默认输入处理,请添加此系统
use bevy::prelude::*;
use bevy_ui_navigation::prelude::{NavRequest, NavRequestSystem};
fn main() {
App::new()
// ...
.add_systems(Update, (
return_trigger_action.before(NavRequestSystem),
));
}
fn return_trigger_action(mut requests: EventWriter<NavRequest>, input: Res<Input<KeyCode>>) {
if input.just_pressed(KeyCode::Return) {
requests.send(NavRequest::Action);
}
}
变更日志
请查看变更日志:https://github.com/nicopap/ui-navigation/blob/master/CHANGELOG.md
版本矩阵
bevy | 支持的最新版本 |
---|---|
0.12 | 0.33.1 |
0.11 | 0.32.0 |
0.10 | 0.24.1 |
0.9 | 0.23.1 |
0.8 | 0.21.0 |
0.7 | 0.18.0 |
0.6 | 0.14.0 |
许可证
版权所有 © 2022 Nicola Papale
本软件根据您的选择,许可为MIT或Apache 2.0。有关详细信息,请参阅许可证目录。
依赖关系
~22–60MB
~1M SLoC