From a6e5cb1da77417e6c80c4ecfed5ebb25221a4073 Mon Sep 17 00:00:00 2001 From: Jonni Liljamo Date: Mon, 9 Jun 2025 21:45:19 +0300 Subject: [PATCH] feat(demo): add bevy UI for throw and despawn --- crates/bevy_dice_demo/src/main.rs | 45 ++++----- crates/bevy_dice_demo/src/ui.rs | 146 ++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 21 deletions(-) create mode 100644 crates/bevy_dice_demo/src/ui.rs diff --git a/crates/bevy_dice_demo/src/main.rs b/crates/bevy_dice_demo/src/main.rs index c104f1b..33f32c6 100644 --- a/crates/bevy_dice_demo/src/main.rs +++ b/crates/bevy_dice_demo/src/main.rs @@ -4,6 +4,8 @@ * This file is licensed under MIT, see LICENSE for more information. */ +#![allow(clippy::type_complexity)] + use std::collections::HashMap; use avian3d::prelude::*; @@ -17,6 +19,7 @@ use bevy_inspector_egui::quick::WorldInspectorPlugin; extern crate console_error_panic_hook; mod toast; +mod ui; fn main() { #[cfg(feature = "wasm")] @@ -58,7 +61,8 @@ fn main() { ..Default::default() }); - app.add_plugins(DicePlugin).add_plugins(toast::ToastPlugin); + app.add_plugins(DicePlugin) + .add_plugins((toast::ToastPlugin, ui::UIPlugin)); app.init_resource::(); app.init_state::() @@ -71,7 +75,9 @@ fn main() { app.add_systems(Update, rotate_light); - app.add_observer(despawn_dice).add_observer(show_die_result); + app.add_observer(throw_die) + .add_observer(despawn_dice) + .add_observer(show_die_result); app.run(); } @@ -175,7 +181,7 @@ impl Default for UIState { } } -fn ui(mut commands: Commands, mut contexts: EguiContexts, mut r_ui_state: ResMut) { +fn ui(mut contexts: EguiContexts, mut r_ui_state: ResMut) { egui::Window::new("Demo").show(contexts.ctx_mut(), |ui| { egui::ComboBox::from_label("") .selected_text(format!("{:?}", r_ui_state.selected_variant)) @@ -188,29 +194,26 @@ fn ui(mut commands: Commands, mut contexts: EguiContexts, mut r_ui_state: ResMut ); } }); - - if ui.button("Throw").clicked() { - commands.trigger(ThrowDie { - id: r_ui_state.die_id, - variant: r_ui_state.selected_variant, - angular_velocity: None, - position: Some(Vec3::new(0.0, 4.0, 0.0)), - rotation: None, - }); - r_ui_state.die_id += 1; - } - - if ui.button("Despawn Dice").clicked() { - commands.trigger(DespawnDice); - } }); } -#[derive(Event)] -struct DespawnDice; +fn throw_die( + _trigger: Trigger, + mut commands: Commands, + mut r_ui_state: ResMut, +) { + commands.trigger(ThrowDie { + id: r_ui_state.die_id, + variant: r_ui_state.selected_variant, + angular_velocity: None, + position: Some(Vec3::new(0.0, 4.0, 0.0)), + rotation: None, + }); + r_ui_state.die_id += 1; +} fn despawn_dice( - _trigger: Trigger, + _trigger: Trigger, mut commands: Commands, q_dice: Query>, ) { diff --git a/crates/bevy_dice_demo/src/ui.rs b/crates/bevy_dice_demo/src/ui.rs new file mode 100644 index 0000000..64b6503 --- /dev/null +++ b/crates/bevy_dice_demo/src/ui.rs @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2025 Jonni Liljamo + * + * This file is licensed under MIT, see LICENSE for more information. + */ + +use bevy::{color::palettes::tailwind::*, prelude::*}; + +const THROW_COLOR: Srgba = LIME_400; +const DESPAWN_COLOR: Srgba = RED_400; + +pub struct UIPlugin; + +impl Plugin for UIPlugin { + fn build(&self, app: &mut App) { + app.add_systems(Startup, setup) + .add_systems(Update, button_interaction); + + app.add_event::() + .add_event::(); + } +} + +#[derive(Component)] +enum ButtonFunction { + Throw, + Despawn, +} + +#[derive(Event)] +pub struct ThrowPressed; + +#[derive(Event)] +pub struct DespawnPressed; + +fn setup(mut commands: Commands) { + commands.spawn(( + Node { + width: Val::Percent(100.0), + height: Val::Percent(100.0), + flex_direction: FlexDirection::Row, + align_items: AlignItems::FlexEnd, + justify_content: JustifyContent::SpaceEvenly, + padding: UiRect::all(Val::Px(8.0)), + overflow: Overflow::scroll_y(), + ..Default::default() + }, + children![ + ( + ButtonFunction::Throw, + Button, + Node { + width: Val::Px(180.0), + height: Val::Px(60.0), + border: UiRect::all(Val::Px(2.0)), + flex_direction: FlexDirection::Column, + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, + margin: UiRect::bottom(Val::Px(4.0)), + ..Default::default() + }, + BackgroundColor(Color::from(THROW_COLOR)), + BorderColor(Color::from(LIME_700)), + BorderRadius::all(Val::Px(10.0)), + BoxShadow::new( + Color::BLACK.with_alpha(0.8), + Val::Px(4.0), + Val::Px(4.0), + Val::DEFAULT, + Val::DEFAULT, + ), + children![( + Text::new("Throw"), + TextFont { + font_size: 18.0, + ..Default::default() + }, + TextColor(Color::BLACK), + ),], + ), + ( + ButtonFunction::Despawn, + Button, + Node { + width: Val::Px(180.0), + height: Val::Px(60.0), + border: UiRect::all(Val::Px(2.0)), + flex_direction: FlexDirection::Column, + justify_content: JustifyContent::Center, + align_items: AlignItems::Center, + margin: UiRect::bottom(Val::Px(4.0)), + ..Default::default() + }, + BackgroundColor(Color::from(DESPAWN_COLOR)), + BorderColor(Color::from(RED_700)), + BorderRadius::all(Val::Px(10.0)), + BoxShadow::new( + Color::BLACK.with_alpha(0.8), + Val::Px(4.0), + Val::Px(4.0), + Val::DEFAULT, + Val::DEFAULT, + ), + children![( + Text::new("Despawn Dice"), + TextFont { + font_size: 18.0, + ..Default::default() + }, + TextColor(Color::BLACK), + ),], + ), + ], + )); +} + +fn button_interaction( + mut commands: Commands, + q_interaction: Query< + (&Interaction, &mut BackgroundColor, &ButtonFunction), + (Changed, With