feat: re-add board offset logic, fixup text rendering

This commit is contained in:
2025-07-26 15:26:37 -05:00
parent a1d37a1a0b
commit 7a6182cb85
8 changed files with 42 additions and 28 deletions

View File

@@ -11,9 +11,16 @@ pub const BOARD_CELL_SIZE: UVec2 = UVec2::new(28, 31);
pub const SCALE: f32 = 2.6; pub const SCALE: f32 = 2.6;
/// The offset of the game board from the top-left corner of the window, in cells. /// The offset of the game board from the top-left corner of the window, in cells.
pub const BOARD_OFFSET: UVec2 = UVec2::new(0, 0); pub const BOARD_CELL_OFFSET: UVec2 = UVec2::new(0, 3);
/// The offset of the game board from the top-left corner of the window, in pixels.
pub const BOARD_PIXEL_OFFSET: UVec2 = UVec2::new(BOARD_CELL_OFFSET.x * CELL_SIZE, BOARD_CELL_OFFSET.y * CELL_SIZE);
/// The size of the game board, in pixels. /// The size of the game board, in pixels.
pub const BOARD_PIXEL_SIZE: UVec2 = UVec2::new(BOARD_CELL_SIZE.x * CELL_SIZE, BOARD_CELL_SIZE.y * CELL_SIZE); pub const BOARD_PIXEL_SIZE: UVec2 = UVec2::new(BOARD_CELL_SIZE.x * CELL_SIZE, BOARD_CELL_SIZE.y * CELL_SIZE);
/// The size of the canvas, in pixels.
pub const CANVAS_SIZE: UVec2 = UVec2::new(
(BOARD_CELL_SIZE.x + BOARD_CELL_OFFSET.x) * CELL_SIZE,
(BOARD_CELL_SIZE.y + BOARD_CELL_OFFSET.y) * CELL_SIZE,
);
/// An enum representing the different types of tiles on the map. /// An enum representing the different types of tiles on the map.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]

View File

