diff --git a/src/game.rs b/src/game.rs index 3719b9d..01d53f2 100644 --- a/src/game.rs +++ b/src/game.rs @@ -251,14 +251,13 @@ impl Game { backbuffer: NonSendMut, debug_state: Res, mut dirty: ResMut| { - if dirty.0 || *debug_state != DebugState::Off { + if dirty.0 || debug_state.enabled { // Only copy backbuffer to main canvas if debug rendering is off // (debug rendering draws directly to main canvas) - if *debug_state == DebugState::Off { - canvas.copy(&backbuffer.0, None, None).unwrap(); + if !debug_state.enabled { + canvas.present(); } dirty.0 = false; - canvas.present(); } }, ), diff --git a/src/systems/debug.rs b/src/systems/debug.rs index 53edcdd..cc85429 100644 --- a/src/systems/debug.rs +++ b/src/systems/debug.rs @@ -16,22 +16,13 @@ use sdl2::render::{Canvas, Texture, TextureCreator}; use sdl2::ttf::Font; use sdl2::video::{Window, WindowContext}; -#[derive(Resource, Default, Debug, Copy, Clone, PartialEq)] -pub enum DebugState { - #[default] - Off, - Graph, - Collision, +#[derive(Resource, Default, Debug, Copy, Clone)] +pub struct DebugState { + pub enabled: bool, } -impl DebugState { - pub fn next(&self) -> Self { - match self { - DebugState::Off => DebugState::Graph, - DebugState::Graph => DebugState::Collision, - DebugState::Collision => DebugState::Off, - } - } +fn f32_to_u8(value: f32) -> u8 { + (value * 255.0) as u8 } /// Resource to hold the debug texture for persistent rendering @@ -110,7 +101,7 @@ pub fn debug_render_system( colliders: Query<(&Collider, &Position)>, cursor: Res, ) { - if *debug_state == DebugState::Off { + if !debug_state.enabled { return; } let scale = @@ -140,79 +131,86 @@ pub fn debug_render_system( // Draw debug info on the high-resolution debug texture canvas .with_texture_canvas(&mut debug_texture.0, |debug_canvas| { - match *debug_state { - DebugState::Graph => { - // Find the closest node to the cursor + // Find the closest node to the cursor - let closest_node = if let Some(cursor_world_pos) = cursor_world_pos { - map.graph - .nodes() - .map(|node| node.position.distance(cursor_world_pos)) - .enumerate() - .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Less)) - .map(|(id, _)| id) + let closest_node = if let Some(cursor_world_pos) = cursor_world_pos { + map.graph + .nodes() + .map(|node| node.position.distance(cursor_world_pos)) + .enumerate() + .min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Less)) + .map(|(id, _)| id) + } else { + None + }; + + debug_canvas.set_draw_color(Color::GREEN); + for (collider, position) in colliders.iter() { + let pos = position.get_pixel_position(&map.graph).unwrap(); + + // Transform position and size using common methods + let pos = (pos * scale).as_ivec2(); + let size = (collider.size * scale) as u32; + + let rect = Rect::from_center(Point::from((pos.x, pos.y)), size, size); + debug_canvas.draw_rect(rect).unwrap(); + } + + debug_canvas.set_draw_color(Color { + a: f32_to_u8(0.4), + ..Color::RED + }); + debug_canvas.set_blend_mode(sdl2::render::BlendMode::Blend); + for (start_node, end_node) in map.graph.edges() { + let start_node_model = map.graph.get_node(start_node).unwrap(); + let end_node = map.graph.get_node(end_node.target).unwrap().position; + + // Transform positions using common method + let start = transform_position_with_offset(start_node_model.position, scale); + let end = transform_position_with_offset(end_node, scale); + + debug_canvas + .draw_line(Point::from((start.x, start.y)), Point::from((end.x, end.y))) + .unwrap(); + } + + for (id, node) in map.graph.nodes().enumerate() { + let pos = node.position; + + // Set color based on whether the node is the closest to the cursor + debug_canvas.set_draw_color(Color { + a: f32_to_u8(if Some(id) == closest_node { 0.75 } else { 0.6 }), + ..(if Some(id) == closest_node { + Color::YELLOW } else { - None - }; + Color::BLUE + }) + }); - debug_canvas.set_draw_color(Color::RED); - for (start_node, end_node) in map.graph.edges() { - let start_node_model = map.graph.get_node(start_node).unwrap(); - let end_node = map.graph.get_node(end_node.target).unwrap().position; + // Transform position using common method + let pos = transform_position_with_offset(pos, scale); + let size = (2.0 * scale) as u32; - // Transform positions using common method - let start = transform_position_with_offset(start_node_model.position, scale); - let end = transform_position_with_offset(end_node, scale); + debug_canvas + .fill_rect(Rect::new(pos.x - (size as i32 / 2), pos.y - (size as i32 / 2), size, size)) + .unwrap(); + } - debug_canvas - .draw_line(Point::from((start.x, start.y)), Point::from((end.x, end.y))) - .unwrap(); - } + // Render node ID if a node is highlighted + if let Some(closest_node_id) = closest_node { + let node = map.graph.get_node(closest_node_id).unwrap(); + let pos = transform_position_with_offset(node.position, scale); - for (id, node) in map.graph.nodes().enumerate() { - let pos = node.position; - - // Set color based on whether the node is the closest to the cursor - debug_canvas.set_draw_color(if Some(id) == closest_node { - Color::YELLOW - } else { - Color::BLUE - }); - - // Transform position using common method - let pos = transform_position_with_offset(pos, scale); - let size = (3.0 * scale) as u32; - - debug_canvas - .fill_rect(Rect::new(pos.x - (size as i32 / 2), pos.y - (size as i32 / 2), size, size)) - .unwrap(); - } - - // Render node ID if a node is highlighted - if let Some(closest_node_id) = closest_node { - let node = map.graph.get_node(closest_node_id).unwrap(); - let pos = transform_position_with_offset(node.position, scale); - - let surface = font.render(&closest_node_id.to_string()).blended(Color::WHITE).unwrap(); - let texture = texture_creator.create_texture_from_surface(&surface).unwrap(); - let dest = Rect::new(pos.x + 10, pos.y - 5, texture.query().width, texture.query().height); - debug_canvas.copy(&texture, None, dest).unwrap(); - } - } - DebugState::Collision => { - debug_canvas.set_draw_color(Color::GREEN); - for (collider, position) in colliders.iter() { - let pos = position.get_pixel_position(&map.graph).unwrap(); - - // Transform position and size using common methods - let pos = (pos * scale).as_ivec2(); - let size = (collider.size * scale) as u32; - - let rect = Rect::from_center(Point::from((pos.x, pos.y)), size, size); - debug_canvas.draw_rect(rect).unwrap(); - } - } - _ => {} + let surface = font + .render(&closest_node_id.to_string()) + .blended(Color { + a: f32_to_u8(0.4), + ..Color::WHITE + }) + .unwrap(); + let texture = texture_creator.create_texture_from_surface(&surface).unwrap(); + let dest = Rect::new(pos.x + 10, pos.y - 5, texture.query().width, texture.query().height); + debug_canvas.copy(&texture, None, dest).unwrap(); } // Render timing information in the top-left corner @@ -222,4 +220,5 @@ pub fn debug_render_system( // Draw the debug texture directly onto the main canvas at full resolution canvas.copy(&debug_texture.0, None, None).unwrap(); + canvas.present(); } diff --git a/src/systems/player.rs b/src/systems/player.rs index eddb6b0..f94dc43 100644 --- a/src/systems/player.rs +++ b/src/systems/player.rs @@ -57,7 +57,7 @@ pub fn player_control_system( state.exit = true; } GameCommand::ToggleDebug => { - *debug_state = debug_state.next(); + debug_state.enabled = !debug_state.enabled; } GameCommand::MuteAudio => { audio_state.muted = !audio_state.muted; diff --git a/src/systems/render.rs b/src/systems/render.rs index 20d5ac9..18b7ced 100644 --- a/src/systems/render.rs +++ b/src/systems/render.rs @@ -120,4 +120,6 @@ pub fn render_system( }) .err() .map(|e| errors.write(TextureError::RenderFailed(e.to_string()).into())); + + canvas.copy(&backbuffer.0, None, None).unwrap(); } diff --git a/tests/player.rs b/tests/player.rs index 7cb857b..e7a7fe5 100644 --- a/tests/player.rs +++ b/tests/player.rs @@ -21,7 +21,7 @@ fn create_test_world() -> World { // Add resources world.insert_resource(GlobalState { exit: false }); - world.insert_resource(DebugState::Off); + world.insert_resource(DebugState::default()); world.insert_resource(AudioState::default()); world.insert_resource(DeltaTime(1.0 / 60.0)); // 60 FPS world.insert_resource(Events::::default()); @@ -222,7 +222,7 @@ fn test_player_control_system_toggle_debug() { // Check that debug state changed let debug_state = world.resource::(); - assert_eq!(*debug_state, DebugState::Graph); + assert_eq!(debug_state.enabled, true); } #[test] @@ -565,7 +565,7 @@ fn test_player_state_persistence_across_systems() { let position = *query.single(&world).expect("Player should exist"); // Check that the state changes persisted individually - assert_eq!(debug_state_after_toggle, DebugState::Graph, "Debug state should have toggled"); + assert_eq!(debug_state_after_toggle.enabled, true, "Debug state should have toggled"); assert!(audio_muted_after_toggle, "Audio should be muted"); // Player position depends on actual map connectivity