mirror of
https://github.com/Xevion/Pac-Man.git
synced 2026-01-30 22:24:58 -06:00
feat(web): add smooth page transitions and WASM loading states
- Implement navigation state tracking with optimistic UI updates - Add loading spinner and error handling for WASM initialization - Insert browser yield points during game initialization to prevent freezing - Redesign leaderboard with tabbed navigation and mock data structure - Add utility CSS classes for consistent page layouts
This commit is contained in:
@@ -32,11 +32,16 @@ impl App {
|
||||
pub fn new() -> GameResult<Self> {
|
||||
info!("Initializing SDL2 application");
|
||||
let sdl_context = sdl2::init().map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||
trace!("Yielding after SDL init");
|
||||
platform::yield_to_browser();
|
||||
|
||||
debug!("Initializing SDL2 subsystems");
|
||||
let ttf_context = sdl2::ttf::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 event_pump = sdl_context.event_pump().map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||
trace!("Yielding after subsystem init");
|
||||
platform::yield_to_browser();
|
||||
|
||||
trace!(
|
||||
width = (CANVAS_SIZE.x as f32 * SCALE).round() as u32,
|
||||
@@ -96,6 +101,8 @@ impl App {
|
||||
// .index(index)
|
||||
.build()
|
||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||
trace!("Yielding after canvas creation");
|
||||
platform::yield_to_browser();
|
||||
|
||||
trace!(
|
||||
logical_width = CANVAS_SIZE.x,
|
||||
@@ -106,12 +113,16 @@ impl App {
|
||||
.set_logical_size(CANVAS_SIZE.x, CANVAS_SIZE.y)
|
||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||
debug!(renderer_info = ?canvas.info(), "Canvas renderer initialized");
|
||||
trace!("Yielding after logical size");
|
||||
platform::yield_to_browser();
|
||||
|
||||
trace!("Creating texture factory");
|
||||
let texture_creator = canvas.texture_creator();
|
||||
|
||||
info!("Starting game initialization");
|
||||
let game = Game::new(canvas, ttf_context, texture_creator, event_pump)?;
|
||||
trace!("Yielding after game init");
|
||||
platform::yield_to_browser();
|
||||
|
||||
info!("Application initialization completed successfully");
|
||||
Ok(App {
|
||||
|
||||
@@ -48,6 +48,7 @@ use crate::{
|
||||
asset::Asset,
|
||||
events::GameCommand,
|
||||
map::render::MapRenderer,
|
||||
platform,
|
||||
systems::{BatchedLinesResource, Bindings, CursorPosition, TtfAtlasResource},
|
||||
texture::sprite::{AtlasMapper, SpriteAtlas},
|
||||
};
|
||||
@@ -116,18 +117,27 @@ impl Game {
|
||||
debug!("Setting up textures and fonts");
|
||||
let (backbuffer, mut map_texture, debug_texture, ttf_atlas) =
|
||||
Self::setup_textures_and_fonts(&mut canvas, &texture_creator, ttf_context)?;
|
||||
trace!("Yielding after texture setup");
|
||||
platform::yield_to_browser();
|
||||
|
||||
debug!("Initializing audio subsystem");
|
||||
let audio = crate::audio::Audio::new();
|
||||
trace!("Yielding after audio init");
|
||||
platform::yield_to_browser();
|
||||
|
||||
debug!("Loading sprite atlas and map tiles");
|
||||
let (mut atlas, map_tiles) = Self::load_atlas_and_map_tiles(&texture_creator)?;
|
||||
trace!("Yielding after atlas load");
|
||||
platform::yield_to_browser();
|
||||
|
||||
debug!("Rendering static map to texture cache");
|
||||
canvas
|
||||
.with_texture_canvas(&mut map_texture, |map_canvas| {
|
||||
MapRenderer::render_map(map_canvas, &mut atlas, &map_tiles);
|
||||
})
|
||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||
trace!("Yielding after map render");
|
||||
platform::yield_to_browser();
|
||||
|
||||
debug!("Building navigation graph from map layout");
|
||||
let map = Map::new(constants::RAW_BOARD)?;
|
||||
@@ -235,30 +245,41 @@ impl Game {
|
||||
sdl2::render::Texture,
|
||||
crate::texture::ttf::TtfAtlas,
|
||||
)> {
|
||||
trace!("Creating backbuffer texture");
|
||||
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);
|
||||
platform::yield_to_browser();
|
||||
|
||||
trace!("Creating map texture");
|
||||
let mut map_texture = texture_creator
|
||||
.create_texture_target(None, CANVAS_SIZE.x, CANVAS_SIZE.y)
|
||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||
map_texture.set_scale_mode(ScaleMode::Nearest);
|
||||
platform::yield_to_browser();
|
||||
|
||||
trace!("Creating debug texture");
|
||||
let output_size = constants::LARGE_CANVAS_SIZE;
|
||||
let mut debug_texture = texture_creator
|
||||
.create_texture_target(Some(sdl2::pixels::PixelFormatEnum::ARGB8888), output_size.x, output_size.y)
|
||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||
debug_texture.set_blend_mode(BlendMode::Blend);
|
||||
debug_texture.set_scale_mode(ScaleMode::Nearest);
|
||||
platform::yield_to_browser();
|
||||
|
||||
trace!("Loading font");
|
||||
let font_data: &'static [u8] = Asset::Font.get_bytes()?.to_vec().leak();
|
||||
let font_asset = RWops::from_bytes(font_data).map_err(|_| GameError::Sdl("Failed to load font".to_string()))?;
|
||||
let debug_font = ttf_context
|
||||
.load_font_from_rwops(font_asset, constants::ui::DEBUG_FONT_SIZE)
|
||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||
|
||||
trace!("Creating TTF atlas");
|
||||
let mut ttf_atlas = crate::texture::ttf::TtfAtlas::new(texture_creator, &debug_font)?;
|
||||
platform::yield_to_browser();
|
||||
|
||||
trace!("Populating TTF atlas");
|
||||
ttf_atlas.populate_atlas(canvas, texture_creator, &debug_font)?;
|
||||
|
||||
Ok((backbuffer, map_texture, debug_texture, ttf_atlas))
|
||||
|
||||
@@ -20,6 +20,10 @@ pub fn sleep(duration: Duration, focused: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
/// No-op on desktop - only needed for browser event loop yielding.
|
||||
#[inline]
|
||||
pub fn yield_to_browser() {}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub fn init_console(force_console: bool) -> Result<(), PlatformError> {
|
||||
use crate::formatter::CustomFormatter;
|
||||
|
||||
@@ -49,6 +49,15 @@ pub fn sleep(duration: Duration, _focused: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Yields control to browser event loop without delay.
|
||||
/// Allows page transitions, animations, and events to process during initialization.
|
||||
/// Uses ASYNCIFY to pause/resume WASM execution.
|
||||
pub fn yield_to_browser() {
|
||||
unsafe {
|
||||
emscripten_sleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_console(_force_console: bool) -> Result<(), PlatformError> {
|
||||
use tracing_subscriber::{fmt, layer::SubscriberExt, EnvFilter};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user