@@ -63,13 +63,13 @@ impl Renderable for Edible {
let pos = self.base.pixel_position; let pos = self.base.pixel_position;
let dest = match &mut self.sprite { let dest = match &mut self.sprite {
EdibleSprite::Pellet(sprite) => { EdibleSprite::Pellet(sprite) => {
let mut tile = sprite.current_tile(); let tile = sprite.current_tile();
let x = pos.x + ((crate::constants::CELL_SIZE as i32 - tile.size.x as i32) / 2); let x = pos.x + ((crate::constants::CELL_SIZE as i32 - tile.size.x as i32) / 2);
let y = pos.y + ((crate::constants::CELL_SIZE as i32 - tile.size.y as i32) / 2); let y = pos.y + ((crate::constants::CELL_SIZE as i32 - tile.size.y as i32) / 2);
sdl2::rect::Rect::new(x, y, tile.size.x as u32, tile.size.y as u32) sdl2::rect::Rect::new(x, y, tile.size.x as u32, tile.size.y as u32)
} }
EdibleSprite::PowerPellet(sprite) => { EdibleSprite::PowerPellet(sprite) => {
let mut tile = sprite.animation.current_tile(); let tile = sprite.animation.current_tile();
let x = pos.x + ((crate::constants::CELL_SIZE as i32 - tile.size.x as i32) / 2); let x = pos.x + ((crate::constants::CELL_SIZE as i32 - tile.size.x as i32) / 2);
let y = pos.y + ((crate::constants::CELL_SIZE as i32 - tile.size.y as i32) / 2); let y = pos.y + ((crate::constants::CELL_SIZE as i32 - tile.size.y as i32) / 2);
sdl2::rect::Rect::new(x, y, tile.size.x as u32, tile.size.y as u32) sdl2::rect::Rect::new(x, y, tile.size.x as u32, tile.size.y as u32)

View File

@@ -6,7 +6,7 @@ pub mod pacman;
pub mod speed; pub mod speed;
use crate::{ use crate::{
constants::{MapTile, BOARD_CELL_SIZE, BOARD_OFFSET, CELL_SIZE}, constants::{MapTile, BOARD_CELL_OFFSET, BOARD_CELL_SIZE, CELL_SIZE},
entity::{direction::Direction, speed::SimpleTickModulator}, entity::{direction::Direction, speed::SimpleTickModulator},
map::Map, map::Map,
}; };
@@ -145,8 +145,8 @@ impl Moving for MovableEntity {
} }
fn update_cell_position(&mut self) { fn update_cell_position(&mut self) {
self.base.cell_position = UVec2::new( self.base.cell_position = UVec2::new(
(self.base.pixel_position.x as u32 / CELL_SIZE) - BOARD_OFFSET.x, (self.base.pixel_position.x as u32 / CELL_SIZE) - BOARD_CELL_OFFSET.x,
(self.base.pixel_position.y as u32 / CELL_SIZE) - BOARD_OFFSET.y, (self.base.pixel_position.y as u32 / CELL_SIZE) - BOARD_CELL_OFFSET.y,
); );
} }
fn next_cell(&self, direction: Option<Direction>) -> IVec2 { fn next_cell(&self, direction: Option<Direction>) -> IVec2 {

View File

@@ -4,12 +4,13 @@ use std::ops::Not;
use std::rc::Rc; use std::rc::Rc;
use anyhow::Result; use anyhow::Result;
use glam::{IVec2, UVec2}; use glam::UVec2;
use rand::rngs::SmallRng; use rand::rngs::SmallRng;
use rand::seq::IteratorRandom; use rand::seq::IteratorRandom;
use rand::SeedableRng; use rand::SeedableRng;
use sdl2::image::LoadTexture; use sdl2::image::LoadTexture;
use sdl2::keyboard::Keycode; use sdl2::keyboard::Keycode;
use sdl2::render::{Texture, TextureCreator}; use sdl2::render::{Texture, TextureCreator};
use sdl2::video::WindowContext; use sdl2::video::WindowContext;
use sdl2::{pixels::Color, render::Canvas, video::Window}; use sdl2::{pixels::Color, render::Canvas, video::Window};
@@ -268,7 +269,6 @@ impl Game {
} }
let _ = this.pacman.borrow_mut().render(texture_canvas); let _ = this.pacman.borrow_mut().render(texture_canvas);
let _ = this.blinky.render(texture_canvas); let _ = this.blinky.render(texture_canvas);
this.render_ui_on(texture_canvas);
match this.debug_mode { match this.debug_mode {
DebugMode::Grid => { DebugMode::Grid => {
DebugRenderer::draw_debug_grid( DebugRenderer::draw_debug_grid(
@@ -290,10 +290,11 @@ impl Game {
}) })
.map_err(|e| anyhow::anyhow!(format!("Failed to render to backbuffer: {e}"))) .map_err(|e| anyhow::anyhow!(format!("Failed to render to backbuffer: {e}")))
} }
pub fn present_backbuffer(&self, canvas: &mut Canvas<Window>, backbuffer: &Texture) -> Result<()> { pub fn present_backbuffer(&mut self, canvas: &mut Canvas<Window>, backbuffer: &Texture) -> Result<()> {
canvas.set_draw_color(Color::BLACK); canvas.set_draw_color(Color::BLACK);
canvas.clear(); canvas.clear();
canvas.copy(backbuffer, None, None).map_err(anyhow::Error::msg)?; canvas.copy(backbuffer, None, None).map_err(anyhow::Error::msg)?;
self.render_ui_on(canvas);
canvas.present(); canvas.present();
Ok(()) Ok(())
} }
@@ -301,22 +302,21 @@ impl Game {
fn render_ui_on<C: sdl2::render::RenderTarget>(&mut self, canvas: &mut sdl2::render::Canvas<C>) { fn render_ui_on<C: sdl2::render::RenderTarget>(&mut self, canvas: &mut sdl2::render::Canvas<C>) {
let lives = 3; let lives = 3;
let score_text = format!("{:02}", self.score); let score_text = format!("{:02}", self.score);
let x_offset = 12; let x_offset = 4;
let y_offset = 2; let y_offset = 2;
let lives_offset = 3; let lives_offset = 3;
let score_offset = 7 - (score_text.len() as i32); let score_offset = 7 - (score_text.len() as i32);
let gap_offset = 6; self.text_texture.set_scale(1.0);
self.text_texture.set_scale(2.0); let _ = self.text_texture.render(
self.text_texture.render(
canvas, canvas,
&format!("{lives}UP HIGH SCORE "), &format!("{lives}UP HIGH SCORE "),
UVec2::new(24 * lives_offset as u32 + x_offset, y_offset), UVec2::new(8 * lives_offset as u32 + x_offset, y_offset),
Color::WHITE, Color::WHITE,
); );
self.text_texture.render( let _ = self.text_texture.render(
canvas, canvas,
&score_text, &score_text,
UVec2::new(24 * score_offset as u32 + x_offset, 24 + y_offset + gap_offset), UVec2::new(8 * score_offset as u32 + x_offset, 8 + y_offset),
Color::WHITE, Color::WHITE,
); );

View File

@@ -1,6 +1,6 @@
#![windows_subsystem = "windows"] #![windows_subsystem = "windows"]
use crate::constants::{BOARD_PIXEL_SIZE, SCALE}; use crate::constants::{CANVAS_SIZE, SCALE};
use crate::game::Game; use crate::game::Game;
use sdl2::event::{Event, WindowEvent}; use sdl2::event::{Event, WindowEvent};
use sdl2::keyboard::Keycode; use sdl2::keyboard::Keycode;
@@ -105,8 +105,8 @@ pub fn main() {
let window = video_subsystem let window = video_subsystem
.window( .window(
"Pac-Man", "Pac-Man",
(BOARD_PIXEL_SIZE.x as f32 * SCALE).round() as u32, (CANVAS_SIZE.x as f32 * SCALE).round() as u32,
(BOARD_PIXEL_SIZE.y as f32 * SCALE).round() as u32, (CANVAS_SIZE.y as f32 * SCALE).round() as u32,
) )
.resizable() .resizable()
.position_centered() .position_centered()
@@ -116,7 +116,7 @@ pub fn main() {
let mut canvas = window.into_canvas().build().expect("Could not build canvas"); let mut canvas = window.into_canvas().build().expect("Could not build canvas");
canvas canvas
.set_logical_size(BOARD_PIXEL_SIZE.x, BOARD_PIXEL_SIZE.y) .set_logical_size(CANVAS_SIZE.x, CANVAS_SIZE.y)
.expect("Could not set logical size"); .expect("Could not set logical size");
let texture_creator = canvas.texture_creator(); let texture_creator = canvas.texture_creator();
@@ -127,7 +127,7 @@ pub fn main() {
// Create a backbuffer texture for drawing // Create a backbuffer texture for drawing
let mut backbuffer = texture_creator_static let mut backbuffer = texture_creator_static
.create_texture_target(None, BOARD_PIXEL_SIZE.x, BOARD_PIXEL_SIZE.y) .create_texture_target(None, CANVAS_SIZE.x, CANVAS_SIZE.y)
.expect("Could not create backbuffer texture"); .expect("Could not create backbuffer texture");
let mut event_pump = sdl_context.event_pump().expect("Could not get SDL EventPump"); let mut event_pump = sdl_context.event_pump().expect("Could not get SDL EventPump");
@@ -154,7 +154,7 @@ pub fn main() {
let mut last_frame_time = Instant::now(); let mut last_frame_time = Instant::now();
event!(tracing::Level::INFO, "Starting game loop ({:?})", loop_time); event!(tracing::Level::INFO, "Starting game loop ({:?})", loop_time);
let mut main_loop = || { let mut main_loop = move || {
let start = Instant::now(); let start = Instant::now();
let current_frame_time = Instant::now(); let current_frame_time = Instant::now();
let frame_duration = current_frame_time.duration_since(last_frame_time); let frame_duration = current_frame_time.duration_since(last_frame_time);

View File

@@ -3,7 +3,7 @@ use rand::rngs::SmallRng;
use rand::seq::IteratorRandom; use rand::seq::IteratorRandom;
use rand::SeedableRng; use rand::SeedableRng;
use crate::constants::{MapTile, BOARD_CELL_SIZE, BOARD_OFFSET, CELL_SIZE}; use crate::constants::{MapTile, BOARD_CELL_OFFSET, BOARD_CELL_SIZE, BOARD_PIXEL_OFFSET, BOARD_PIXEL_SIZE, CELL_SIZE};
use crate::texture::sprite::AtlasTile; use crate::texture::sprite::AtlasTile;
use glam::{IVec2, UVec2}; use glam::{IVec2, UVec2};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
@@ -104,7 +104,10 @@ impl Map {
/// ///
/// * `cell` - The cell coordinates, in grid coordinates. /// * `cell` - The cell coordinates, in grid coordinates.
pub fn cell_to_pixel(cell: UVec2) -> IVec2 { pub fn cell_to_pixel(cell: UVec2) -> IVec2 {
IVec2::new((cell.x * CELL_SIZE) as i32, ((cell.y + BOARD_OFFSET.y) * CELL_SIZE) as i32) IVec2::new(
(cell.x * CELL_SIZE) as i32,
((cell.y + BOARD_CELL_OFFSET.y) * CELL_SIZE) as i32,
)
} }
/// Returns a reference to a cached vector of all valid playable positions in the maze. /// Returns a reference to a cached vector of all valid playable positions in the maze.
@@ -162,7 +165,12 @@ impl Map {
/// Renders the map to the given canvas using the provided map texture. /// Renders the map to the given canvas using the provided map texture.
pub fn render(&self, canvas: &mut Canvas<Window>, map_texture: &mut AtlasTile) { pub fn render(&self, canvas: &mut Canvas<Window>, map_texture: &mut AtlasTile) {
let dest = Rect::new(0, 0, CELL_SIZE * BOARD_CELL_SIZE.x, CELL_SIZE * BOARD_CELL_SIZE.y); let dest = Rect::new(
BOARD_PIXEL_OFFSET.x as i32,
BOARD_PIXEL_OFFSET.y as i32,
BOARD_PIXEL_SIZE.x,
BOARD_PIXEL_SIZE.y,
);
let _ = map_texture.render(canvas, dest); let _ = map_texture.render(canvas, dest);
} }
} }

View File

@@ -1,6 +1,6 @@
//! This module provides a simple animation and atlas system for textures. //! This module provides a simple animation and atlas system for textures.
use anyhow::Result; use anyhow::Result;
use sdl2::{pixels::Color, render::WindowCanvas}; use sdl2::render::WindowCanvas;
use crate::texture::sprite::AtlasTile; use crate::texture::sprite::AtlasTile;
@@ -43,7 +43,7 @@ impl AnimatedTexture {
} }
pub fn render(&mut self, canvas: &mut WindowCanvas, dest: sdl2::rect::Rect) -> Result<()> { pub fn render(&mut self, canvas: &mut WindowCanvas, dest: sdl2::rect::Rect) -> Result<()> {
let mut tile = self.current_tile(); let tile = self.current_tile();
tile.render(canvas, dest) tile.render(canvas, dest)
} }
} }

View File

@@ -49,7 +49,6 @@
use anyhow::Result; use anyhow::Result;
use glam::UVec2; use glam::UVec2;
use sdl2::pixels::Color; use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{Canvas, RenderTarget}; use sdl2::render::{Canvas, RenderTarget};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;