feat!: dynamic map rendering from tiles

This commit is contained in:
2025-08-13 00:25:34 -05:00
parent c1e421bbbb
commit 09e3d85821
89 changed files with 1072 additions and 1024 deletions

View File

File diff suppressed because it is too large Load Diff

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -18,8 +18,6 @@ pub const SCALE: f32 = 2.6;
pub const BOARD_CELL_OFFSET: UVec2 = UVec2::new(0, 3); 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. /// 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); 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.
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. /// The size of the canvas, in pixels.
pub const CANVAS_SIZE: UVec2 = UVec2::new( pub const CANVAS_SIZE: UVec2 = UVec2::new(
(BOARD_CELL_SIZE.x + BOARD_CELL_OFFSET.x) * CELL_SIZE, (BOARD_CELL_SIZE.x + BOARD_CELL_OFFSET.x) * CELL_SIZE,

View File

@@ -174,7 +174,7 @@ impl Game {
canvas.clear(); canvas.clear();
self.state self.state
.map .map
.render(canvas, &mut self.state.atlas, &mut self.state.map_texture); .render(canvas, &mut self.state.atlas, &mut self.state.map_tiles);
// Render all items // Render all items
for item in &self.state.items { for item in &self.state.items {

View File

@@ -1,4 +1,4 @@
use sdl2::{image::LoadTexture, pixels::Color, render::TextureCreator, video::WindowContext}; use sdl2::{image::LoadTexture, render::TextureCreator, video::WindowContext};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::{ use crate::{
@@ -6,12 +6,13 @@ use crate::{
audio::Audio, audio::Audio,
constants::RAW_BOARD, constants::RAW_BOARD,
entity::{ entity::{
collision::{Collidable, CollisionSystem, EntityId}, collision::{Collidable, CollisionSystem},
ghost::{Ghost, GhostType}, ghost::{Ghost, GhostType},
item::Item, item::Item,
pacman::Pacman, pacman::Pacman,
}, },
error::{GameError, GameResult, TextureError}, error::{GameError, GameResult, TextureError},
game::EntityId,
map::Map, map::Map,
texture::{ texture::{
sprite::{AtlasMapper, AtlasTile, SpriteAtlas}, sprite::{AtlasMapper, AtlasTile, SpriteAtlas},
@@ -28,20 +29,20 @@ use crate::{
pub struct GameState { pub struct GameState {
pub score: u32, pub score: u32,
pub map: Map, pub map: Map,
pub map_tiles: Vec<AtlasTile>,
pub pacman: Pacman, pub pacman: Pacman,
pub pacman_id: EntityId,
pub ghosts: SmallVec<[Ghost; 4]>, pub ghosts: SmallVec<[Ghost; 4]>,
pub ghost_ids: SmallVec<[EntityId; 4]>,
pub items: Vec<Item>, pub items: Vec<Item>,
pub item_ids: Vec<EntityId>,
pub debug_mode: bool, pub debug_mode: bool,
// Collision system // Collision system
pub(crate) collision_system: CollisionSystem, pub(crate) collision_system: CollisionSystem,
pub(crate) pacman_id: EntityId,
pub(crate) ghost_ids: SmallVec<[EntityId; 4]>,
pub(crate) item_ids: Vec<EntityId>,
// Rendering resources // Rendering resources
pub(crate) atlas: SpriteAtlas, pub(crate) atlas: SpriteAtlas,
pub(crate) map_texture: AtlasTile,
pub(crate) text_texture: TextTexture, pub(crate) text_texture: TextTexture,
// Audio // Audio
@@ -71,9 +72,13 @@ impl GameState {
let atlas_mapper: AtlasMapper = serde_json::from_slice(&atlas_json)?; let atlas_mapper: AtlasMapper = serde_json::from_slice(&atlas_json)?;
let atlas = SpriteAtlas::new(atlas_texture, atlas_mapper); let atlas = SpriteAtlas::new(atlas_texture, atlas_mapper);
let mut map_texture = SpriteAtlas::get_tile(&atlas, "maze/full.png") let mut map_tiles = Vec::with_capacity(35);
.ok_or_else(|| GameError::Texture(TextureError::AtlasTileNotFound("maze/full.png".to_string())))?; for i in 0..35 {
map_texture.color = Some(Color::RGB(0x20, 0x20, 0xf9)); let tile_name = format!("maze/tiles/{}.png", i);
let tile = SpriteAtlas::get_tile(&atlas, &tile_name)
.ok_or(GameError::Texture(TextureError::AtlasTileNotFound(tile_name)))?;
map_tiles.push(tile);
}
let text_texture = TextTexture::new(1.0); let text_texture = TextTexture::new(1.0);
let audio = Audio::new(); let audio = Audio::new();
@@ -89,11 +94,10 @@ impl GameState {
let pacman_id = collision_system.register_entity(pacman.position()); let pacman_id = collision_system.register_entity(pacman.position());
// Register items // Register items
let mut item_ids = Vec::new(); let item_ids = items
for item in &items { .iter()
let item_id = collision_system.register_entity(item.position()); .map(|item| collision_system.register_entity(item.position()))
item_ids.push(item_id); .collect();
}
// Create and register ghosts // Create and register ghosts
let ghosts = [GhostType::Blinky, GhostType::Pinky, GhostType::Inky, GhostType::Clyde] let ghosts = [GhostType::Blinky, GhostType::Pinky, GhostType::Inky, GhostType::Clyde]
@@ -110,26 +114,27 @@ impl GameState {
.map(|(ghost_type, start_node)| Ghost::new(&map.graph, *start_node, *ghost_type, &atlas)) .map(|(ghost_type, start_node)| Ghost::new(&map.graph, *start_node, *ghost_type, &atlas))
.collect::<GameResult<SmallVec<[_; 4]>>>()?; .collect::<GameResult<SmallVec<[_; 4]>>>()?;
// Register ghosts
let ghost_ids = ghosts let ghost_ids = ghosts
.iter() .iter()
.map(|ghost| collision_system.register_entity(ghost.position())) .map(|ghost| collision_system.register_entity(ghost.position()))
.collect::<SmallVec<[_; 4]>>(); .collect();
Ok(Self { Ok(Self {
score: 0,
map, map,
atlas,
map_tiles,
pacman, pacman,
ghosts,
items,
debug_mode: false,
collision_system,
pacman_id, pacman_id,
ghosts,
ghost_ids, ghost_ids,
items,
item_ids, item_ids,
map_texture,
text_texture, text_texture,
audio, audio,
atlas, score: 0,
debug_mode: false,
collision_system,
}) })
} }
} }

View File

@@ -158,8 +158,8 @@ impl Map {
/// ///
/// This function draws the static map texture to the screen at the correct /// This function draws the static map texture to the screen at the correct
/// position and scale. /// position and scale.
pub fn render<T: RenderTarget>(&self, canvas: &mut Canvas<T>, atlas: &mut SpriteAtlas, map_texture: &mut AtlasTile) { pub fn render<T: RenderTarget>(&self, canvas: &mut Canvas<T>, atlas: &mut SpriteAtlas, map_tiles: &mut [AtlasTile]) {
MapRenderer::render_map(canvas, atlas, map_texture); MapRenderer::render_map(canvas, atlas, map_tiles);
} }
/// Generates Item entities for pellets and energizers from the parsed map. /// Generates Item entities for pellets and energizers from the parsed map.

95
src/map/layout.rs Normal file
View File

@@ -0,0 +1,95 @@
pub const TILE_MAP: [[usize; 28]; 31] = [
[
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4,
],
[
5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9,
],
[
5, 6, 10, 11, 11, 12, 6, 10, 11, 11, 11, 12, 6, 7, 8, 6, 10, 11, 11, 11, 12, 6, 10, 11, 11, 12, 6, 9,
],
[
5, 6, 7, 6, 6, 8, 6, 7, 6, 6, 6, 8, 6, 7, 8, 6, 7, 6, 6, 6, 8, 6, 7, 6, 6, 8, 6, 9,
],
[
5, 6, 13, 14, 14, 15, 6, 13, 14, 14, 14, 15, 6, 13, 15, 6, 13, 14, 14, 14, 15, 6, 13, 14, 14, 15, 6, 9,
],
[
5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9,
],
[
5, 6, 10, 11, 11, 12, 6, 10, 12, 6, 10, 11, 11, 11, 11, 11, 11, 12, 6, 10, 12, 6, 10, 11, 11, 12, 6, 9,
],
[
5, 6, 13, 14, 14, 15, 6, 7, 8, 6, 13, 14, 14, 16, 17, 14, 14, 15, 6, 7, 8, 6, 13, 14, 14, 15, 6, 9,
],
[
5, 6, 6, 6, 6, 6, 6, 7, 8, 6, 6, 6, 6, 7, 8, 6, 6, 6, 6, 7, 8, 6, 6, 6, 6, 6, 6, 9,
],
[
18, 19, 19, 19, 19, 12, 6, 7, 20, 11, 11, 12, 6, 7, 8, 6, 10, 11, 11, 21, 8, 6, 10, 19, 19, 19, 19, 22,
],
[
6, 6, 6, 6, 6, 5, 6, 7, 17, 14, 14, 15, 6, 13, 15, 6, 13, 14, 14, 16, 8, 6, 9, 6, 6, 6, 6, 6,
],
[
6, 6, 6, 6, 6, 5, 6, 7, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 8, 6, 9, 6, 6, 6, 6, 6,
],
[
6, 6, 6, 6, 6, 5, 6, 7, 8, 6, 23, 19, 24, 25, 25, 26, 19, 27, 6, 7, 8, 6, 9, 6, 6, 6, 6, 6,
],
[
1, 1, 1, 1, 1, 15, 6, 13, 15, 6, 9, 6, 6, 6, 6, 6, 6, 5, 6, 13, 15, 6, 13, 1, 1, 1, 1, 1,
],
[
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
],
[
19, 19, 19, 19, 19, 12, 6, 10, 12, 6, 9, 6, 6, 6, 6, 6, 6, 5, 6, 10, 12, 6, 10, 19, 19, 19, 19, 19,
],
[
6, 6, 6, 6, 6, 5, 6, 7, 8, 6, 28, 1, 1, 1, 1, 1, 1, 29, 6, 7, 8, 6, 9, 6, 6, 6, 6, 6,
],
[
6, 6, 6, 6, 6, 5, 6, 7, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 8, 6, 9, 6, 6, 6, 6, 6,
],
[
6, 6, 6, 6, 6, 5, 6, 7, 8, 6, 10, 11, 11, 11, 11, 11, 11, 12, 6, 7, 8, 6, 9, 6, 6, 6, 6, 6,
],
[
0, 1, 1, 1, 1, 15, 6, 13, 15, 6, 13, 14, 14, 16, 17, 14, 14, 15, 6, 13, 15, 6, 13, 1, 1, 1, 1, 4,
],
[
5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9,
],
[
5, 6, 10, 11, 11, 12, 6, 10, 11, 11, 11, 12, 6, 7, 8, 6, 10, 11, 11, 11, 12, 6, 30, 11, 11, 12, 6, 9,
],
[
5, 6, 13, 14, 16, 8, 6, 13, 14, 14, 14, 15, 6, 13, 15, 6, 13, 14, 14, 14, 15, 6, 7, 17, 14, 15, 6, 9,
],
[
5, 6, 6, 6, 7, 8, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 8, 6, 6, 6, 9,
],
[
31, 11, 12, 6, 7, 8, 6, 10, 12, 6, 10, 11, 11, 11, 11, 11, 11, 12, 6, 10, 12, 6, 7, 8, 6, 10, 11, 32,
],
[
33, 14, 15, 6, 13, 15, 6, 7, 8, 6, 13, 14, 14, 16, 17, 14, 14, 15, 6, 7, 8, 6, 13, 15, 6, 13, 14, 34,
],
[
5, 6, 6, 6, 6, 6, 6, 7, 8, 6, 6, 6, 6, 7, 8, 6, 6, 6, 6, 7, 8, 6, 6, 6, 6, 6, 6, 9,
],
[
5, 6, 10, 11, 11, 11, 11, 21, 20, 11, 11, 12, 6, 7, 8, 6, 10, 11, 11, 21, 20, 11, 11, 11, 11, 12, 6, 9,
],
[
5, 6, 13, 14, 14, 14, 14, 14, 14, 14, 14, 15, 6, 13, 15, 6, 13, 14, 14, 14, 14, 14, 14, 14, 14, 15, 6, 9,
],
[
5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 9,
],
[
18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 22,
],
];

View File

@@ -1,6 +1,7 @@
//! This module defines the game map and provides functions for interacting with it. //! This module defines the game map and provides functions for interacting with it.
pub mod builder; pub mod builder;
pub mod layout;
pub mod parser; pub mod parser;
pub mod render; pub mod render;

View File

@@ -1,5 +1,7 @@
//! Map rendering functionality. //! Map rendering functionality.
use crate::constants::{BOARD_CELL_OFFSET, CELL_SIZE};
use crate::map::layout::TILE_MAP;
use crate::texture::sprite::{AtlasTile, SpriteAtlas}; use crate::texture::sprite::{AtlasTile, SpriteAtlas};
use crate::texture::text::TextTexture; use crate::texture::text::TextTexture;
use glam::Vec2; use glam::Vec2;
@@ -17,15 +19,22 @@ impl MapRenderer {
/// ///
/// This function draws the static map texture to the screen at the correct /// This function draws the static map texture to the screen at the correct
/// position and scale. /// position and scale.
pub fn render_map<T: RenderTarget>(canvas: &mut Canvas<T>, atlas: &mut SpriteAtlas, map_texture: &mut AtlasTile) { pub fn render_map<T: RenderTarget>(canvas: &mut Canvas<T>, atlas: &mut SpriteAtlas, map_tiles: &mut [AtlasTile]) {
let dest = Rect::new( for (y, row) in TILE_MAP.iter().enumerate() {
crate::constants::BOARD_PIXEL_OFFSET.x as i32, for (x, &tile_index) in row.iter().enumerate() {
crate::constants::BOARD_PIXEL_OFFSET.y as i32, let mut tile = map_tiles[tile_index];
crate::constants::BOARD_PIXEL_SIZE.x, tile.color = Some(Color::RGB(0x20, 0x20, 0xf9));
crate::constants::BOARD_PIXEL_SIZE.y, let dest = Rect::new(
); (BOARD_CELL_OFFSET.x as usize * CELL_SIZE as usize + x * CELL_SIZE as usize) as i32,
if let Err(e) = map_texture.render(canvas, atlas, dest) { (BOARD_CELL_OFFSET.y as usize * CELL_SIZE as usize + y * CELL_SIZE as usize) as i32,
tracing::error!("Failed to render map: {}", e); CELL_SIZE,
CELL_SIZE,
);
if let Err(e) = tile.render(canvas, atlas, dest) {
tracing::error!("Failed to render map tile: {}", e);
}
}
} }
} }