Compare commits

...

1 Commits

4 changed files with 32 additions and 47 deletions

View File

@@ -4,8 +4,9 @@ use glam::Vec2;
use sdl2::event::{Event, WindowEvent};
use sdl2::keyboard::Keycode;
use sdl2::render::{Canvas, ScaleMode, Texture, TextureCreator};
use sdl2::ttf::Sdl2TtfContext;
use sdl2::video::{Window, WindowContext};
use sdl2::EventPump;
use sdl2::{AudioSubsystem, EventPump, Sdl, VideoSubsystem};
use tracing::{error, event};
use crate::error::{GameError, GameResult};
@@ -14,26 +15,31 @@ use crate::constants::{CANVAS_SIZE, LOOP_TIME, SCALE};
use crate::game::Game;
use crate::platform::get_platform;
pub struct App<'a> {
pub struct App {
game: Game,
canvas: Canvas<Window>,
event_pump: EventPump,
backbuffer: Texture<'a>,
event_pump: &'static mut EventPump,
backbuffer: Texture<'static>,
paused: bool,
last_tick: Instant,
cursor_pos: Vec2,
}
impl App<'_> {
impl App {
pub fn new() -> GameResult<Self> {
let sdl_context: &'static Sdl = Box::leak(Box::new(sdl2::init().map_err(|e| GameError::Sdl(e.to_string()))?));
let video_subsystem: &'static VideoSubsystem =
Box::leak(Box::new(sdl_context.video().map_err(|e| GameError::Sdl(e.to_string()))?));
let audio_subsystem: &'static AudioSubsystem =
Box::leak(Box::new(sdl_context.audio().map_err(|e| GameError::Sdl(e.to_string()))?));
let ttf_context: &'static Sdl2TtfContext =
Box::leak(Box::new(sdl2::ttf::init().map_err(|e| GameError::Sdl(e.to_string()))?));
let event_pump: &'static mut EventPump =
Box::leak(Box::new(sdl_context.event_pump().map_err(|e| GameError::Sdl(e.to_string()))?));
// Initialize platform-specific console
get_platform().init_console()?;
let sdl_context = sdl2::init().map_err(|e| GameError::Sdl(e.to_string()))?;
let video_subsystem = sdl_context.video().map_err(|e| GameError::Sdl(e.to_string()))?;
let audio_subsystem = sdl_context.audio().map_err(|e| GameError::Sdl(e.to_string()))?;
let ttf_context = sdl2::ttf::init().map_err(|e| GameError::Sdl(e.to_string()))?;
let window = video_subsystem
.window(
"Pac-Man",
@@ -50,18 +56,16 @@ impl App<'_> {
.set_logical_size(CANVAS_SIZE.x, CANVAS_SIZE.y)
.map_err(|e| GameError::Sdl(e.to_string()))?;
let texture_creator_static: &'static TextureCreator<WindowContext> = Box::leak(Box::new(canvas.texture_creator()));
let texture_creator: &'static TextureCreator<WindowContext> = Box::leak(Box::new(canvas.texture_creator()));
let mut game = Game::new(texture_creator_static, &ttf_context, &audio_subsystem)?;
game.audio.set_mute(cfg!(debug_assertions));
let mut game = Game::new(texture_creator, ttf_context, audio_subsystem)?;
// game.audio.set_mute(cfg!(debug_assertions));
let mut backbuffer = texture_creator_static
let mut backbuffer = texture_creator
.create_texture_target(None, CANVAS_SIZE.x, CANVAS_SIZE.y)
.map_err(|e| GameError::Sdl(e.to_string()))?;
backbuffer.set_scale_mode(ScaleMode::Nearest);
let event_pump = sdl_context.event_pump().map_err(|e| GameError::Sdl(e.to_string()))?;
// Initial draw
game.draw(&mut canvas, &mut backbuffer)
.map_err(|e| GameError::Sdl(e.to_string()))?;

View File

@@ -25,7 +25,7 @@ use crate::{
},
map::Map,
texture::{
sprite::{self, AtlasMapper, AtlasTile, SpriteAtlas},
sprite::{AtlasMapper, AtlasTile, SpriteAtlas},
text::TextTexture,
},
};
@@ -59,7 +59,7 @@ pub struct Game {
impl Game {
pub fn new(
texture_creator: &TextureCreator<WindowContext>,
texture_creator: &'static TextureCreator<WindowContext>,
_ttf_context: &sdl2::ttf::Sdl2TtfContext,
_audio_subsystem: &sdl2::AudioSubsystem,
) -> GameResult<Game> {
@@ -74,19 +74,16 @@ impl Game {
.ok_or_else(|| GameError::NotFound("Pac-Man starting position not found in graph".to_string()))?;
let atlas_bytes = get_asset_bytes(Asset::Atlas)?;
let atlas_texture = unsafe {
let texture = texture_creator.load_texture_bytes(&atlas_bytes).map_err(|e| {
if e.to_string().contains("format") || e.to_string().contains("unsupported") {
GameError::Texture(TextureError::InvalidFormat(format!("Unsupported texture format: {e}")))
} else {
GameError::Texture(TextureError::LoadFailed(e.to_string()))
}
})?;
sprite::texture_to_static(texture)
};
let atlas_texture = Box::leak(Box::new(texture_creator.load_texture_bytes(&atlas_bytes).map_err(|e| {
if e.to_string().contains("format") || e.to_string().contains("unsupported") {
GameError::Texture(TextureError::InvalidFormat(format!("Unsupported texture format: {e}")))
} else {
GameError::Texture(TextureError::LoadFailed(e.to_string()))
}
})?));
let atlas_json = get_asset_bytes(Asset::AtlasJson)?;
let atlas_mapper: AtlasMapper = serde_json::from_slice(&atlas_json)?;
let atlas = SpriteAtlas::new(atlas_texture, atlas_mapper);
let atlas = SpriteAtlas::new(unsafe { std::mem::transmute_copy(atlas_texture) }, atlas_mapper);
let mut map_texture = SpriteAtlas::get_tile(&atlas, "maze/full.png")
.ok_or_else(|| GameError::Texture(TextureError::AtlasTileNotFound("maze/full.png".to_string())))?;
@@ -255,6 +252,7 @@ impl Game {
if !item.is_collected() {
item.collect();
self.score += item.get_score();
self.audio.eat();
// Handle energizer effects
if matches!(item.item_type, crate::entity::item::ItemType::Energizer) {

View File

@@ -138,20 +138,3 @@ impl SpriteAtlas {
self.default_color
}
}
/// Converts a `Texture` to a `Texture<'static>` using transmute.
///
/// # Safety
///
/// This function is unsafe because it uses `std::mem::transmute` to change the lifetime
/// of the texture from the original lifetime to `'static`. The caller must ensure that:
///
/// - The original `Texture` will live for the entire duration of the program
/// - No references to the original texture exist that could become invalid
/// - The texture is not dropped while still being used as a `'static` reference
///
/// This is typically used when you have a texture that you know will live for the entire
/// program duration and need to store it in a structure that requires a `'static` lifetime.
pub unsafe fn texture_to_static(texture: Texture) -> Texture<'static> {
std::mem::transmute(texture)
}

View File

@@ -25,7 +25,7 @@ fn test_fruit_kind_increasing_score() {
.collect::<Vec<_>>();
kinds.sort_unstable_by_key(|(index, _)| *index);
assert_eq!(kinds.len(), FruitKind::COUNT as usize);
assert_eq!(kinds.len(), FruitKind::COUNT);
// Check that the score increases as expected
for window in kinds.windows(2) {