refactor: split up and move texture-related code into src/texture submodule

This commit is contained in:
2025-07-24 12:41:36 -05:00
parent cfa73c58a8
commit 002da46045
9 changed files with 158 additions and 141 deletions

View File

@@ -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<Window>, position: (i32, i32), direction: Direction, frame: Option<u32>);
}
/// 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<Rect> {
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<Window>, position: (i32, i32), direction: Direction, frame: Option<u32>) {
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<Window>, position: (i32, i32), direction: Direction, frame: Option<u32>) {
let frame = frame.unwrap_or_else(|| self.current_frame());
self.atlas.render(canvas, position, direction, Some(frame));
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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};

View File

@@ -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.
///

View File

@@ -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) {

72
src/texture/animated.rs Normal file
View File

@@ -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<Window>, position: (i32, i32), direction: Direction, frame: Option<u32>) {
let frame = frame.unwrap_or_else(|| self.current_frame());
self.atlas.render(canvas, position, direction, Some(frame));
}
}

67
src/texture/atlas.rs Normal file
View File

@@ -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<Rect> {
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<Window>, position: (i32, i32), direction: Direction, frame: Option<u32>) {
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");
}
}

11
src/texture/mod.rs Normal file
View File

@@ -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<Window>, position: (i32, i32), direction: Direction, frame: Option<u32>);
}
pub mod animated;
pub mod atlas;