diff --git a/src/game/mod.rs b/src/game/mod.rs index 705b93a..7c0c6bc 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -9,15 +9,18 @@ use sdl2::{ video::WindowContext, }; +use crate::entity::r#trait::Entity; use crate::error::{EntityError, GameError, GameResult}; use crate::entity::{ collision::{Collidable, CollisionSystem, EntityId}, ghost::{Ghost, GhostType}, pacman::Pacman, - r#trait::Entity, }; +use crate::map::render::MapRenderer; +use crate::{constants, texture::sprite::SpriteAtlas}; + pub mod state; use state::GameState; @@ -168,13 +171,36 @@ impl Game { } pub fn draw(&mut self, canvas: &mut Canvas, backbuffer: &mut Texture) -> GameResult<()> { + // Only render the map texture once and cache it + if !self.state.map_rendered { + let mut map_texture = self + .state + .texture_creator + .create_texture_target(None, constants::CANVAS_SIZE.x, constants::CANVAS_SIZE.y) + .map_err(|e| GameError::Sdl(e.to_string()))?; + + canvas + .with_texture_canvas(&mut map_texture, |map_canvas| { + let mut map_tiles = Vec::with_capacity(35); + for i in 0..35 { + let tile_name = format!("maze/tiles/{}.png", i); + let tile = SpriteAtlas::get_tile(&self.state.atlas, &tile_name).unwrap(); + map_tiles.push(tile); + } + MapRenderer::render_map(map_canvas, &mut self.state.atlas, &mut map_tiles); + }) + .map_err(|e| GameError::Sdl(e.to_string()))?; + self.state.map_texture = Some(map_texture); + self.state.map_rendered = true; + } + canvas .with_texture_canvas(backbuffer, |canvas| { canvas.set_draw_color(Color::BLACK); canvas.clear(); - self.state - .map - .render(canvas, &mut self.state.atlas, &mut self.state.map_tiles); + if let Some(ref map_texture) = self.state.map_texture { + canvas.copy(map_texture, None, None).unwrap(); + } // Render all items for item in &self.state.items { diff --git a/src/game/state.rs b/src/game/state.rs index 456260b..7174701 100644 --- a/src/game/state.rs +++ b/src/game/state.rs @@ -1,4 +1,8 @@ -use sdl2::{image::LoadTexture, render::TextureCreator, video::WindowContext}; +use sdl2::{ + image::LoadTexture, + render::{Texture, TextureCreator}, + video::WindowContext, +}; use smallvec::SmallVec; use crate::{ @@ -15,7 +19,7 @@ use crate::{ game::EntityId, map::Map, texture::{ - sprite::{AtlasMapper, AtlasTile, SpriteAtlas}, + sprite::{AtlasMapper, SpriteAtlas}, text::TextTexture, }, }; @@ -31,7 +35,6 @@ include!(concat!(env!("OUT_DIR"), "/atlas_data.rs")); pub struct GameState { pub score: u32, pub map: Map, - pub map_tiles: Vec, pub pacman: Pacman, pub pacman_id: EntityId, pub ghosts: SmallVec<[Ghost; 4]>, @@ -49,6 +52,11 @@ pub struct GameState { // Audio pub audio: Audio, + + // Map texture pre-rendering + pub(crate) map_texture: Option>, + pub(crate) map_rendered: bool, + pub(crate) texture_creator: &'static TextureCreator, } impl GameState { @@ -60,7 +68,7 @@ impl GameState { pub fn new(texture_creator: &'static TextureCreator) -> GameResult { let map = Map::new(RAW_BOARD)?; - let pacman_start_node = map.start_positions.pacman; + let start_node = map.start_positions.pacman; let atlas_bytes = get_asset_bytes(Asset::Atlas)?; let atlas_texture = texture_creator.load_texture_bytes(&atlas_bytes).map_err(|e| { @@ -76,17 +84,9 @@ impl GameState { }; let atlas = SpriteAtlas::new(atlas_texture, atlas_mapper); - let mut map_tiles = Vec::with_capacity(35); - for i in 0..35 { - 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 audio = Audio::new(); - let pacman = Pacman::new(&map.graph, pacman_start_node, &atlas)?; + let pacman = Pacman::new(&map.graph, start_node, &atlas)?; // Generate items (pellets and energizers) let items = map.generate_items(&atlas)?; @@ -127,7 +127,6 @@ impl GameState { Ok(Self { map, atlas, - map_tiles, pacman, pacman_id, ghosts, @@ -139,6 +138,9 @@ impl GameState { score: 0, debug_mode: false, collision_system, + map_texture: None, + map_rendered: false, + texture_creator, }) } } diff --git a/src/map/builder.rs b/src/map/builder.rs index 6093701..bf0b361 100644 --- a/src/map/builder.rs +++ b/src/map/builder.rs @@ -6,7 +6,7 @@ use crate::entity::graph::{EdgePermissions, Graph, Node, NodeId}; use crate::entity::item::{Item, ItemType}; use crate::map::parser::MapTileParser; use crate::map::render::MapRenderer; -use crate::texture::sprite::{AtlasTile, Sprite, SpriteAtlas}; +use crate::texture::sprite::{Sprite, SpriteAtlas}; use glam::{IVec2, Vec2}; use sdl2::render::{Canvas, RenderTarget}; use std::collections::{HashMap, VecDeque}; @@ -154,14 +154,6 @@ impl Map { }) } - /// Renders the map to the given canvas. - /// - /// This function draws the static map texture to the screen at the correct - /// position and scale. - pub fn render(&self, canvas: &mut Canvas, atlas: &mut SpriteAtlas, map_tiles: &mut [AtlasTile]) { - MapRenderer::render_map(canvas, atlas, map_tiles); - } - /// Generates Item entities for pellets and energizers from the parsed map. pub fn generate_items(&self, atlas: &SpriteAtlas) -> GameResult> { // Pre-load sprites to avoid repeated texture lookups