mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-09 06:07:53 -06:00
refactor: optimize debug system, remove redundant code & tests
This commit is contained in:
@@ -218,11 +218,6 @@ impl Graph {
|
|||||||
self.nodes.get(id)
|
self.nodes.get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the total number of nodes in the graph.
|
|
||||||
pub fn node_count(&self) -> usize {
|
|
||||||
self.nodes.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over all nodes in the graph.
|
/// Returns an iterator over all nodes in the graph.
|
||||||
pub fn nodes(&self) -> impl Iterator<Item = &Node> {
|
pub fn nodes(&self) -> impl Iterator<Item = &Node> {
|
||||||
self.nodes.iter()
|
self.nodes.iter()
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ use crate::{
|
|||||||
constants,
|
constants,
|
||||||
events::GameCommand,
|
events::GameCommand,
|
||||||
map::render::MapRenderer,
|
map::render::MapRenderer,
|
||||||
systems::{debug::CursorPosition, input::Bindings},
|
systems::input::{Bindings, CursorPosition},
|
||||||
texture::sprite::{AtlasMapper, SpriteAtlas},
|
texture::sprite::{AtlasMapper, SpriteAtlas},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,9 @@ use crate::constants::{MapTile, BOARD_CELL_SIZE, CELL_SIZE};
|
|||||||
use crate::entity::direction::Direction;
|
use crate::entity::direction::Direction;
|
||||||
use crate::entity::graph::{Graph, Node, TraversalFlags};
|
use crate::entity::graph::{Graph, Node, TraversalFlags};
|
||||||
use crate::map::parser::MapTileParser;
|
use crate::map::parser::MapTileParser;
|
||||||
use crate::map::render::MapRenderer;
|
|
||||||
use crate::systems::movement::NodeId;
|
use crate::systems::movement::NodeId;
|
||||||
use crate::texture::sprite::SpriteAtlas;
|
|
||||||
use bevy_ecs::resource::Resource;
|
use bevy_ecs::resource::Resource;
|
||||||
use glam::{IVec2, Vec2};
|
use glam::{IVec2, Vec2};
|
||||||
use sdl2::render::{Canvas, RenderTarget};
|
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
@@ -165,20 +162,6 @@ impl Map {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders a debug visualization with cursor-based highlighting.
|
|
||||||
///
|
|
||||||
/// This function provides interactive debugging by highlighting the nearest node
|
|
||||||
/// to the cursor, showing its ID, and highlighting its connections.
|
|
||||||
pub fn debug_render_with_cursor<T: RenderTarget>(
|
|
||||||
&self,
|
|
||||||
canvas: &mut Canvas<T>,
|
|
||||||
text_renderer: &mut crate::texture::text::TextTexture,
|
|
||||||
atlas: &mut SpriteAtlas,
|
|
||||||
cursor_pos: glam::Vec2,
|
|
||||||
) -> GameResult<()> {
|
|
||||||
MapRenderer::debug_render_with_cursor(&self.graph, canvas, text_renderer, atlas, cursor_pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds the house structure in the graph.
|
/// Builds the house structure in the graph.
|
||||||
fn build_house(
|
fn build_house(
|
||||||
graph: &mut Graph,
|
graph: &mut Graph,
|
||||||
|
|||||||
@@ -3,14 +3,10 @@
|
|||||||
use crate::constants::{BOARD_CELL_OFFSET, CELL_SIZE};
|
use crate::constants::{BOARD_CELL_OFFSET, CELL_SIZE};
|
||||||
use crate::map::layout::TILE_MAP;
|
use crate::map::layout::TILE_MAP;
|
||||||
use crate::texture::sprite::{AtlasTile, SpriteAtlas};
|
use crate::texture::sprite::{AtlasTile, SpriteAtlas};
|
||||||
use crate::texture::text::TextTexture;
|
|
||||||
use glam::Vec2;
|
|
||||||
use sdl2::pixels::Color;
|
use sdl2::pixels::Color;
|
||||||
use sdl2::rect::{Point, Rect};
|
use sdl2::rect::Rect;
|
||||||
use sdl2::render::{Canvas, RenderTarget};
|
use sdl2::render::{Canvas, RenderTarget};
|
||||||
|
|
||||||
use crate::error::{EntityError, GameError, GameResult};
|
|
||||||
|
|
||||||
/// Handles rendering operations for the map.
|
/// Handles rendering operations for the map.
|
||||||
pub struct MapRenderer;
|
pub struct MapRenderer;
|
||||||
|
|
||||||
@@ -37,111 +33,4 @@ impl MapRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders a debug visualization with cursor-based highlighting.
|
|
||||||
///
|
|
||||||
/// This function provides interactive debugging by highlighting the nearest node
|
|
||||||
/// to the cursor, showing its ID, and highlighting its connections.
|
|
||||||
pub fn debug_render_with_cursor<T: RenderTarget>(
|
|
||||||
graph: &crate::entity::graph::Graph,
|
|
||||||
canvas: &mut Canvas<T>,
|
|
||||||
text_renderer: &mut TextTexture,
|
|
||||||
atlas: &mut SpriteAtlas,
|
|
||||||
cursor_pos: Vec2,
|
|
||||||
) -> GameResult<()> {
|
|
||||||
// Find the nearest node to the cursor
|
|
||||||
let nearest_node = Self::find_nearest_node(graph, cursor_pos);
|
|
||||||
|
|
||||||
// Draw all connections in blue
|
|
||||||
canvas.set_draw_color(Color::RGB(0, 0, 128)); // Dark blue for regular connections
|
|
||||||
for i in 0..graph.node_count() {
|
|
||||||
let node = graph.get_node(i).ok_or(GameError::Entity(EntityError::NodeNotFound(i)))?;
|
|
||||||
let pos = node.position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2();
|
|
||||||
|
|
||||||
for edge in graph.adjacency_list[i].edges() {
|
|
||||||
let end_pos = graph
|
|
||||||
.get_node(edge.target)
|
|
||||||
.ok_or(GameError::Entity(EntityError::NodeNotFound(edge.target)))?
|
|
||||||
.position
|
|
||||||
+ crate::constants::BOARD_PIXEL_OFFSET.as_vec2();
|
|
||||||
canvas
|
|
||||||
.draw_line((pos.x as i32, pos.y as i32), (end_pos.x as i32, end_pos.y as i32))
|
|
||||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw all nodes in green
|
|
||||||
canvas.set_draw_color(Color::RGB(0, 128, 0)); // Dark green for regular nodes
|
|
||||||
for i in 0..graph.node_count() {
|
|
||||||
let node = graph.get_node(i).ok_or(GameError::Entity(EntityError::NodeNotFound(i)))?;
|
|
||||||
let pos = node.position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2();
|
|
||||||
|
|
||||||
canvas
|
|
||||||
.fill_rect(Rect::new(0, 0, 3, 3).centered_on(Point::new(pos.x as i32, pos.y as i32)))
|
|
||||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Highlight connections from the nearest node in bright blue
|
|
||||||
if let Some(nearest_id) = nearest_node {
|
|
||||||
let nearest_pos = graph
|
|
||||||
.get_node(nearest_id)
|
|
||||||
.ok_or(GameError::Entity(EntityError::NodeNotFound(nearest_id)))?
|
|
||||||
.position
|
|
||||||
+ crate::constants::BOARD_PIXEL_OFFSET.as_vec2();
|
|
||||||
|
|
||||||
canvas.set_draw_color(Color::RGB(0, 255, 255)); // Bright cyan for highlighted connections
|
|
||||||
for edge in graph.adjacency_list[nearest_id].edges() {
|
|
||||||
let end_pos = graph
|
|
||||||
.get_node(edge.target)
|
|
||||||
.ok_or(GameError::Entity(EntityError::NodeNotFound(edge.target)))?
|
|
||||||
.position
|
|
||||||
+ crate::constants::BOARD_PIXEL_OFFSET.as_vec2();
|
|
||||||
canvas
|
|
||||||
.draw_line(
|
|
||||||
(nearest_pos.x as i32, nearest_pos.y as i32),
|
|
||||||
(end_pos.x as i32, end_pos.y as i32),
|
|
||||||
)
|
|
||||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Highlight the nearest node in bright green
|
|
||||||
canvas.set_draw_color(Color::RGB(0, 255, 0)); // Bright green for highlighted node
|
|
||||||
canvas
|
|
||||||
.fill_rect(Rect::new(0, 0, 5, 5).centered_on(Point::new(nearest_pos.x as i32, nearest_pos.y as i32)))
|
|
||||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
|
||||||
|
|
||||||
// Draw node ID text (small, offset to top right)
|
|
||||||
text_renderer.set_scale(0.5); // Small text
|
|
||||||
let id_text = format!("#{nearest_id}");
|
|
||||||
let text_pos = glam::UVec2::new(
|
|
||||||
(nearest_pos.x + 4.0) as u32, // Offset to the right
|
|
||||||
(nearest_pos.y - 6.0) as u32, // Offset to the top
|
|
||||||
);
|
|
||||||
if let Err(e) = text_renderer.render(canvas, atlas, &id_text, text_pos) {
|
|
||||||
tracing::error!("Failed to render node ID text: {}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds the nearest node to the given cursor position.
|
|
||||||
pub fn find_nearest_node(graph: &crate::entity::graph::Graph, cursor_pos: Vec2) -> Option<usize> {
|
|
||||||
let mut nearest_id = None;
|
|
||||||
let mut nearest_distance = f32::INFINITY;
|
|
||||||
|
|
||||||
for i in 0..graph.node_count() {
|
|
||||||
if let Some(node) = graph.get_node(i) {
|
|
||||||
let node_pos = node.position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2();
|
|
||||||
let distance = cursor_pos.distance(node_pos);
|
|
||||||
|
|
||||||
if distance < nearest_distance {
|
|
||||||
nearest_distance = distance;
|
|
||||||
nearest_id = Some(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nearest_id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,19 +4,17 @@ use std::cmp::Ordering;
|
|||||||
use crate::constants::BOARD_PIXEL_OFFSET;
|
use crate::constants::BOARD_PIXEL_OFFSET;
|
||||||
use crate::map::builder::Map;
|
use crate::map::builder::Map;
|
||||||
use crate::systems::components::Collider;
|
use crate::systems::components::Collider;
|
||||||
|
use crate::systems::input::CursorPosition;
|
||||||
use crate::systems::movement::Position;
|
use crate::systems::movement::Position;
|
||||||
use crate::systems::profiling::SystemTimings;
|
use crate::systems::profiling::SystemTimings;
|
||||||
use crate::systems::render::BackbufferResource;
|
use crate::systems::render::BackbufferResource;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use glam::Vec2;
|
use glam::{IVec2, UVec2, Vec2};
|
||||||
use sdl2::pixels::Color;
|
use sdl2::pixels::Color;
|
||||||
use sdl2::rect::Rect;
|
use sdl2::rect::{Point, Rect};
|
||||||
use sdl2::render::{Canvas, Texture, TextureCreator};
|
use sdl2::render::{Canvas, Texture, TextureCreator};
|
||||||
use sdl2::video::{Window, WindowContext};
|
use sdl2::video::{Window, WindowContext};
|
||||||
|
|
||||||
#[derive(Resource, Default, Debug, Copy, Clone)]
|
|
||||||
pub struct CursorPosition(pub Vec2);
|
|
||||||
|
|
||||||
#[derive(Resource, Default, Debug, Copy, Clone, PartialEq)]
|
#[derive(Resource, Default, Debug, Copy, Clone, PartialEq)]
|
||||||
pub enum DebugState {
|
pub enum DebugState {
|
||||||
#[default]
|
#[default]
|
||||||
@@ -38,41 +36,9 @@ impl DebugState {
|
|||||||
/// Resource to hold the debug texture for persistent rendering
|
/// Resource to hold the debug texture for persistent rendering
|
||||||
pub struct DebugTextureResource(pub Texture<'static>);
|
pub struct DebugTextureResource(pub Texture<'static>);
|
||||||
|
|
||||||
/// Transforms a position from logical canvas coordinates to output canvas coordinates
|
|
||||||
fn transform_position(pos: (f32, f32), output_size: (u32, u32), logical_size: (u32, u32)) -> (i32, i32) {
|
|
||||||
let scale_x = output_size.0 as f32 / logical_size.0 as f32;
|
|
||||||
let scale_y = output_size.1 as f32 / logical_size.1 as f32;
|
|
||||||
let scale = scale_x.min(scale_y);
|
|
||||||
|
|
||||||
let offset_x = (output_size.0 as f32 - logical_size.0 as f32 * scale) / 2.0;
|
|
||||||
let offset_y = (output_size.1 as f32 - logical_size.1 as f32 * scale) / 2.0;
|
|
||||||
|
|
||||||
let x = (pos.0 * scale + offset_x) as i32;
|
|
||||||
let y = (pos.1 * scale + offset_y) as i32;
|
|
||||||
(x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transforms a position from logical canvas coordinates to output canvas coordinates (with board offset)
|
/// Transforms a position from logical canvas coordinates to output canvas coordinates (with board offset)
|
||||||
fn transform_position_with_offset(pos: (f32, f32), output_size: (u32, u32), logical_size: (u32, u32)) -> (i32, i32) {
|
fn transform_position_with_offset(pos: Vec2, scale: f32) -> IVec2 {
|
||||||
let scale_x = output_size.0 as f32 / logical_size.0 as f32;
|
((pos + BOARD_PIXEL_OFFSET.as_vec2()) * scale).as_ivec2()
|
||||||
let scale_y = output_size.1 as f32 / logical_size.1 as f32;
|
|
||||||
let scale = scale_x.min(scale_y);
|
|
||||||
|
|
||||||
let offset_x = (output_size.0 as f32 - logical_size.0 as f32 * scale) / 2.0;
|
|
||||||
let offset_y = (output_size.1 as f32 - logical_size.1 as f32 * scale) / 2.0;
|
|
||||||
|
|
||||||
let x = ((pos.0 + BOARD_PIXEL_OFFSET.x as f32) * scale + offset_x) as i32;
|
|
||||||
let y = ((pos.1 + BOARD_PIXEL_OFFSET.y as f32) * scale + offset_y) as i32;
|
|
||||||
(x, y)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transforms a size from logical canvas coordinates to output canvas coordinates
|
|
||||||
fn transform_size(size: f32, output_size: (u32, u32), logical_size: (u32, u32)) -> u32 {
|
|
||||||
let scale_x = output_size.0 as f32 / logical_size.0 as f32;
|
|
||||||
let scale_y = output_size.1 as f32 / logical_size.1 as f32;
|
|
||||||
let scale = scale_x.min(scale_y); // Use the smaller scale to maintain aspect ratio
|
|
||||||
|
|
||||||
(size * scale) as u32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders timing information in the top-left corner of the screen
|
/// Renders timing information in the top-left corner of the screen
|
||||||
@@ -133,6 +99,7 @@ fn render_timing_display(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn debug_render_system(
|
pub fn debug_render_system(
|
||||||
mut canvas: NonSendMut<&mut Canvas<Window>>,
|
mut canvas: NonSendMut<&mut Canvas<Window>>,
|
||||||
backbuffer: NonSendMut<BackbufferResource>,
|
backbuffer: NonSendMut<BackbufferResource>,
|
||||||
@@ -146,10 +113,8 @@ pub fn debug_render_system(
|
|||||||
if *debug_state == DebugState::Off {
|
if *debug_state == DebugState::Off {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let scale =
|
||||||
// Get canvas sizes for coordinate transformation
|
(UVec2::from(canvas.output_size().unwrap()).as_vec2() / UVec2::from(canvas.logical_size()).as_vec2()).min_element();
|
||||||
let output_size = canvas.output_size().unwrap();
|
|
||||||
let logical_size = canvas.logical_size();
|
|
||||||
|
|
||||||
// Copy the current backbuffer to the debug texture
|
// Copy the current backbuffer to the debug texture
|
||||||
canvas
|
canvas
|
||||||
@@ -166,7 +131,10 @@ pub fn debug_render_system(
|
|||||||
// Get texture creator before entering the closure to avoid borrowing conflicts
|
// Get texture creator before entering the closure to avoid borrowing conflicts
|
||||||
let mut texture_creator = canvas.texture_creator();
|
let mut texture_creator = canvas.texture_creator();
|
||||||
|
|
||||||
let cursor_world_pos = cursor.0 - BOARD_PIXEL_OFFSET.as_vec2();
|
let cursor_world_pos = match *cursor {
|
||||||
|
CursorPosition::None => None,
|
||||||
|
CursorPosition::Some { position, .. } => Some(position - BOARD_PIXEL_OFFSET.as_vec2()),
|
||||||
|
};
|
||||||
|
|
||||||
// Draw debug info on the high-resolution debug texture
|
// Draw debug info on the high-resolution debug texture
|
||||||
canvas
|
canvas
|
||||||
@@ -175,13 +143,16 @@ pub fn debug_render_system(
|
|||||||
DebugState::Graph => {
|
DebugState::Graph => {
|
||||||
// Find the closest node to the cursor
|
// Find the closest node to the cursor
|
||||||
|
|
||||||
let closest_node = map
|
let closest_node = if let Some(cursor_world_pos) = cursor_world_pos {
|
||||||
.graph
|
map.graph
|
||||||
.nodes()
|
.nodes()
|
||||||
.map(|node| node.position.distance(cursor_world_pos))
|
.map(|node| node.position.distance(cursor_world_pos))
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Less))
|
.min_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap_or(Ordering::Less))
|
||||||
.map(|(id, _)| id);
|
.map(|(id, _)| id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
debug_canvas.set_draw_color(Color::RED);
|
debug_canvas.set_draw_color(Color::RED);
|
||||||
for (start_node, end_node) in map.graph.edges() {
|
for (start_node, end_node) in map.graph.edges() {
|
||||||
@@ -189,34 +160,45 @@ pub fn debug_render_system(
|
|||||||
let end_node = map.graph.get_node(end_node.target).unwrap().position;
|
let end_node = map.graph.get_node(end_node.target).unwrap().position;
|
||||||
|
|
||||||
// Transform positions using common method
|
// Transform positions using common method
|
||||||
let (start_x, start_y) = transform_position_with_offset(
|
let start = transform_position_with_offset(start_node_model.position, scale);
|
||||||
(start_node_model.position.x, start_node_model.position.y),
|
let end = transform_position_with_offset(end_node, scale);
|
||||||
output_size,
|
|
||||||
logical_size,
|
|
||||||
);
|
|
||||||
let (end_x, end_y) = transform_position_with_offset((end_node.x, end_node.y), output_size, logical_size);
|
|
||||||
|
|
||||||
debug_canvas.draw_line((start_x, start_y), (end_x, end_y)).unwrap();
|
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() {
|
for (id, node) in map.graph.nodes().enumerate() {
|
||||||
let pos = node.position;
|
let pos = node.position;
|
||||||
|
|
||||||
// Set color based on whether the node is the closest to the cursor
|
// Set color based on whether the node is the closest to the cursor
|
||||||
if Some(id) == closest_node {
|
debug_canvas.set_draw_color(if Some(id) == closest_node {
|
||||||
debug_canvas.set_draw_color(Color::YELLOW);
|
Color::YELLOW
|
||||||
} else {
|
} else {
|
||||||
debug_canvas.set_draw_color(Color::BLUE);
|
Color::BLUE
|
||||||
}
|
});
|
||||||
|
|
||||||
// Transform position using common method
|
// Transform position using common method
|
||||||
let (x, y) = transform_position_with_offset((pos.x, pos.y), output_size, logical_size);
|
let pos = transform_position_with_offset(pos, scale);
|
||||||
let size = transform_size(4.0, output_size, logical_size);
|
let size = (3.0 * scale) as u32;
|
||||||
|
|
||||||
debug_canvas
|
debug_canvas
|
||||||
.fill_rect(Rect::new(x - (size as i32 / 2), y - (size as i32 / 2), size, size))
|
.fill_rect(Rect::new(pos.x - (size as i32 / 2), pos.y - (size as i32 / 2), size, size))
|
||||||
.unwrap();
|
.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 ttf_context = sdl2::ttf::init().unwrap();
|
||||||
|
let font = ttf_context.load_font("assets/site/TerminalVector.ttf", 12).unwrap();
|
||||||
|
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 => {
|
DebugState::Collision => {
|
||||||
debug_canvas.set_draw_color(Color::GREEN);
|
debug_canvas.set_draw_color(Color::GREEN);
|
||||||
@@ -224,39 +206,16 @@ pub fn debug_render_system(
|
|||||||
let pos = position.get_pixel_position(&map.graph).unwrap();
|
let pos = position.get_pixel_position(&map.graph).unwrap();
|
||||||
|
|
||||||
// Transform position and size using common methods
|
// Transform position and size using common methods
|
||||||
let (x, y) = transform_position((pos.x, pos.y), output_size, logical_size);
|
let pos = (pos * scale).as_ivec2();
|
||||||
let size = transform_size(collider.size, output_size, logical_size);
|
let size = (collider.size * scale) as u32;
|
||||||
|
|
||||||
// Center the collision box on the entity
|
let rect = Rect::from_center(Point::from((pos.x, pos.y)), size, size);
|
||||||
let rect = Rect::new(x - (size as i32 / 2), y - (size as i32 / 2), size, size);
|
|
||||||
debug_canvas.draw_rect(rect).unwrap();
|
debug_canvas.draw_rect(rect).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render node ID if a node is highlighted
|
|
||||||
if let DebugState::Graph = *debug_state {
|
|
||||||
if let Some(closest_node_id) = 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 node = map.graph.get_node(closest_node_id).unwrap();
|
|
||||||
let (x, y) = transform_position_with_offset((node.position.x, node.position.y), output_size, logical_size);
|
|
||||||
|
|
||||||
let ttf_context = sdl2::ttf::init().unwrap();
|
|
||||||
let font = ttf_context.load_font("assets/site/TerminalVector.ttf", 12).unwrap();
|
|
||||||
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(x + 10, y - 5, texture.query().width, texture.query().height);
|
|
||||||
debug_canvas.copy(&texture, None, dest).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render timing information in the top-left corner
|
// Render timing information in the top-left corner
|
||||||
render_timing_display(debug_canvas, &mut texture_creator, &timings);
|
render_timing_display(debug_canvas, &mut texture_creator, &timings);
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,18 +3,28 @@ use std::collections::{HashMap, HashSet};
|
|||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
event::EventWriter,
|
event::EventWriter,
|
||||||
resource::Resource,
|
resource::Resource,
|
||||||
system::{NonSendMut, ResMut},
|
system::{NonSendMut, Res, ResMut},
|
||||||
};
|
};
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use sdl2::{event::Event, keyboard::Keycode, EventPump};
|
use sdl2::{event::Event, keyboard::Keycode, EventPump};
|
||||||
|
|
||||||
use crate::systems::debug::CursorPosition;
|
use crate::systems::components::DeltaTime;
|
||||||
use crate::{
|
use crate::{
|
||||||
entity::direction::Direction,
|
entity::direction::Direction,
|
||||||
events::{GameCommand, GameEvent},
|
events::{GameCommand, GameEvent},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Resource)]
|
#[derive(Resource, Default, Debug, Copy, Clone)]
|
||||||
|
pub enum CursorPosition {
|
||||||
|
#[default]
|
||||||
|
None,
|
||||||
|
Some {
|
||||||
|
position: Vec2,
|
||||||
|
remaining_time: f32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Resource, Debug, Clone)]
|
||||||
pub struct Bindings {
|
pub struct Bindings {
|
||||||
key_bindings: HashMap<Keycode, GameCommand>,
|
key_bindings: HashMap<Keycode, GameCommand>,
|
||||||
movement_keys: HashSet<Keycode>,
|
movement_keys: HashSet<Keycode>,
|
||||||
@@ -63,12 +73,14 @@ impl Default for Bindings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn input_system(
|
pub fn input_system(
|
||||||
|
delta_time: Res<DeltaTime>,
|
||||||
mut bindings: ResMut<Bindings>,
|
mut bindings: ResMut<Bindings>,
|
||||||
mut writer: EventWriter<GameEvent>,
|
mut writer: EventWriter<GameEvent>,
|
||||||
mut pump: NonSendMut<&'static mut EventPump>,
|
mut pump: NonSendMut<&'static mut EventPump>,
|
||||||
mut cursor: ResMut<CursorPosition>,
|
mut cursor: ResMut<CursorPosition>,
|
||||||
) {
|
) {
|
||||||
let mut movement_key_pressed = false;
|
let mut movement_key_pressed = false;
|
||||||
|
let mut cursor_seen = false;
|
||||||
|
|
||||||
for event in pump.poll_iter() {
|
for event in pump.poll_iter() {
|
||||||
match event {
|
match event {
|
||||||
@@ -76,7 +88,11 @@ pub fn input_system(
|
|||||||
writer.write(GameEvent::Command(GameCommand::Exit));
|
writer.write(GameEvent::Command(GameCommand::Exit));
|
||||||
}
|
}
|
||||||
Event::MouseMotion { x, y, .. } => {
|
Event::MouseMotion { x, y, .. } => {
|
||||||
cursor.0 = Vec2::new(x as f32, y as f32);
|
*cursor = CursorPosition::Some {
|
||||||
|
position: Vec2::new(x as f32, y as f32),
|
||||||
|
remaining_time: 0.20,
|
||||||
|
};
|
||||||
|
cursor_seen = true;
|
||||||
}
|
}
|
||||||
Event::KeyUp {
|
Event::KeyUp {
|
||||||
repeat: false,
|
repeat: false,
|
||||||
@@ -117,4 +133,11 @@ pub fn input_system(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let (false, CursorPosition::Some { remaining_time, .. }) = (cursor_seen, &mut *cursor) {
|
||||||
|
*remaining_time -= delta_time.0;
|
||||||
|
if *remaining_time <= 0.0 {
|
||||||
|
*cursor = CursorPosition::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +0,0 @@
|
|||||||
use glam::Vec2;
|
|
||||||
use pacman::entity::graph::{Graph, Node};
|
|
||||||
use pacman::map::render::MapRenderer;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_find_nearest_node() {
|
|
||||||
let mut graph = Graph::new();
|
|
||||||
|
|
||||||
// Add some test nodes
|
|
||||||
let node1 = graph.add_node(Node {
|
|
||||||
position: Vec2::new(10.0, 10.0),
|
|
||||||
});
|
|
||||||
let node2 = graph.add_node(Node {
|
|
||||||
position: Vec2::new(50.0, 50.0),
|
|
||||||
});
|
|
||||||
let node3 = graph.add_node(Node {
|
|
||||||
position: Vec2::new(100.0, 100.0),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Test cursor near node1
|
|
||||||
let cursor_pos = Vec2::new(12.0, 8.0);
|
|
||||||
let nearest = MapRenderer::find_nearest_node(&graph, cursor_pos);
|
|
||||||
assert_eq!(nearest, Some(node1));
|
|
||||||
|
|
||||||
// Test cursor near node2
|
|
||||||
let cursor_pos = Vec2::new(45.0, 55.0);
|
|
||||||
let nearest = MapRenderer::find_nearest_node(&graph, cursor_pos);
|
|
||||||
assert_eq!(nearest, Some(node2));
|
|
||||||
|
|
||||||
// Test cursor near node3
|
|
||||||
let cursor_pos = Vec2::new(98.0, 102.0);
|
|
||||||
let nearest = MapRenderer::find_nearest_node(&graph, cursor_pos);
|
|
||||||
assert_eq!(nearest, Some(node3));
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
use pacman::constants::RAW_BOARD;
|
|
||||||
use pacman::map::builder::Map;
|
|
||||||
|
|
||||||
mod item;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_game_map_creation() {
|
|
||||||
let map = Map::new(RAW_BOARD).unwrap();
|
|
||||||
|
|
||||||
assert!(map.graph.node_count() > 0);
|
|
||||||
assert!(!map.grid_to_node.is_empty());
|
|
||||||
}
|
|
||||||
@@ -29,7 +29,7 @@ fn test_graph_basic_operations() {
|
|||||||
position: glam::Vec2::new(16.0, 0.0),
|
position: glam::Vec2::new(16.0, 0.0),
|
||||||
});
|
});
|
||||||
|
|
||||||
assert_eq!(graph.node_count(), 2);
|
assert_eq!(graph.nodes().count(), 2);
|
||||||
assert!(graph.get_node(node1).is_some());
|
assert!(graph.get_node(node1).is_some());
|
||||||
assert!(graph.get_node(node2).is_some());
|
assert!(graph.get_node(node2).is_some());
|
||||||
assert!(graph.get_node(999).is_none());
|
assert!(graph.get_node(999).is_none());
|
||||||
@@ -102,7 +102,7 @@ fn should_add_connected_node() {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(graph.node_count(), 2);
|
assert_eq!(graph.nodes().count(), 2);
|
||||||
let edge = graph.find_edge(node1, node2);
|
let edge = graph.find_edge(node1, node2);
|
||||||
assert!(edge.is_some());
|
assert!(edge.is_some());
|
||||||
assert_eq!(edge.unwrap().direction, Direction::Right);
|
assert_eq!(edge.unwrap().direction, Direction::Right);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use pacman::map::builder::Map;
|
|||||||
fn test_map_creation() {
|
fn test_map_creation() {
|
||||||
let map = Map::new(RAW_BOARD).unwrap();
|
let map = Map::new(RAW_BOARD).unwrap();
|
||||||
|
|
||||||
assert!(map.graph.node_count() > 0);
|
assert!(map.graph.nodes().count() > 0);
|
||||||
assert!(!map.grid_to_node.is_empty());
|
assert!(!map.grid_to_node.is_empty());
|
||||||
|
|
||||||
// Check that some connections were made
|
// Check that some connections were made
|
||||||
|
|||||||
Reference in New Issue
Block a user