mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-09 04:07:57 -06:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c489f32908 |
36
src/app.rs
36
src/app.rs
@@ -4,8 +4,9 @@ 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};
|
||||||
|
use sdl2::ttf::Sdl2TtfContext;
|
||||||
use sdl2::video::{Window, WindowContext};
|
use sdl2::video::{Window, WindowContext};
|
||||||
use sdl2::EventPump;
|
use sdl2::{AudioSubsystem, EventPump, Sdl, VideoSubsystem};
|
||||||
use tracing::{error, event};
|
use tracing::{error, event};
|
||||||
|
|
||||||
use crate::error::{GameError, GameResult};
|
use crate::error::{GameError, GameResult};
|
||||||
@@ -14,26 +15,31 @@ use crate::constants::{CANVAS_SIZE, LOOP_TIME, SCALE};
|
|||||||
use crate::game::Game;
|
use crate::game::Game;
|
||||||
use crate::platform::get_platform;
|
use crate::platform::get_platform;
|
||||||
|
|
||||||
pub struct App<'a> {
|
pub struct App {
|
||||||
game: Game,
|
game: Game,
|
||||||
canvas: Canvas<Window>,
|
canvas: Canvas<Window>,
|
||||||
event_pump: EventPump,
|
event_pump: &'static mut EventPump,
|
||||||
backbuffer: Texture<'a>,
|
backbuffer: Texture<'static>,
|
||||||
paused: bool,
|
paused: bool,
|
||||||
last_tick: Instant,
|
last_tick: Instant,
|
||||||
cursor_pos: Vec2,
|
cursor_pos: Vec2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App<'_> {
|
impl App {
|
||||||
pub fn new() -> GameResult<Self> {
|
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
|
// Initialize platform-specific console
|
||||||
get_platform().init_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
|
let window = video_subsystem
|
||||||
.window(
|
.window(
|
||||||
"Pac-Man",
|
"Pac-Man",
|
||||||
@@ -50,18 +56,16 @@ impl App<'_> {
|
|||||||
.set_logical_size(CANVAS_SIZE.x, CANVAS_SIZE.y)
|
.set_logical_size(CANVAS_SIZE.x, CANVAS_SIZE.y)
|
||||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
.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)?;
|
let mut game = Game::new(texture_creator, ttf_context, audio_subsystem)?;
|
||||||
game.audio.set_mute(cfg!(debug_assertions));
|
// 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)
|
.create_texture_target(None, CANVAS_SIZE.x, CANVAS_SIZE.y)
|
||||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||||
backbuffer.set_scale_mode(ScaleMode::Nearest);
|
backbuffer.set_scale_mode(ScaleMode::Nearest);
|
||||||
|
|
||||||
let event_pump = sdl_context.event_pump().map_err(|e| GameError::Sdl(e.to_string()))?;
|
|
||||||
|
|
||||||
// Initial draw
|
// Initial draw
|
||||||
game.draw(&mut canvas, &mut backbuffer)
|
game.draw(&mut canvas, &mut backbuffer)
|
||||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||||
|
|||||||
24
src/game.rs
24
src/game.rs
@@ -25,7 +25,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
map::Map,
|
map::Map,
|
||||||
texture::{
|
texture::{
|
||||||
sprite::{self, AtlasMapper, AtlasTile, SpriteAtlas},
|
sprite::{AtlasMapper, AtlasTile, SpriteAtlas},
|
||||||
text::TextTexture,
|
text::TextTexture,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -59,7 +59,7 @@ pub struct Game {
|
|||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
texture_creator: &TextureCreator<WindowContext>,
|
texture_creator: &'static TextureCreator<WindowContext>,
|
||||||
_ttf_context: &sdl2::ttf::Sdl2TtfContext,
|
_ttf_context: &sdl2::ttf::Sdl2TtfContext,
|
||||||
_audio_subsystem: &sdl2::AudioSubsystem,
|
_audio_subsystem: &sdl2::AudioSubsystem,
|
||||||
) -> GameResult<Game> {
|
) -> GameResult<Game> {
|
||||||
@@ -74,19 +74,16 @@ impl Game {
|
|||||||
.ok_or_else(|| GameError::NotFound("Pac-Man starting position not found in graph".to_string()))?;
|
.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_bytes = get_asset_bytes(Asset::Atlas)?;
|
||||||
let atlas_texture = unsafe {
|
let atlas_texture = Box::leak(Box::new(texture_creator.load_texture_bytes(&atlas_bytes).map_err(|e| {
|
||||||
let texture = texture_creator.load_texture_bytes(&atlas_bytes).map_err(|e| {
|
if e.to_string().contains("format") || e.to_string().contains("unsupported") {
|
||||||
if e.to_string().contains("format") || e.to_string().contains("unsupported") {
|
GameError::Texture(TextureError::InvalidFormat(format!("Unsupported texture format: {e}")))
|
||||||
GameError::Texture(TextureError::InvalidFormat(format!("Unsupported texture format: {e}")))
|
} else {
|
||||||
} else {
|
GameError::Texture(TextureError::LoadFailed(e.to_string()))
|
||||||
GameError::Texture(TextureError::LoadFailed(e.to_string()))
|
}
|
||||||
}
|
})?));
|
||||||
})?;
|
|
||||||
sprite::texture_to_static(texture)
|
|
||||||
};
|
|
||||||
let atlas_json = get_asset_bytes(Asset::AtlasJson)?;
|
let atlas_json = get_asset_bytes(Asset::AtlasJson)?;
|
||||||
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(unsafe { std::mem::transmute_copy(atlas_texture) }, atlas_mapper);
|
||||||
|
|
||||||
let mut map_texture = SpriteAtlas::get_tile(&atlas, "maze/full.png")
|
let mut map_texture = SpriteAtlas::get_tile(&atlas, "maze/full.png")
|
||||||
.ok_or_else(|| GameError::Texture(TextureError::AtlasTileNotFound("maze/full.png".to_string())))?;
|
.ok_or_else(|| GameError::Texture(TextureError::AtlasTileNotFound("maze/full.png".to_string())))?;
|
||||||
@@ -255,6 +252,7 @@ impl Game {
|
|||||||
if !item.is_collected() {
|
if !item.is_collected() {
|
||||||
item.collect();
|
item.collect();
|
||||||
self.score += item.get_score();
|
self.score += item.get_score();
|
||||||
|
self.audio.eat();
|
||||||
|
|
||||||
// Handle energizer effects
|
// Handle energizer effects
|
||||||
if matches!(item.item_type, crate::entity::item::ItemType::Energizer) {
|
if matches!(item.item_type, crate::entity::item::ItemType::Energizer) {
|
||||||
|
|||||||
@@ -138,20 +138,3 @@ impl SpriteAtlas {
|
|||||||
self.default_color
|
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)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ fn test_fruit_kind_increasing_score() {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
kinds.sort_unstable_by_key(|(index, _)| *index);
|
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
|
// Check that the score increases as expected
|
||||||
for window in kinds.windows(2) {
|
for window in kinds.windows(2) {
|
||||||
|
|||||||
Reference in New Issue
Block a user