diff --git a/src/animation.rs b/src/animation.rs deleted file mode 100644 index 6b9b13a..0000000 --- a/src/animation.rs +++ /dev/null @@ -1,136 +0,0 @@ -//! This module provides a simple animation and atlas system for textures. -use sdl2::{ - rect::Rect, - render::{Canvas, Texture}, - video::Window, -}; - -use crate::direction::Direction; - -/// Trait for drawable atlas-based textures -pub trait FrameDrawn { - fn render(&self, canvas: &mut Canvas, position: (i32, i32), direction: Direction, frame: Option); -} - -/// A texture atlas abstraction for static (non-animated) rendering. -pub struct AtlasTexture<'a> { - pub raw_texture: Texture<'a>, - pub offset: (i32, i32), - pub frame_count: u32, - pub frame_width: u32, - pub frame_height: u32, -} - -impl<'a> AtlasTexture<'a> { - pub fn new(texture: Texture<'a>, frame_count: u32, frame_width: u32, frame_height: u32, offset: Option<(i32, i32)>) -> Self { - AtlasTexture { - raw_texture: texture, - frame_count, - frame_width, - frame_height, - offset: offset.unwrap_or((0, 0)), - } - } - - pub fn get_frame_rect(&self, frame: u32) -> Option { - if frame >= self.frame_count { - return None; - } - Some(Rect::new( - frame as i32 * self.frame_width as i32, - 0, - self.frame_width, - self.frame_height, - )) - } - - pub fn set_color_modulation(&mut self, r: u8, g: u8, b: u8) { - self.raw_texture.set_color_mod(r, g, b); - } -} - -impl<'a> FrameDrawn for AtlasTexture<'a> { - fn render(&self, canvas: &mut Canvas, position: (i32, i32), direction: Direction, frame: Option) { - let texture_source_frame_rect = self.get_frame_rect(frame.unwrap_or(0)); - let canvas_destination_rect = Rect::new( - position.0 + self.offset.0, - position.1 + self.offset.1, - self.frame_width, - self.frame_height, - ); - canvas - .copy_ex( - &self.raw_texture, - texture_source_frame_rect, - Some(canvas_destination_rect), - direction.angle(), - None, - false, - false, - ) - .expect("Could not render texture on canvas"); - } -} - -/// An animated texture using a texture atlas. -pub struct AnimatedAtlasTexture<'a> { - pub atlas: AtlasTexture<'a>, - pub ticks_per_frame: u32, - pub ticker: u32, - pub reversed: bool, - pub paused: bool, -} - -impl<'a> AnimatedAtlasTexture<'a> { - pub fn new( - texture: Texture<'a>, - ticks_per_frame: u32, - frame_count: u32, - width: u32, - height: u32, - offset: Option<(i32, i32)>, - ) -> Self { - AnimatedAtlasTexture { - atlas: AtlasTexture::new(texture, frame_count, width, height, offset), - ticks_per_frame, - ticker: 0, - reversed: false, - paused: false, - } - } - - fn current_frame(&self) -> u32 { - self.ticker / self.ticks_per_frame - } - - /// Advances the animation by one tick, unless paused. - pub fn tick(&mut self) { - if self.paused { - return; - } - if self.reversed { - if self.ticker > 0 { - self.ticker -= 1; - } - if self.ticker == 0 { - self.reversed = !self.reversed; - } - } else { - self.ticker += 1; - if self.ticker + 1 == self.ticks_per_frame * self.atlas.frame_count { - self.reversed = !self.reversed; - } - } - } - - pub fn set_color_modulation(&mut self, r: u8, g: u8, b: u8) { - self.atlas.set_color_modulation(r, g, b); - } -} - -impl<'a> FrameDrawn for AnimatedAtlasTexture<'a> { - fn render(&self, canvas: &mut Canvas, position: (i32, i32), direction: Direction, frame: Option) { - let frame = frame.unwrap_or_else(|| self.current_frame()); - self.atlas.render(canvas, position, direction, Some(frame)); - } -} diff --git a/src/edible.rs b/src/edible.rs index 0d63028..d8306bd 100644 --- a/src/edible.rs +++ b/src/edible.rs @@ -1,9 +1,10 @@ //! Edible entity for Pac-Man: pellets, power pellets, and fruits. -use crate::animation::{AtlasTexture, FrameDrawn}; use crate::constants::{FruitType, MapTile, BOARD_HEIGHT, BOARD_WIDTH}; use crate::direction::Direction; use crate::entity::{Entity, Renderable, StaticEntity}; use crate::map::Map; +use crate::texture::atlas::AtlasTexture; +use crate::texture::FrameDrawn; use glam::{IVec2, UVec2}; use sdl2::{render::Canvas, video::Window}; use std::cell::RefCell; diff --git a/src/entity/ghost.rs b/src/entity/ghost.rs index dfe4817..e33bfbc 100644 --- a/src/entity/ghost.rs +++ b/src/entity/ghost.rs @@ -2,13 +2,14 @@ use rand::rngs::SmallRng; use rand::Rng; use rand::SeedableRng; -use crate::animation::{AnimatedAtlasTexture, FrameDrawn}; use crate::constants::{MapTile, BOARD_WIDTH}; use crate::direction::Direction; use crate::entity::pacman::Pacman; use crate::entity::{Entity, MovableEntity, Moving, Renderable}; use crate::map::Map; use crate::modulation::{SimpleTickModulator, TickModulator}; +use crate::texture::animated::AnimatedAtlasTexture; +use crate::texture::FrameDrawn; use glam::{IVec2, UVec2}; use sdl2::pixels::Color; use sdl2::render::Texture; diff --git a/src/entity/pacman.rs b/src/entity/pacman.rs index 71238e0..bccd487 100644 --- a/src/entity/pacman.rs +++ b/src/entity/pacman.rs @@ -8,11 +8,12 @@ use sdl2::{ }; use crate::{ - animation::{AnimatedAtlasTexture, FrameDrawn}, direction::Direction, entity::{Entity, MovableEntity, Moving, Renderable, StaticEntity}, map::Map, modulation::{SimpleTickModulator, TickModulator}, + texture::animated::AnimatedAtlasTexture, + texture::FrameDrawn, }; use glam::{IVec2, UVec2}; diff --git a/src/game.rs b/src/game.rs index 0f84802..4e28938 100644 --- a/src/game.rs +++ b/src/game.rs @@ -15,7 +15,6 @@ use sdl2::ttf::Font; use sdl2::video::WindowContext; use sdl2::{pixels::Color, render::Canvas, video::Window}; -use crate::animation::AtlasTexture; use crate::asset::{get_asset_bytes, Asset}; use crate::audio::Audio; use crate::constants::RAW_BOARD; @@ -26,6 +25,7 @@ use crate::entity::blinky::Blinky; use crate::entity::pacman::Pacman; use crate::entity::Renderable; use crate::map::Map; +use crate::texture::atlas::AtlasTexture; /// The main game state. /// diff --git a/src/main.rs b/src/main.rs index 5bb8110..1024588 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,7 +52,6 @@ unsafe fn attach_console() { // Do NOT call AllocConsole here - we don't want a console when launched from Explorer } -mod animation; mod asset; mod audio; mod constants; @@ -66,6 +65,7 @@ mod game; mod helper; mod map; mod modulation; +mod texture; #[cfg(not(target_os = "emscripten"))] fn sleep(value: Duration) { diff --git a/src/texture/animated.rs b/src/texture/animated.rs new file mode 100644 index 0000000..d8e4b05 --- /dev/null +++ b/src/texture/animated.rs @@ -0,0 +1,72 @@ +//! This module provides a simple animation and atlas system for textures. +use sdl2::{ + render::{Canvas, Texture}, + video::Window, +}; + +use crate::direction::Direction; +use crate::texture::atlas::AtlasTexture; +use crate::texture::FrameDrawn; + +/// An animated texture using a texture atlas. +pub struct AnimatedAtlasTexture<'a> { + pub atlas: AtlasTexture<'a>, + pub ticks_per_frame: u32, + pub ticker: u32, + pub reversed: bool, + pub paused: bool, +} + +impl<'a> AnimatedAtlasTexture<'a> { + pub fn new( + texture: Texture<'a>, + ticks_per_frame: u32, + frame_count: u32, + width: u32, + height: u32, + offset: Option<(i32, i32)>, + ) -> Self { + AnimatedAtlasTexture { + atlas: AtlasTexture::new(texture, frame_count, width, height, offset), + ticks_per_frame, + ticker: 0, + reversed: false, + paused: false, + } + } + + fn current_frame(&self) -> u32 { + self.ticker / self.ticks_per_frame + } + + /// Advances the animation by one tick, unless paused. + pub fn tick(&mut self) { + if self.paused { + return; + } + if self.reversed { + if self.ticker > 0 { + self.ticker -= 1; + } + if self.ticker == 0 { + self.reversed = !self.reversed; + } + } else { + self.ticker += 1; + if self.ticker + 1 == self.ticks_per_frame * self.atlas.frame_count { + self.reversed = !self.reversed; + } + } + } + + pub fn set_color_modulation(&mut self, r: u8, g: u8, b: u8) { + self.atlas.set_color_modulation(r, g, b); + } +} + +impl<'a> FrameDrawn for AnimatedAtlasTexture<'a> { + fn render(&self, canvas: &mut Canvas, position: (i32, i32), direction: Direction, frame: Option) { + let frame = frame.unwrap_or_else(|| self.current_frame()); + self.atlas.render(canvas, position, direction, Some(frame)); + } +} diff --git a/src/texture/atlas.rs b/src/texture/atlas.rs new file mode 100644 index 0000000..8a27fa7 --- /dev/null +++ b/src/texture/atlas.rs @@ -0,0 +1,67 @@ +use sdl2::{ + rect::Rect, + render::{Canvas, Texture}, + video::Window, +}; + +use crate::{direction::Direction, texture::FrameDrawn}; + +/// A texture atlas abstraction for static (non-animated) rendering. +pub struct AtlasTexture<'a> { + pub raw_texture: Texture<'a>, + pub offset: (i32, i32), + pub frame_count: u32, + pub frame_width: u32, + pub frame_height: u32, +} + +impl<'a> AtlasTexture<'a> { + pub fn new(texture: Texture<'a>, frame_count: u32, frame_width: u32, frame_height: u32, offset: Option<(i32, i32)>) -> Self { + AtlasTexture { + raw_texture: texture, + frame_count, + frame_width, + frame_height, + offset: offset.unwrap_or((0, 0)), + } + } + + pub fn get_frame_rect(&self, frame: u32) -> Option { + if frame >= self.frame_count { + return None; + } + Some(Rect::new( + frame as i32 * self.frame_width as i32, + 0, + self.frame_width, + self.frame_height, + )) + } + + pub fn set_color_modulation(&mut self, r: u8, g: u8, b: u8) { + self.raw_texture.set_color_mod(r, g, b); + } +} + +impl<'a> FrameDrawn for AtlasTexture<'a> { + fn render(&self, canvas: &mut Canvas, position: (i32, i32), direction: Direction, frame: Option) { + let texture_source_frame_rect = self.get_frame_rect(frame.unwrap_or(0)); + let canvas_destination_rect = Rect::new( + position.0 + self.offset.0, + position.1 + self.offset.1, + self.frame_width, + self.frame_height, + ); + canvas + .copy_ex( + &self.raw_texture, + texture_source_frame_rect, + Some(canvas_destination_rect), + direction.angle(), + None, + false, + false, + ) + .expect("Could not render texture on canvas"); + } +} diff --git a/src/texture/mod.rs b/src/texture/mod.rs new file mode 100644 index 0000000..01683a8 --- /dev/null +++ b/src/texture/mod.rs @@ -0,0 +1,11 @@ +use sdl2::{render::Canvas, video::Window}; + +use crate::direction::Direction; + +/// Trait for drawable atlas-based textures +pub trait FrameDrawn { + fn render(&self, canvas: &mut Canvas, position: (i32, i32), direction: Direction, frame: Option); +} + +pub mod animated; +pub mod atlas;