refactor(dev-tools): reorganize dev tools module code

This commit is contained in:
Kristofers Solo 2024-12-08 18:02:06 +02:00
parent dca6747f83
commit 9ecb38b442
11 changed files with 192 additions and 131 deletions

View File

@ -1,126 +0,0 @@
//! Development tools for the game. This plugin is only enabled in dev builds.
use bevy::{
dev_tools::{
states::log_transitions,
ui_debug_overlay::{DebugUiPlugin, UiDebugOptions},
},
input::common_conditions::input_just_pressed,
prelude::*,
window::PrimaryWindow,
};
use bevy_inspector_egui::{bevy_egui::EguiContext, DefaultInspectorConfigPlugin};
use crate::{
maze::{events::RecreateMazeEvent, MazeConfig},
screens::Screen,
};
use bevy_egui::{
egui::{self, Button, Color32, DragValue, ScrollArea},
EguiPlugin,
};
pub(super) fn plugin(app: &mut App) {
// Log `Screen` state transitions.
app.add_systems(Update, log_transitions::<Screen>)
.add_plugins(EguiPlugin)
.add_plugins(DebugUiPlugin)
.add_plugins(DefaultInspectorConfigPlugin)
.add_systems(Update, inspector_ui)
// Toggle the debug overlay for UI.
.add_systems(
Update,
toggle_debug_ui.run_if(input_just_pressed(TOGGLE_KEY)),
);
}
const TOGGLE_KEY: KeyCode = KeyCode::Backquote;
fn toggle_debug_ui(mut options: ResMut<UiDebugOptions>) {
options.toggle();
}
fn inspector_ui(world: &mut World) {
let Ok(egui_context) = world
.query_filtered::<&mut EguiContext, With<PrimaryWindow>>()
.get_single(world)
else {
return;
};
let mut egui_context = egui_context.clone();
egui::Window::new("UI").show(egui_context.get_mut(), |ui| {
ScrollArea::vertical().show(ui, |ui| {
bevy_inspector_egui::bevy_inspector::ui_for_world(world, ui);
});
});
egui::Window::new("Maze Controls").show(egui_context.get_mut(), |ui| {
if let Some(mut maze_config) = world.get_resource_mut::<MazeConfig>() {
ui.heading("Maze Configuration");
// radius controls
ui.horizontal(|ui| {
ui.label("Radius:");
ui.add(
DragValue::new(&mut maze_config.radius)
.speed(1)
.range(1..=100),
);
});
// height controls
ui.horizontal(|ui| {
ui.label("Height:");
ui.add(
DragValue::new(&mut maze_config.height)
.speed(0.5)
.range(1.0..=50.),
);
});
// start position
ui.horizontal(|ui| {
ui.label("Start Position:");
ui.add(
DragValue::new(&mut maze_config.start_pos.x)
.speed(1)
.prefix("x: "),
);
ui.add(
DragValue::new(&mut maze_config.start_pos.y)
.speed(1)
.prefix("y: "),
);
});
// end position
ui.horizontal(|ui| {
ui.label("End Position:");
ui.add(
DragValue::new(&mut maze_config.end_pos.x)
.speed(1)
.prefix("x: "),
);
ui.add(
DragValue::new(&mut maze_config.end_pos.y)
.speed(1)
.prefix("y: "),
);
});
ui.add_space(8.);
let button = Button::new("Recreate maze").fill(Color32::from_rgb(108, 108, 108));
if ui.add(button).clicked() {
if let Some(mut event_writer) =
world.get_resource_mut::<Events<RecreateMazeEvent>>()
{
event_writer.send(RecreateMazeEvent { floor: 1 });
}
}
}
});
}

4
src/dev_tools/mod.rs Normal file
View File

@ -0,0 +1,4 @@
mod plugin;
mod ui;
pub use plugin::DevToolsPlugin;

36
src/dev_tools/plugin.rs Normal file
View File

@ -0,0 +1,36 @@
use crate::screens::Screen;
use bevy::{
dev_tools::{
states::log_transitions,
ui_debug_overlay::{DebugUiPlugin, UiDebugOptions},
},
input::common_conditions::input_just_pressed,
prelude::*,
};
use bevy_egui::EguiPlugin;
use bevy_inspector_egui::DefaultInspectorConfigPlugin;
use super::ui::{inspector_ui, maze_controls_ui};
#[derive(Debug)]
pub struct DevToolsPlugin;
impl Plugin for DevToolsPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Update, log_transitions::<Screen>)
.add_plugins(EguiPlugin)
.add_plugins(DebugUiPlugin)
.add_plugins(DefaultInspectorConfigPlugin)
.add_systems(Update, (inspector_ui, maze_controls_ui))
.add_systems(
Update,
toggle_debug_ui.run_if(input_just_pressed(TOGGLE_KEY)),
);
}
}
const TOGGLE_KEY: KeyCode = KeyCode::Backquote;
fn toggle_debug_ui(mut options: ResMut<UiDebugOptions>) {
options.toggle();
}

View File

@ -0,0 +1,20 @@
use bevy::{prelude::*, window::PrimaryWindow};
use bevy_egui::egui::{self, ScrollArea};
use bevy_inspector_egui::bevy_egui::EguiContext;
pub(crate) fn inspector_ui(world: &mut World) {
let Ok(egui_context) = world
.query_filtered::<&mut EguiContext, With<PrimaryWindow>>()
.get_single(world)
else {
return;
};
let mut egui_context = egui_context.clone();
egui::Window::new("UI").show(egui_context.get_mut(), |ui| {
ScrollArea::vertical().show(ui, |ui| {
bevy_inspector_egui::bevy_inspector::ui_for_world(world, ui);
});
});
}

