mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-09 02:07:56 -06:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e964adc818 |
14
src/app.rs
14
src/app.rs
@@ -1,6 +1,7 @@
|
|||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use glam::Vec2;
|
||||||
use sdl2::event::{Event, WindowEvent};
|
use sdl2::event::{Event, WindowEvent};
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
use sdl2::render::{Canvas, ScaleMode, Texture, TextureCreator};
|
use sdl2::render::{Canvas, ScaleMode, Texture, TextureCreator};
|
||||||
@@ -19,6 +20,7 @@ pub struct App<'a> {
|
|||||||
backbuffer: Texture<'a>,
|
backbuffer: Texture<'a>,
|
||||||
paused: bool,
|
paused: bool,
|
||||||
last_tick: Instant,
|
last_tick: Instant,
|
||||||
|
cursor_pos: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App<'_> {
|
impl App<'_> {
|
||||||
@@ -56,7 +58,7 @@ impl App<'_> {
|
|||||||
|
|
||||||
// Initial draw
|
// Initial draw
|
||||||
game.draw(&mut canvas, &mut backbuffer)?;
|
game.draw(&mut canvas, &mut backbuffer)?;
|
||||||
game.present_backbuffer(&mut canvas, &backbuffer)?;
|
game.present_backbuffer(&mut canvas, &backbuffer, glam::Vec2::ZERO)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
game,
|
game,
|
||||||
@@ -65,6 +67,7 @@ impl App<'_> {
|
|||||||
backbuffer,
|
backbuffer,
|
||||||
paused: false,
|
paused: false,
|
||||||
last_tick: Instant::now(),
|
last_tick: Instant::now(),
|
||||||
|
cursor_pos: Vec2::ZERO,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,6 +112,10 @@ impl App<'_> {
|
|||||||
Event::KeyDown { keycode, .. } => {
|
Event::KeyDown { keycode, .. } => {
|
||||||
self.game.keyboard_event(keycode.unwrap());
|
self.game.keyboard_event(keycode.unwrap());
|
||||||
}
|
}
|
||||||
|
Event::MouseMotion { x, y, .. } => {
|
||||||
|
// Convert window coordinates to logical coordinates
|
||||||
|
self.cursor_pos = Vec2::new(x as f32, y as f32);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -121,7 +128,10 @@ impl App<'_> {
|
|||||||
if let Err(e) = self.game.draw(&mut self.canvas, &mut self.backbuffer) {
|
if let Err(e) = self.game.draw(&mut self.canvas, &mut self.backbuffer) {
|
||||||
error!("Failed to draw game: {e}");
|
error!("Failed to draw game: {e}");
|
||||||
}
|
}
|
||||||
if let Err(e) = self.game.present_backbuffer(&mut self.canvas, &self.backbuffer) {
|
if let Err(e) = self
|
||||||
|
.game
|
||||||
|
.present_backbuffer(&mut self.canvas, &self.backbuffer, self.cursor_pos)
|
||||||
|
{
|
||||||
error!("Failed to present backbuffer: {e}");
|
error!("Failed to present backbuffer: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/game.rs
10
src/game.rs
@@ -108,10 +108,16 @@ impl Game {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn present_backbuffer<T: RenderTarget>(&mut self, canvas: &mut Canvas<T>, backbuffer: &Texture) -> Result<()> {
|
pub fn present_backbuffer<T: RenderTarget>(
|
||||||
|
&mut self,
|
||||||
|
canvas: &mut Canvas<T>,
|
||||||
|
backbuffer: &Texture,
|
||||||
|
cursor_pos: glam::Vec2,
|
||||||
|
) -> Result<()> {
|
||||||
canvas.copy(backbuffer, None, None).map_err(anyhow::Error::msg)?;
|
canvas.copy(backbuffer, None, None).map_err(anyhow::Error::msg)?;
|
||||||
if self.debug_mode {
|
if self.debug_mode {
|
||||||
self.map.debug_render_nodes(canvas);
|
self.map
|
||||||
|
.debug_render_with_cursor(canvas, &mut self.text_texture, &mut self.atlas, cursor_pos);
|
||||||
}
|
}
|
||||||
self.draw_hud(canvas)?;
|
self.draw_hud(canvas)?;
|
||||||
canvas.present();
|
canvas.present();
|
||||||
|
|||||||
@@ -184,13 +184,18 @@ impl Map {
|
|||||||
MapRenderer::render_map(canvas, atlas, map_texture);
|
MapRenderer::render_map(canvas, atlas, map_texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders a debug visualization of the navigation graph.
|
/// Renders a debug visualization with cursor-based highlighting.
|
||||||
///
|
///
|
||||||
/// This function is intended for development and debugging purposes. It draws the
|
/// This function provides interactive debugging by highlighting the nearest node
|
||||||
/// nodes and edges of the graph on top of the map, allowing for visual
|
/// to the cursor, showing its ID, and highlighting its connections.
|
||||||
/// inspection of the navigation paths.
|
pub fn debug_render_with_cursor<T: RenderTarget>(
|
||||||
pub fn debug_render_nodes<T: RenderTarget>(&self, canvas: &mut Canvas<T>) {
|
&self,
|
||||||
MapRenderer::debug_render_nodes(&self.graph, canvas);
|
canvas: &mut Canvas<T>,
|
||||||
|
text_renderer: &mut crate::texture::text::TextTexture,
|
||||||
|
atlas: &mut SpriteAtlas,
|
||||||
|
cursor_pos: glam::Vec2,
|
||||||
|
) {
|
||||||
|
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.
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
//! Map rendering functionality.
|
//! Map rendering functionality.
|
||||||
|
|
||||||
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::{Point, Rect};
|
||||||
use sdl2::render::{Canvas, RenderTarget};
|
use sdl2::render::{Canvas, RenderTarget};
|
||||||
@@ -23,45 +25,93 @@ impl MapRenderer {
|
|||||||
let _ = map_texture.render(canvas, atlas, dest);
|
let _ = map_texture.render(canvas, atlas, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Renders a debug visualization of the navigation graph.
|
/// Renders a debug visualization with cursor-based highlighting.
|
||||||
///
|
///
|
||||||
/// This function is intended for development and debugging purposes. It draws the
|
/// This function provides interactive debugging by highlighting the nearest node
|
||||||
/// nodes and edges of the graph on top of the map, allowing for visual
|
/// to the cursor, showing its ID, and highlighting its connections.
|
||||||
/// inspection of the navigation paths.
|
pub fn debug_render_with_cursor<T: RenderTarget>(
|
||||||
pub fn debug_render_nodes<T: RenderTarget>(graph: &crate::entity::graph::Graph, canvas: &mut Canvas<T>) {
|
graph: &crate::entity::graph::Graph,
|
||||||
|
canvas: &mut Canvas<T>,
|
||||||
|
text_renderer: &mut TextTexture,
|
||||||
|
atlas: &mut SpriteAtlas,
|
||||||
|
cursor_pos: Vec2,
|
||||||
|
) {
|
||||||
|
// 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() {
|
for i in 0..graph.node_count() {
|
||||||
let node = graph.get_node(i).unwrap();
|
let node = graph.get_node(i).unwrap();
|
||||||
let pos = node.position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2();
|
let pos = node.position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2();
|
||||||
|
|
||||||
// Draw connections
|
|
||||||
canvas.set_draw_color(Color::BLUE);
|
|
||||||
|
|
||||||
for edge in graph.adjacency_list[i].edges() {
|
for edge in graph.adjacency_list[i].edges() {
|
||||||
let end_pos = graph.get_node(edge.target).unwrap().position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2();
|
let end_pos = graph.get_node(edge.target).unwrap().position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2();
|
||||||
canvas
|
canvas
|
||||||
.draw_line((pos.x as i32, pos.y as i32), (end_pos.x as i32, end_pos.y as i32))
|
.draw_line((pos.x as i32, pos.y as i32), (end_pos.x as i32, end_pos.y as i32))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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).unwrap();
|
||||||
|
let pos = node.position + crate::constants::BOARD_PIXEL_OFFSET.as_vec2();
|
||||||
|
|
||||||
// Draw node
|
|
||||||
// let color = if pacman.position.from_node_idx() == i.into() {
|
|
||||||
// Color::GREEN
|
|
||||||
// } else if let Some(to_idx) = pacman.position.to_node_idx() {
|
|
||||||
// if to_idx == i.into() {
|
|
||||||
// Color::CYAN
|
|
||||||
// } else {
|
|
||||||
// Color::RED
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// Color::RED
|
|
||||||
// };
|
|
||||||
canvas.set_draw_color(Color::GREEN);
|
|
||||||
canvas
|
canvas
|
||||||
.fill_rect(Rect::new(0, 0, 3, 3).centered_on(Point::new(pos.x as i32, pos.y as i32)))
|
.fill_rect(Rect::new(0, 0, 3, 3).centered_on(Point::new(pos.x as i32, pos.y as i32)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
// Draw node index
|
// Highlight connections from the nearest node in bright blue
|
||||||
// text.render(canvas, atlas, &i.to_string(), pos.as_uvec2()).unwrap();
|
if let Some(nearest_id) = nearest_node {
|
||||||
|
let nearest_pos = graph.get_node(nearest_id).unwrap().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).unwrap().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),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// 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
|
||||||
|
);
|
||||||
|
let _ = text_renderer.render(canvas, atlas, &id_text, text_pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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() {
|
||||||
|
let node = graph.get_node(i).unwrap();
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
34
tests/debug_rendering.rs
Normal file
34
tests/debug_rendering.rs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user