mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-06 23:15:42 -06:00
docs: minor documentation commentsa cross project
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
//! This module provides a simple animation system for textures.
|
||||||
use sdl2::{
|
use sdl2::{
|
||||||
rect::Rect,
|
rect::Rect,
|
||||||
render::{Canvas, Texture},
|
render::{Canvas, Texture},
|
||||||
@@ -6,18 +7,37 @@ use sdl2::{
|
|||||||
|
|
||||||
use crate::direction::Direction;
|
use crate::direction::Direction;
|
||||||
|
|
||||||
|
/// An animated texture, which is a texture that is rendered as a series of
|
||||||
|
/// frames.
|
||||||
pub struct AnimatedTexture<'a> {
|
pub struct AnimatedTexture<'a> {
|
||||||
raw_texture: Texture<'a>,
|
raw_texture: Texture<'a>,
|
||||||
|
/// The current tick of the animation.
|
||||||
ticker: u32,
|
ticker: u32,
|
||||||
|
/// Whether the animation is currently playing in reverse.
|
||||||
reversed: bool,
|
reversed: bool,
|
||||||
|
/// The offset of the texture, in pixels.
|
||||||
offset: (i32, i32),
|
offset: (i32, i32),
|
||||||
|
/// The number of ticks per frame.
|
||||||
ticks_per_frame: u32,
|
ticks_per_frame: u32,
|
||||||
|
/// The number of frames in the animation.
|
||||||
frame_count: u32,
|
frame_count: u32,
|
||||||
|
/// The width of each frame, in pixels.
|
||||||
frame_width: u32,
|
frame_width: u32,
|
||||||
|
/// The height of each frame, in pixels.
|
||||||
frame_height: u32,
|
frame_height: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AnimatedTexture<'a> {
|
impl<'a> AnimatedTexture<'a> {
|
||||||
|
/// Creates a new `AnimatedTexture`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `texture` - The texture to animate.
|
||||||
|
/// * `ticks_per_frame` - The number of ticks to display each frame for.
|
||||||
|
/// * `frame_count` - The number of frames in the animation.
|
||||||
|
/// * `frame_width` - The width of each frame.
|
||||||
|
/// * `frame_height` - The height of each frame.
|
||||||
|
/// * `offset` - The offset of the texture, in pixels.
|
||||||
pub fn new(
|
pub fn new(
|
||||||
texture: Texture<'a>,
|
texture: Texture<'a>,
|
||||||
ticks_per_frame: u32,
|
ticks_per_frame: u32,
|
||||||
@@ -38,12 +58,14 @@ impl<'a> AnimatedTexture<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the current frame number
|
/// Returns the current frame number.
|
||||||
fn current_frame(&self) -> u32 {
|
fn current_frame(&self) -> u32 {
|
||||||
self.ticker / self.ticks_per_frame
|
self.ticker / self.ticks_per_frame
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move to the next frame. If we are at the end of the animation, reverse the direction
|
/// Advances the animation by one tick.
|
||||||
|
///
|
||||||
|
/// The animation will play forwards, then backwards, then forwards, and so on.
|
||||||
pub fn tick(&mut self) {
|
pub fn tick(&mut self) {
|
||||||
if self.reversed {
|
if self.reversed {
|
||||||
self.ticker -= 1;
|
self.ticker -= 1;
|
||||||
@@ -60,7 +82,7 @@ impl<'a> AnimatedTexture<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the frame rect (portion of the texture to render) for the given frame.
|
/// Calculates the source rectangle for the given frame.
|
||||||
fn get_frame_rect(&self, frame: u32) -> Rect {
|
fn get_frame_rect(&self, frame: u32) -> Rect {
|
||||||
if frame >= self.frame_count {
|
if frame >= self.frame_count {
|
||||||
panic!("Frame {} is out of bounds for this texture", frame);
|
panic!("Frame {} is out of bounds for this texture", frame);
|
||||||
@@ -74,6 +96,13 @@ impl<'a> AnimatedTexture<'a> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Renders the animation to the canvas.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `canvas` - The canvas to render to.
|
||||||
|
/// * `position` - The position to render the animation at.
|
||||||
|
/// * `direction` - The direction the animation is facing.
|
||||||
pub fn render(
|
pub fn render(
|
||||||
&mut self,
|
&mut self,
|
||||||
canvas: &mut Canvas<Window>,
|
canvas: &mut Canvas<Window>,
|
||||||
@@ -84,7 +113,15 @@ impl<'a> AnimatedTexture<'a> {
|
|||||||
self.tick();
|
self.tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Functions like render, but only ticks the animation until the given frame is reached.
|
/// Renders the animation to the canvas, but only ticks the animation until
|
||||||
|
/// the given frame is reached.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `canvas` - The canvas to render to.
|
||||||
|
/// * `position` - The position to render the animation at.
|
||||||
|
/// * `direction` - The direction the animation is facing.
|
||||||
|
/// * `frame` - The frame to render until.
|
||||||
pub fn render_until(
|
pub fn render_until(
|
||||||
&mut self,
|
&mut self,
|
||||||
canvas: &mut Canvas<Window>,
|
canvas: &mut Canvas<Window>,
|
||||||
@@ -92,7 +129,9 @@ impl<'a> AnimatedTexture<'a> {
|
|||||||
direction: Direction,
|
direction: Direction,
|
||||||
frame: u32,
|
frame: u32,
|
||||||
) {
|
) {
|
||||||
// TODO: If the frame we're targeting is in the opposite direction (due to self.reverse), we should pre-emptively reverse.
|
// TODO: If the frame we're targeting is in the opposite direction (due
|
||||||
|
// to self.reverse), we should pre-emptively reverse. This would require
|
||||||
|
// a more complex ticking mechanism.
|
||||||
let current = self.current_frame();
|
let current = self.current_frame();
|
||||||
self.render_static(canvas, position, direction, Some(current));
|
self.render_static(canvas, position, direction, Some(current));
|
||||||
|
|
||||||
@@ -101,7 +140,14 @@ impl<'a> AnimatedTexture<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renders a specific frame of the animation. Defaults to the current frame.
|
/// Renders a specific frame of the animation.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `canvas` - The canvas to render to.
|
||||||
|
/// * `position` - The position to render the animation at.
|
||||||
|
/// * `direction` - The direction the animation is facing.
|
||||||
|
/// * `frame` - The frame to render. If `None`, the current frame is used.
|
||||||
pub fn render_static(
|
pub fn render_static(
|
||||||
&mut self,
|
&mut self,
|
||||||
canvas: &mut Canvas<Window>,
|
canvas: &mut Canvas<Window>,
|
||||||
|
|||||||
11
src/audio.rs
11
src/audio.rs
@@ -1,16 +1,21 @@
|
|||||||
|
//! This module handles the audio playback for the game.
|
||||||
use sdl2::{
|
use sdl2::{
|
||||||
mixer::{self, Chunk, InitFlag, LoaderRWops, DEFAULT_FORMAT},
|
mixer::{self, Chunk, InitFlag, LoaderRWops, DEFAULT_FORMAT},
|
||||||
rwops::RWops,
|
rwops::RWops,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Embed sound files directly into the executable
|
|
||||||
const SOUND_1_DATA: &[u8] = include_bytes!("../assets/wav/1.ogg");
|
const SOUND_1_DATA: &[u8] = include_bytes!("../assets/wav/1.ogg");
|
||||||
const SOUND_2_DATA: &[u8] = include_bytes!("../assets/wav/2.ogg");
|
const SOUND_2_DATA: &[u8] = include_bytes!("../assets/wav/2.ogg");
|
||||||
const SOUND_3_DATA: &[u8] = include_bytes!("../assets/wav/3.ogg");
|
const SOUND_3_DATA: &[u8] = include_bytes!("../assets/wav/3.ogg");
|
||||||
const SOUND_4_DATA: &[u8] = include_bytes!("../assets/wav/4.ogg");
|
const SOUND_4_DATA: &[u8] = include_bytes!("../assets/wav/4.ogg");
|
||||||
|
|
||||||
|
/// An array of all the sound effect data.
|
||||||
const SOUND_DATA: [&[u8]; 4] = [SOUND_1_DATA, SOUND_2_DATA, SOUND_3_DATA, SOUND_4_DATA];
|
const SOUND_DATA: [&[u8]; 4] = [SOUND_1_DATA, SOUND_2_DATA, SOUND_3_DATA, SOUND_4_DATA];
|
||||||
|
|
||||||
|
/// The audio system for the game.
|
||||||
|
///
|
||||||
|
/// This struct is responsible for initializing the audio device, loading sounds,
|
||||||
|
/// and playing them.
|
||||||
pub struct Audio {
|
pub struct Audio {
|
||||||
_mixer_context: mixer::Sdl2MixerContext,
|
_mixer_context: mixer::Sdl2MixerContext,
|
||||||
sounds: Vec<Chunk>,
|
sounds: Vec<Chunk>,
|
||||||
@@ -18,6 +23,7 @@ pub struct Audio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Audio {
|
impl Audio {
|
||||||
|
/// Creates a new `Audio` instance.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let frequency = 44100;
|
let frequency = 44100;
|
||||||
let format = DEFAULT_FORMAT;
|
let format = DEFAULT_FORMAT;
|
||||||
@@ -53,6 +59,9 @@ impl Audio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Plays the "eat" sound effect.
|
||||||
|
///
|
||||||
|
/// This function also logs the time since the last sound effect was played.
|
||||||
pub fn eat(&mut self) {
|
pub fn eat(&mut self) {
|
||||||
if let Some(chunk) = self.sounds.get(self.next_sound_index) {
|
if let Some(chunk) = self.sounds.get(self.next_sound_index) {
|
||||||
match mixer::Channel(0).play(chunk, 0) {
|
match mixer::Channel(0).play(chunk, 0) {
|
||||||
|
|||||||
@@ -1,21 +1,40 @@
|
|||||||
|
//! This module contains all the constants used in the game.
|
||||||
|
|
||||||
|
/// The width of the game board, in cells.
|
||||||
pub const BOARD_WIDTH: u32 = 28;
|
pub const BOARD_WIDTH: u32 = 28;
|
||||||
pub const BOARD_HEIGHT: u32 = 31; // Adjusted to fit map texture?
|
/// The height of the game board, in cells.
|
||||||
|
pub const BOARD_HEIGHT: u32 = 31;
|
||||||
|
/// The size of each cell, in pixels.
|
||||||
pub const CELL_SIZE: u32 = 24;
|
pub const CELL_SIZE: u32 = 24;
|
||||||
|
|
||||||
pub const BOARD_OFFSET: (u32, u32) = (0, 3); // Relative cell offset for where map text / grid starts
|
/// The offset of the game board from the top-left corner of the window, in
|
||||||
|
/// cells.
|
||||||
|
pub const BOARD_OFFSET: (u32, u32) = (0, 3);
|
||||||
|
|
||||||
|
/// The width of the window, in pixels.
|
||||||
pub const WINDOW_WIDTH: u32 = CELL_SIZE * BOARD_WIDTH;
|
pub const WINDOW_WIDTH: u32 = CELL_SIZE * BOARD_WIDTH;
|
||||||
pub const WINDOW_HEIGHT: u32 = CELL_SIZE * (BOARD_HEIGHT + 6); // Map texture is 6 cells taller (3 above, 3 below) than the grid
|
/// The height of the window, in pixels.
|
||||||
|
///
|
||||||
|
/// The map texture is 6 cells taller than the grid (3 above, 3 below), so we
|
||||||
|
/// add 6 to the board height to get the window height.
|
||||||
|
pub const WINDOW_HEIGHT: u32 = CELL_SIZE * (BOARD_HEIGHT + 6);
|
||||||
|
|
||||||
|
/// An enum representing the different types of tiles on the map.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub enum MapTile {
|
pub enum MapTile {
|
||||||
|
/// An empty tile.
|
||||||
Empty,
|
Empty,
|
||||||
|
/// A wall tile.
|
||||||
Wall,
|
Wall,
|
||||||
|
/// A regular pellet.
|
||||||
Pellet,
|
Pellet,
|
||||||
|
/// A power pellet.
|
||||||
PowerPellet,
|
PowerPellet,
|
||||||
|
/// A starting position for an entity.
|
||||||
StartingPosition(u8),
|
StartingPosition(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The raw layout of the game board, as a 2D array of characters.
|
||||||
pub const RAW_BOARD: [&str; BOARD_HEIGHT as usize] = [
|
pub const RAW_BOARD: [&str; BOARD_HEIGHT as usize] = [
|
||||||
"############################",
|
"############################",
|
||||||
"#............##............#",
|
"#............##............#",
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
|
//! This module defines the `Entity` trait, which is implemented by all game
|
||||||
|
//! objects that can be moved and rendered.
|
||||||
|
|
||||||
|
/// A trait for game objects that can be moved and rendered.
|
||||||
pub trait Entity {
|
pub trait Entity {
|
||||||
// Returns true if the entity is colliding with the other entity
|
/// Returns true if the entity is colliding with the other entity.
|
||||||
fn is_colliding(&self, other: &dyn Entity) -> bool;
|
fn is_colliding(&self, other: &dyn Entity) -> bool;
|
||||||
// Returns the absolute position of the entity
|
/// Returns the absolute position of the entity, in pixels.
|
||||||
fn position(&self) -> (i32, i32);
|
fn position(&self) -> (i32, i32);
|
||||||
// Returns the cell position of the entity (XY position within the grid)
|
/// Returns the cell position of the entity, in grid coordinates.
|
||||||
fn cell_position(&self) -> (u32, u32);
|
fn cell_position(&self) -> (u32, u32);
|
||||||
|
/// Returns the position of the entity within its current cell, in pixels.
|
||||||
fn internal_position(&self) -> (u32, u32);
|
fn internal_position(&self) -> (u32, u32);
|
||||||
// Tick the entity (move it, perform collision checks, etc)
|
/// Ticks the entity, which updates its state and position.
|
||||||
fn tick(&mut self);
|
fn tick(&mut self);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! This module contains helper functions that are used throughout the game.
|
||||||
|
|
||||||
/// Checks if two grid positions are adjacent to each other
|
/// Checks if two grid positions are adjacent to each other
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
|
|||||||
22
src/main.rs
22
src/main.rs
@@ -22,6 +22,11 @@ use winapi::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Attaches the process to the parent console on Windows.
|
||||||
|
///
|
||||||
|
/// This allows the application to print to the console when run from a terminal,
|
||||||
|
/// which is useful for debugging purposes. If the application is not run from a
|
||||||
|
/// terminal, this function does nothing.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
unsafe fn attach_console() {
|
unsafe fn attach_console() {
|
||||||
if GetConsoleWindow() != std::ptr::null_mut() as HWND {
|
if GetConsoleWindow() != std::ptr::null_mut() as HWND {
|
||||||
@@ -58,7 +63,12 @@ mod map;
|
|||||||
mod modulation;
|
mod modulation;
|
||||||
mod pacman;
|
mod pacman;
|
||||||
|
|
||||||
|
/// The main entry point of the application.
|
||||||
|
///
|
||||||
|
/// This function initializes SDL, the window, the game state, and then enters
|
||||||
|
/// the main game loop.
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
// Attaches the console on Windows for debugging purposes.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
unsafe {
|
unsafe {
|
||||||
attach_console();
|
attach_console();
|
||||||
@@ -109,13 +119,16 @@ pub fn main() {
|
|||||||
game.draw();
|
game.draw();
|
||||||
game.tick();
|
game.tick();
|
||||||
|
|
||||||
|
// The target time for each frame of the game loop (60 FPS).
|
||||||
let loop_time = Duration::from_secs(1) / 60;
|
let loop_time = Duration::from_secs(1) / 60;
|
||||||
let mut tick_no = 0u32;
|
let mut tick_no = 0u32;
|
||||||
|
|
||||||
// The start of a period of time over which we average the frame time.
|
// The start of a period of time over which we average the frame time.
|
||||||
let mut last_averaging_time = Instant::now();
|
let mut last_averaging_time = Instant::now();
|
||||||
|
// The total time spent sleeping during the current averaging period.
|
||||||
let mut sleep_time = Duration::ZERO;
|
let mut sleep_time = Duration::ZERO;
|
||||||
let mut paused = false;
|
let mut paused = false;
|
||||||
|
// Whether the window is currently shown.
|
||||||
let mut shown = false;
|
let mut shown = false;
|
||||||
|
|
||||||
event!(
|
event!(
|
||||||
@@ -126,7 +139,9 @@ pub fn main() {
|
|||||||
let mut main_loop = || {
|
let mut main_loop = || {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
// TODO: Fix key repeat delay issues by using VecDeque for instant key repeat
|
// TODO: Fix key repeat delay issues by using a queue for keyboard events.
|
||||||
|
// This would allow for instant key repeat without being affected by the
|
||||||
|
// main loop's tick rate.
|
||||||
for event in event_pump.poll_iter() {
|
for event in event_pump.poll_iter() {
|
||||||
match event {
|
match event {
|
||||||
Event::Window { win_event, .. } => match win_event {
|
Event::Window { win_event, .. } => match win_event {
|
||||||
@@ -167,9 +182,9 @@ pub fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Proper pausing implementation that does not interfere with statistic gathering
|
// TODO: Implement a proper pausing mechanism that does not interfere with
|
||||||
|
// statistic gathering and other background tasks.
|
||||||
if !paused {
|
if !paused {
|
||||||
// game.audio_demo_tick();
|
|
||||||
game.tick();
|
game.tick();
|
||||||
game.draw();
|
game.draw();
|
||||||
}
|
}
|
||||||
@@ -197,6 +212,7 @@ pub fn main() {
|
|||||||
|
|
||||||
tick_no += 1;
|
tick_no += 1;
|
||||||
|
|
||||||
|
// Caclulate and display performance statistics every 60 seconds.
|
||||||
const PERIOD: u32 = 60 * 60;
|
const PERIOD: u32 = 60 * 60;
|
||||||
let tick_mod = tick_no % PERIOD;
|
let tick_mod = tick_no % PERIOD;
|
||||||
if tick_mod % PERIOD == 0 {
|
if tick_mod % PERIOD == 0 {
|
||||||
|
|||||||
38
src/map.rs
38
src/map.rs
@@ -1,12 +1,24 @@
|
|||||||
use crate::constants::MapTile;
|
//! This module defines the game map and provides functions for interacting with it.
|
||||||
use crate::constants::{BOARD_HEIGHT, BOARD_WIDTH, RAW_BOARD};
|
use crate::constants::{MapTile, BOARD_OFFSET, CELL_SIZE};
|
||||||
|
use crate::constants::{BOARD_HEIGHT, BOARD_WIDTH};
|
||||||
|
|
||||||
|
/// The game map.
|
||||||
|
///
|
||||||
|
/// The map is represented as a 2D array of `MapTile`s. It also stores a copy of
|
||||||
|
/// the original map, which can be used to reset the map to its initial state.
|
||||||
pub struct Map {
|
pub struct Map {
|
||||||
|
/// The current state of the map.
|
||||||
current: [[MapTile; BOARD_HEIGHT as usize]; BOARD_WIDTH as usize],
|
current: [[MapTile; BOARD_HEIGHT as usize]; BOARD_WIDTH as usize],
|
||||||
|
/// The default state of the map.
|
||||||
default: [[MapTile; BOARD_HEIGHT as usize]; BOARD_WIDTH as usize],
|
default: [[MapTile; BOARD_HEIGHT as usize]; BOARD_WIDTH as usize],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Map {
|
impl Map {
|
||||||
|
/// Creates a new `Map` instance from a raw board layout.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `raw_board` - A 2D array of characters representing the board layout.
|
||||||
pub fn new(raw_board: [&str; BOARD_HEIGHT as usize]) -> Map {
|
pub fn new(raw_board: [&str; BOARD_HEIGHT as usize]) -> Map {
|
||||||
let mut map = [[MapTile::Empty; BOARD_HEIGHT as usize]; BOARD_WIDTH as usize];
|
let mut map = [[MapTile::Empty; BOARD_HEIGHT as usize]; BOARD_WIDTH as usize];
|
||||||
|
|
||||||
@@ -46,6 +58,7 @@ impl Map {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Resets the map to its original state.
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
// Restore the map to its original state
|
// Restore the map to its original state
|
||||||
for x in 0..BOARD_WIDTH as usize {
|
for x in 0..BOARD_WIDTH as usize {
|
||||||
@@ -55,6 +68,11 @@ impl Map {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the tile at the given cell coordinates.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `cell` - The cell coordinates, in grid coordinates.
|
||||||
pub fn get_tile(&self, cell: (i32, i32)) -> Option<MapTile> {
|
pub fn get_tile(&self, cell: (i32, i32)) -> Option<MapTile> {
|
||||||
let x = cell.0 as usize;
|
let x = cell.0 as usize;
|
||||||
let y = cell.1 as usize;
|
let y = cell.1 as usize;
|
||||||
@@ -66,6 +84,12 @@ impl Map {
|
|||||||
Some(self.current[x][y])
|
Some(self.current[x][y])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the tile at the given cell coordinates.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `cell` - The cell coordinates, in grid coordinates.
|
||||||
|
/// * `tile` - The tile to set.
|
||||||
pub fn set_tile(&mut self, cell: (i32, i32), tile: MapTile) -> bool {
|
pub fn set_tile(&mut self, cell: (i32, i32), tile: MapTile) -> bool {
|
||||||
let x = cell.0 as usize;
|
let x = cell.0 as usize;
|
||||||
let y = cell.1 as usize;
|
let y = cell.1 as usize;
|
||||||
@@ -78,7 +102,15 @@ impl Map {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts cell coordinates to pixel coordinates.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `cell` - The cell coordinates, in grid coordinates.
|
||||||
pub fn cell_to_pixel(cell: (u32, u32)) -> (i32, i32) {
|
pub fn cell_to_pixel(cell: (u32, u32)) -> (i32, i32) {
|
||||||
((cell.0 as i32) * 24, ((cell.1 + 3) as i32) * 24)
|
(
|
||||||
|
(cell.0 * CELL_SIZE) as i32,
|
||||||
|
((cell.1 + BOARD_OFFSET.1) * CELL_SIZE) as i32,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//! This module provides a tick modulator, which can be used to slow down
|
||||||
|
//! operations by a percentage.
|
||||||
/// A tick modulator allows you to slow down operations by a percentage.
|
/// A tick modulator allows you to slow down operations by a percentage.
|
||||||
///
|
///
|
||||||
/// Unfortunately, switching to floating point numbers for entities can induce floating point errors, slow down calculations
|
/// Unfortunately, switching to floating point numbers for entities can induce floating point errors, slow down calculations
|
||||||
@@ -12,17 +14,25 @@
|
|||||||
///
|
///
|
||||||
/// For example, if we want to slow down the speed by 10%, we would need to skip every 10th tick.
|
/// For example, if we want to slow down the speed by 10%, we would need to skip every 10th tick.
|
||||||
pub trait TickModulator {
|
pub trait TickModulator {
|
||||||
|
/// Creates a new tick modulator.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `percent` - The percentage to slow down by, from 0.0 to 1.0.
|
||||||
fn new(percent: f32) -> Self;
|
fn new(percent: f32) -> Self;
|
||||||
|
/// Returns whether or not the operation should be performed on this tick.
|
||||||
fn next(&mut self) -> bool;
|
fn next(&mut self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A simple tick modulator that skips every Nth tick.
|
||||||
pub struct SimpleTickModulator {
|
pub struct SimpleTickModulator {
|
||||||
tick_count: u32,
|
tick_count: u32,
|
||||||
ticks_left: u32,
|
ticks_left: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add tests
|
// TODO: Add tests for the tick modulator to ensure that it is working correctly.
|
||||||
// TODO: Look into average precision, binary code modulation strategy
|
// TODO: Look into average precision and binary code modulation strategies to see
|
||||||
|
// if they would be a better fit for this use case.
|
||||||
impl TickModulator for SimpleTickModulator {
|
impl TickModulator for SimpleTickModulator {
|
||||||
fn new(percent: f32) -> Self {
|
fn new(percent: f32) -> Self {
|
||||||
let ticks_required: u32 = (1f32 / (1f32 - percent)).round() as u32;
|
let ticks_required: u32 = (1f32 / (1f32 - percent)).round() as u32;
|
||||||
@@ -34,15 +44,12 @@ impl TickModulator for SimpleTickModulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self) -> bool {
|
fn next(&mut self) -> bool {
|
||||||
self.ticks_left -= 1;
|
|
||||||
|
|
||||||
// Return whether or not we should skip this tick
|
|
||||||
if self.ticks_left == 0 {
|
if self.ticks_left == 0 {
|
||||||
// We've reached the tick to skip, reset the counter
|
|
||||||
self.ticks_left = self.tick_count;
|
self.ticks_left = self.tick_count;
|
||||||
false
|
return false;
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.ticks_left -= 1;
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user