View File

@ -0,0 +1,117 @@
use std::ops::RangeInclusive;
use bevy::{prelude::*, window::PrimaryWindow};
use hexx::Hex;
use rand::{thread_rng, Rng};
use crate::maze::{events::RecreateMazeEvent, MazeConfig, MazePluginLoaded};
use bevy_egui::{
egui::{self, emath::Numeric, DragValue, TextEdit, Ui},
EguiContext,
};
pub(crate) fn maze_controls_ui(world: &mut World) {
if world.get_resource::<MazePluginLoaded>().is_none() {
return;
}
let Ok(egui_context) = world
.query_filtered::<&mut EguiContext, With<PrimaryWindow>>()
.get_single(world)
else {
return;
};
let mut egui_context = egui_context.clone();
egui::Window::new("Maze Controls").show(egui_context.get_mut(), |ui| {
if let Some(mut maze_config) = world.get_resource_mut::<MazeConfig>() {
let mut changed = false;
ui.heading("Maze Configuration");
changed |= add_seed_control(ui, &mut maze_config.seed);
changed |= add_drag_value_control(ui, "Radius:", &mut maze_config.radius, 1.0, 1..=100);
changed |=
add_drag_value_control(ui, "Height:", &mut maze_config.height, 0.5, 1.0..=50.0);
changed |= add_position_control(ui, "Start Position:", &mut maze_config.start_pos);
changed |= add_position_control(ui, "End Position:", &mut maze_config.end_pos);
// Trigger recreation if any value changed
if changed {
if let Some(mut event_writer) =
world.get_resource_mut::<Events<RecreateMazeEvent>>()
{
event_writer.send(RecreateMazeEvent { floor: 1 });
}
}
}
});
}
fn add_drag_value_control<T: Numeric>(
ui: &mut egui::Ui,
label: &str,
value: &mut T,
speed: f64,
range: RangeInclusive<T>,
) -> bool {
let mut changed = false;
ui.horizontal(|ui| {
ui.label(label);
let response = ui.add(DragValue::new(value).speed(speed).range(range));
changed = response.changed();
});
changed
}
fn add_position_control(ui: &mut Ui, label: &str, pos: &mut Hex) -> bool {
let mut changed = false;
ui.horizontal(|ui| {
ui.label(label);
let response_x = ui.add(DragValue::new(&mut pos.x).speed(1).prefix("x: "));
let response_y = ui.add(DragValue::new(&mut pos.y).speed(1).prefix("y: "));
changed = response_x.changed() || response_y.changed();
});
changed
}
fn add_seed_control(ui: &mut Ui, seed: &mut u64) -> bool {
let mut changed = false;
ui.horizontal(|ui| {
ui.label("Seed:");
let mut seed_text = seed.to_string();
let response = ui.add(
TextEdit::singleline(&mut seed_text)
.desired_width(150.0)
.hint_text("Enter seed"),
);
// Parse text input when changed
if response.changed() {
if let Ok(new_seed) = seed_text.parse::<u64>() {
*seed = new_seed;
changed = true;
}
}
// New random seed button
if ui.button("🎲").clicked() {
*seed = thread_rng().gen();
changed = true;
}
// Copy button
if ui.button("📋").clicked() {
ui.output_mut(|o| o.copied_text = seed.to_string());
}
});
changed
}

5
src/dev_tools/ui/mod.rs Normal file
View File

@ -0,0 +1,5 @@
mod inspector;
mod maze_controls;
pub(crate) use inspector::inspector_ui;
pub(crate) use maze_controls::maze_controls_ui;

View File

@ -70,7 +70,7 @@ impl Plugin for AppPlugin {
// Enable dev tools for dev builds.
#[cfg(feature = "dev")]
app.add_plugins(dev_tools::plugin);
app.add_plugins(dev_tools::DevToolsPlugin);
}
}

View File

@ -7,8 +7,8 @@ pub mod plugin;
mod resources;
mod systems;
pub use resources::MazeConfig;
pub use resources::{MazeConfig, MazePluginLoaded};
pub fn spawn_grid(world: &mut World) {
pub fn spawn_maze(world: &mut World) {
MazePlugin.apply(world);
}

View File

@ -7,7 +7,7 @@ use super::{
events::RecreateMazeEvent,
resources::Layout,
systems::{self, recreation::handle_maze_recreation_event},
MazeConfig,
MazeConfig, MazePluginLoaded,
};
#[derive(Default)]
@ -24,6 +24,7 @@ impl Plugin for MazePlugin {
impl Command for MazePlugin {
fn apply(self, world: &mut World) {
world.insert_resource(MazePluginLoaded);
world.run_system_once(systems::setup::setup);
}
}

View File

@ -5,6 +5,10 @@ use hexx::{Hex, HexLayout, HexOrientation};
use rand::{rngs::StdRng, thread_rng, Rng, SeedableRng};
use thiserror::Error;
#[derive(Debug, Default, Reflect, Resource)]
#[reflect(Resource)]
pub struct MazePluginLoaded;
pub(crate) const WALL_SIZE: f32 = 1.0;
#[derive(Debug, Error)]
pub enum MazeConfigError {

View File

@ -5,7 +5,7 @@ use bevy::{input::common_conditions::input_just_pressed, prelude::*};
#[cfg(feature = "demo")]
use crate::demo::level::spawn_level as spawn_level_command;
#[cfg(not(feature = "demo"))]
use crate::maze::spawn_grid as spawn_level_command;
use crate::maze::spawn_maze as spawn_level_command;
use crate::{asset_tracking::LoadResource, audio::Music, screens::Screen};
pub(super) fn plugin(app: &mut App) {