#bevy #ui #gamedev

better_button

通过跟踪更多状态以及这些状态是否刚刚进入或退出,扩展Bevy按钮,以支持按下、悬停和鼠标悬停状态

5个版本 (稳定)

1.2.0 2024年7月20日
1.1.1 2024年7月9日
1.1.0 2024年7月5日
1.0.0 2024年6月29日
0.8.0 2024年6月26日

#440 in 游戏开发

Download history 231/week @ 2024-06-23 152/week @ 2024-06-30 160/week @ 2024-07-07 105/week @ 2024-07-14 35/week @ 2024-07-21 58/week @ 2024-07-28 5/week @ 2024-08-04

每月197次下载

MIT许可证

18KB
266

简介

扩展了Bevy提供的Interaction组件,通过跟踪更多状态以及这些状态是否刚刚进入或退出。这些状态都打包在BButtonBundle组件中,可以通过查询这些组件或监听它们生成的事件来使用。

该库通过根据放置在旁边的Interaction组件更新额外的按钮组件来工作,这确保了与Bevy本身的按钮行为保持一致。

只需将BButtonPlugin添加到您的项目中,并使用BButtonBundle而不是Bevy的ButtonBundle即可开始。

教程

1. 设置

创建一个新的二进制crate,将bevy作为依赖项添加,并将以下代码复制到您的main.rs

use bevy::prelude::*;
use bevy::color::palettes::css::*;

fn main() {
    App::new()
        .add_plugins(
            (
                DefaultPlugins,
            )
        )
        .add_systems(
            Startup,
            (
                spawn_camera,
            ),
        )
        .run();
}

fn spawn_camera(mut commands: Commands) {
    commands.spawn(
        Camera3dBundle {
            ..default()
        }
    );
}

2. 添加BButtonPlugin

导入better_button预处理器并添加BButtonPlugin

use bevy::prelude::*;
use bevy::color::palettes::css::*;
use better_button::prelude::*; // <------- Import the `better_button` prelude.

fn main() {
    App::new()
        .add_plugins(
            (
                DefaultPlugins,
                BButtonPlugin // <------- Add the `BButtonPlugin` to the app.
            )
        )
        .run();
}

这将为您的应用程序添加必要的系统,并注册按钮事件。

3. 创建BButtonBundle

BButtonBundle包含Bevy的ButtonBundle以及由better_button crate提供的按钮组件。

创建一个新的系统来生成您的第一个BButtonBundle

fn spawn_button(mut commands: Commands) {
    commands.spawn(BButtonBundle::new(
        ButtonBundle {
            style: Style
            {
                width: Val::Px(125.0),
                height: Val::Px(125.0),
                align_self: AlignSelf::Center,
                justify_self: JustifySelf::Center,
                ..default()
            },
            background_color: BackgroundColor(WHITE.into()),
            ..default()
        }
    ));
}

将其添加到您的Bevy应用程序中

fn main() {
    App::new()
        .add_plugins(
            (
                DefaultPlugins,
            )
        )
        .add_systems(
            Startup,
            (
                spawn_camera,
                spawn_button // <------- Add the `spawn_button` system to the `Startup` schedule.
            ),
        )
        .run();
}

4. 在Update中响应按钮按下

创建一个新的系统,当按钮被按下时改变其颜色

fn respond_to_button_state(
    mut query: Query<(&BPressState, &mut BackgroundColor)>
) {
    for (state, mut background_color) in &mut query {
        if state.just_entered {
            background_color.0 = YELLOW_GREEN.into();
        }
        if state.just_exited {
            background_color.0 = WHITE.into();
        }
    }
}

该系统查询BPressState组件,这是我们之前使用的BButtonBundle的一部分。

现在将此系统添加到您的应用程序中,但请确保指定它应该在BButtonUpdateSet之前或之后运行。这确保了您的系统在连续运行之间至少运行一次,因为Bevy中系统集和系统的运行顺序可能会从帧到帧发生变化。

fn main() {
    App::new()
        .add_plugins(
            (
                DefaultPlugins,
                BButtonPlugin
            )
        )
        .add_systems(
            Startup,
            (
                spawn_camera,
                spawn_button
            ),
        )
        .add_systems(
            Update, // <------- Make sure it's in the `Update` schedule. The reason will be explained later.
            respond_to_button_state.after(BButtonUpdateSet) // <------- Add the system, and set it to run after the `BButtonUpdateSet`.
        )
        .run();
}

4. 通过读取事件响应按钮按下

由于我们创建的按钮的状态组件在使用 BButtonPlugin 时在 Update 调度中更新,因此我们不能从 FixedUpdate 调度中的按钮组件查询中可靠地读取按钮按下。这是因为 BPressState 组件的 just_enteredjust_exited 属性仅在 Update 调度中设置一次 true。而且,由于在大多数情况下 Update 调度在每个 FixedUpdate 中运行多次,因此 just_enteredjust_exited 属性可能在两个 FixedUpdate 帧之间设置为 true 然后再次设置为 false

这就是使用事件的时候,因为 Bevy 确保在事件消失之前,所有在 UpdateFixedUpdate 中运行的系统都恰好接收到一次生成的任何事件。

创建一个新的系统,当鼠标悬停在按钮上时更改按钮的颜色,这次使用事件

fn respond_to_button_events(
    mut query: Query<&mut BackgroundColor, With<BHoverState>>,
    mut event_reader: EventReader<BHoverEvent>,
) {
    for event in event_reader.read() {
        match event {
            BHoverEvent::JustEntered { entity } => {
                if let Ok(mut background_color) = query.get_mut(*entity) {
                    background_color.0 = YELLOW_GREEN.into();
                }
            }
            BHoverEvent::JustExited { entity } => {
                if let Ok(mut background_color) = query.get_mut(*entity) {
                    background_color.0 = WHITE.into();
                }
            }
        }
    }
}

注释掉之前的系统,并将最新的系统添加到你的 Bevy 应用中的 FixedUpdate 调度中

fn main() {
    App::new()
        .add_plugins(
            (
                DefaultPlugins,
                BButtonPlugin
            )
        )
        .add_systems(
            Startup,
            (
                spawn_camera,  
                spawn_button  
            ),
        )
        // .add_systems(
        //     Update,
        //     respond_to_button_state.after(BButtonUpdateSet)
        // )
        .add_systems(
            FixedUpdate,
            respond_to_button_events, // <------- Add the `respond_to_button_events` system to `FixedUpdate`.
        )
        .run();
}

正如你可能已经注意到的,我们不需要指定新系统是在 BButtonUpdateSet 之前还是之后运行。如前所述,Bevy 确保所有系统恰好一次接收到它们读取的事件。因此,即使我们没有使用 .after(BButtonUpdateSet) 将我们的新系统添加到 Update 调度中,我们也可以确信它不会错过任何内容。

5. 结论

是否使用带有事件的 better_button crate 或直接查询组件取决于你。这两种技术都有其优缺点。

一般来说,当在 Update 调度中工作时,与非游戏逻辑一起工作时直接查询按钮组件通常更容易。例如,更新按钮的视觉。另一方面,在 FixedUpdate 中工作时,使用事件是必要的。例如,使用按钮按下在移动设备上使角色跳跃。

接下来是什么?

请查看 Wiki 了解有关此库的更多信息。

依赖项

~23MB
~427K SLoC