@@ 11,7 11,8 @@ use std::collections::HashMap;
use avian3d::prelude::*;
use bevy::{asset::LoadState, prelude::*};
use bevy_dice::{DicePlugin, DicePluginConfig, Die, DieResult, DieVariant, ThrowDie};
-use bevy_egui::{EguiContextPass, EguiContexts, EguiPlugin, egui};
+#[cfg(feature = "debug")]
+use bevy_egui::EguiPlugin;
use bevy_embedded_assets::EmbeddedAssetPlugin;
#[cfg(feature = "debug")]
use bevy_inspector_egui::quick::WorldInspectorPlugin;
@@ 47,11 48,11 @@ fn main() {
PhysicsDebugPlugin::default(),
));
+ #[cfg(feature = "debug")]
app.add_plugins((
EguiPlugin {
enable_multipass_for_primary_context: true,
},
- #[cfg(feature = "debug")]
WorldInspectorPlugin::default(),
));
@@ 70,12 71,12 @@ fn main() {
.add_systems(Update, wait_for_assets_to_load)
.add_systems(OnEnter(ExampleState::Running), setup);
- app.init_resource::<UIState>()
- .add_systems(EguiContextPass, ui);
+ app.init_resource::<State>();
app.add_systems(Update, rotate_light);
- app.add_observer(throw_die)
+ app.add_observer(variant_selected)
+ .add_observer(throw_die)
.add_observer(despawn_dice)
.add_observer(show_die_result);
@@ 167,12 168,12 @@ struct ExampleAssets {
}
#[derive(Resource)]
-struct UIState {
+struct State {
selected_variant: DieVariant,
die_id: u32,
}
-impl Default for UIState {
+impl Default for State {
fn default() -> Self {
Self {
selected_variant: DieVariant::D6,
@@ 181,35 182,23 @@ impl Default for UIState {
}
}
-fn ui(mut contexts: EguiContexts, mut r_ui_state: ResMut<UIState>) {
- egui::Window::new("Demo").show(contexts.ctx_mut(), |ui| {
- egui::ComboBox::from_label("")
- .selected_text(format!("{:?}", r_ui_state.selected_variant))
- .show_ui(ui, |ui| {
- for variant in DieVariant::VALUES {
- ui.selectable_value(
- &mut r_ui_state.selected_variant,
- variant,
- format!("{:?}", variant),
- );
- }
- });
- });
+fn variant_selected(trigger: Trigger<ui::VariantSelected>, mut r_state: ResMut<State>) {
+ r_state.selected_variant = trigger.0;
}
fn throw_die(
_trigger: Trigger<ui::ThrowPressed>,
mut commands: Commands,
- mut r_ui_state: ResMut<UIState>,
+ mut r_state: ResMut<State>,
) {
commands.trigger(ThrowDie {
- id: r_ui_state.die_id,
- variant: r_ui_state.selected_variant,
+ id: r_state.die_id,
+ variant: r_state.selected_variant,
angular_velocity: None,
position: Some(Vec3::new(0.0, 4.0, 0.0)),
rotation: None,
});
- r_ui_state.die_id += 1;
+ r_state.die_id += 1;
}
fn despawn_dice(
@@ 5,19 5,32 @@
*/
use bevy::{color::palettes::tailwind::*, prelude::*};
+use bevy_dice::DieVariant;
const THROW_COLOR: Srgba = LIME_400;
const DESPAWN_COLOR: Srgba = RED_400;
+const VARIANT_COLOR: Srgba = AMBER_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_systems(Startup, setup).add_systems(
+ Update,
+ (
+ button_interaction,
+ variant_dropdown_interaction,
+ variant_button_interaction,
+ ),
+ );
- app.add_event::<ThrowPressed>()
+ app.add_event::<ToggleVariantButtons>()
+ .add_event::<VariantSelected>()
+ .add_event::<ThrowPressed>()
.add_event::<DespawnPressed>();
+
+ app.add_observer(toggle_variant_buttons)
+ .add_observer(set_selected_variant_text);
}
}
@@ 68,25 81,6 @@ fn button(function: ButtonFunction, text: impl Into<String>, color: Srgba) -> im
)
}
-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![
- button(ButtonFunction::Throw, "Throw", THROW_COLOR),
- button(ButtonFunction::Despawn, "Despawn Dice", DESPAWN_COLOR),
- ],
- ));
-}
-
fn button_interaction(
mut commands: Commands,
q_interaction: Query<
@@ 117,3 111,203 @@ fn button_interaction(
}
}
}
+
+#[derive(Component)]
+struct VariantDropdown;
+
+#[derive(Component)]
+struct SelectedVariantText;
+
+#[derive(Event)]
+struct ToggleVariantButtons;
+
+#[derive(Component)]
+struct VariantButton(DieVariant);
+
+#[derive(Event)]
+pub struct VariantSelected(pub DieVariant);
+
+fn variant_button(variant: VariantButton, text: impl Into<String>) -> impl Bundle {
+ (
+ variant,
+ Button,
+ Node {
+ width: Val::Px(120.0),
+ height: Val::Px(40.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(VARIANT_COLOR)),
+ BorderColor(Color::from(VARIANT_COLOR.darker(0.3))),
+ 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,
+ ),
+ Visibility::Hidden,
+ children![(
+ Text::new(text),
+ TextFont {
+ font_size: 18.0,
+ ..Default::default()
+ },
+ TextColor(Color::BLACK),
+ ),],
+ )
+}
+
+fn variant_dropdown_interaction(
+ mut commands: Commands,
+ q_interaction: Query<
+ (&Interaction, &mut BackgroundColor),
+ (Changed<Interaction>, With<VariantDropdown>),
+ >,
+) {
+ for (interaction, mut background) in q_interaction {
+ match *interaction {
+ Interaction::Pressed => {
+ commands.trigger(ToggleVariantButtons);
+ *background = VARIANT_COLOR.darker(0.3).into();
+ }
+ Interaction::Hovered => {
+ *background = VARIANT_COLOR.darker(0.1).into();
+ }
+ Interaction::None => {
+ *background = VARIANT_COLOR.into();
+ }
+ }
+ }
+}
+
+fn toggle_variant_buttons(
+ _trigger: Trigger<ToggleVariantButtons>,
+ mut q_variant_button_visibilities: Query<&mut Visibility, With<VariantButton>>,
+) {
+ for mut visibility in &mut q_variant_button_visibilities {
+ visibility.toggle_visible_hidden();
+ }
+}
+
+fn variant_button_interaction(
+ mut commands: Commands,
+ q_interaction: Query<
+ (&Interaction, &mut BackgroundColor, &VariantButton),
+ (Changed<Interaction>, With<Button>),
+ >,
+) {
+ for (interaction, mut background, variant_button) in q_interaction {
+ match *interaction {
+ Interaction::Pressed => {
+ commands.trigger(ToggleVariantButtons);
+ commands.trigger(VariantSelected(variant_button.0));
+ *background = VARIANT_COLOR.darker(0.3).into();
+ }
+ Interaction::Hovered => {
+ *background = VARIANT_COLOR.darker(0.1).into();
+ }
+ Interaction::None => {
+ *background = VARIANT_COLOR.into();
+ }
+ }
+ }
+}
+
+fn set_selected_variant_text(
+ trigger: Trigger<VariantSelected>,
+ mut q_text: Query<&mut Text, With<SelectedVariantText>>,
+) {
+ if let Ok(mut text) = q_text.single_mut() {
+ **text = format!("{:?}", trigger.0);
+ }
+}
+
+fn setup(mut commands: Commands) {
+ let buttons = commands
+ .spawn((
+ Node {
+ width: Val::Percent(100.0),
+ flex_direction: FlexDirection::Row,
+ align_items: AlignItems::FlexEnd,
+ justify_content: JustifyContent::SpaceEvenly,
+ overflow: Overflow::scroll_y(),
+ ..Default::default()
+ },
+ children![
+ button(ButtonFunction::Throw, "Throw", THROW_COLOR),
+ button(ButtonFunction::Despawn, "Despawn Dice", DESPAWN_COLOR),
+ ],
+ ))
+ .id();
+
+ let dropdown = commands
+ .spawn((
+ Node {
+ width: Val::Percent(100.0),
+ flex_direction: FlexDirection::Column,
+ overflow: Overflow::scroll_y(),
+ ..Default::default()
+ },
+ children![(
+ VariantDropdown,
+ Button,
+ Node {
+ width: Val::Px(120.0),
+ height: Val::Px(40.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(8.0)),
+ ..Default::default()
+ },
+ BackgroundColor(Color::from(VARIANT_COLOR)),
+ BorderColor(Color::from(VARIANT_COLOR.darker(0.3))),
+ 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![(
+ SelectedVariantText,
+ // D6 is the default.
+ Text::new("D6"),
+ TextFont {
+ font_size: 18.0,
+ ..Default::default()
+ },
+ TextColor(Color::BLACK),
+ ),],
+ )],
+ ))
+ .id();
+
+ for variant in DieVariant::VALUES {
+ commands.entity(dropdown).with_child(variant_button(
+ VariantButton(variant),
+ format!("{:?}", variant),
+ ));
+ }
+
+ let layout = commands
+ .spawn((Node {
+ width: Val::Percent(100.0),
+ height: Val::Percent(100.0),
+ flex_direction: FlexDirection::Column,
+ align_items: AlignItems::FlexEnd,
+ justify_content: JustifyContent::SpaceBetween,
+ padding: UiRect::all(Val::Px(8.0)),
+ ..Default::default()
+ },))
+ .id();
+ commands.entity(layout).add_children(&[dropdown, buttons]);
+}