mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-06 11:15:46 -06:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 03b2c5a659 | |||
| 64e226be70 | |||
| f998ddd344 | |||
| b2ad8e7afe | |||
| 799d5d85e8 | |||
| 9730d02da5 | |||
| f634beffee | |||
| d15dbe3982 | |||
| de5cddd9b6 | |||
| e3f37ab48e | |||
| 3dd8d5aff7 | |||
| ad084d1cd8 | |||
| 852e54f1bf | |||
| a62ddab9af | |||
| 50d0bc7d5f |
@@ -1,4 +1,8 @@
|
|||||||
[target.wasm32-unknown-emscripten]
|
[target.wasm32-unknown-emscripten]
|
||||||
rustflags = [
|
rustflags = [
|
||||||
"--use-preload-plugins --preload-file assets -s USE_SDL=2 -s USE_SDL_IMAGE=2 -s ASSERTIONS=1",
|
"-O", "-C", "link-args=-O2 --profiling",
|
||||||
]
|
#"-C", "link-args=-O3 --closure 1",
|
||||||
|
"-C", "link-args=-sASYNCIFY -sALLOW_MEMORY_GROWTH=1",
|
||||||
|
"-C", "link-args=-sUSE_SDL=2 -sUSE_SDL_IMAGE=2 -sSDL2_IMAGE_FORMATS=['png']",
|
||||||
|
"-C", "link-args=--preload-file assets/ -lidbfs.js",
|
||||||
|
]
|
||||||
|
|||||||
30
.github/workflows/deploy.yaml
vendored
30
.github/workflows/deploy.yaml
vendored
@@ -1,28 +1,38 @@
|
|||||||
name: Github Pages
|
name: Github Pages
|
||||||
|
|
||||||
on: [push]
|
on: [push]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-github-pages:
|
deploy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2 # repo checkout
|
- uses: actions/checkout@v2 # repo checkout
|
||||||
|
|
||||||
- uses: mymindstorm/setup-emsdk@v11 # setup emscripten toolchain
|
- uses: mymindstorm/setup-emsdk@v11 # setup emscripten toolchain
|
||||||
# with:
|
with:
|
||||||
# version: 3.1.35
|
version: 1.39.20
|
||||||
|
|
||||||
- uses: actions-rs/toolchain@v1 # get rust toolchain for wasm
|
- uses: actions-rs/toolchain@v1 # get rust toolchain for wasm
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
target: wasm32-unknown-emscripten
|
target: wasm32-unknown-emscripten
|
||||||
override: true
|
override: true
|
||||||
|
|
||||||
|
# TODO: Update to v2
|
||||||
- name: Rust Cache # cache the rust build artefacts
|
- name: Rust Cache # cache the rust build artefacts
|
||||||
uses: Swatinem/rust-cache@v1
|
uses: Swatinem/rust-cache@v1
|
||||||
|
|
||||||
- name: Build # build
|
- name: Build # build
|
||||||
run: ./build.sh
|
run: ./scripts/build-wasm.sh
|
||||||
- name: Deploy
|
|
||||||
uses: JamesIves/github-pages-deploy-action@v4
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-pages-artifact@v2
|
||||||
with:
|
with:
|
||||||
folder: dist
|
path: './dist/'
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
- name: Deploy
|
||||||
|
uses: actions/deploy-pages@v2
|
||||||
7
BUILD.md
Normal file
7
BUILD.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Building Pac-Man
|
||||||
|
|
||||||
|
## GitHub Actions Workflow
|
||||||
|
|
||||||
|
1. Build workflow produces executables & WASM files for all platforms
|
||||||
|
2. Uploaded as artifacts
|
||||||
|
3. Deployment workflow downloads artifacts and uploads to GitHub Pages
|
||||||
21
assets/index.html
Normal file
21
assets/index.html
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
<script>
|
||||||
|
var Module = {
|
||||||
|
'locateFile': function (path, prefix) {
|
||||||
|
if (path.endsWith(".data")) {
|
||||||
|
return prefix + "deps/" + path;
|
||||||
|
}
|
||||||
|
return prefix + path;
|
||||||
|
},
|
||||||
|
'canvas': document.getElementById('canvas'),
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script src="pacman.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
10
build.sh
10
build.sh
@@ -1,10 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -eux
|
|
||||||
|
|
||||||
cargo build --target=wasm32-unknown-emscripten --release
|
|
||||||
|
|
||||||
mkdir -p dist
|
|
||||||
|
|
||||||
cp target/wasm32-unknown-emscripten/release/Pac_Man.wasm dist
|
|
||||||
cp target/wasm32-unknown-emscripten/release/Pac-Man.js dist
|
|
||||||
cp index.html dist
|
|
||||||
21
index.html
21
index.html
@@ -1,21 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<canvas id="canvas"></canvas>
|
|
||||||
<script type="text/javascript">
|
|
||||||
let Module = {
|
|
||||||
canvas: (function () {
|
|
||||||
// this is how we provide a canvas to our sdl2
|
|
||||||
return document.getElementById("canvas");
|
|
||||||
})(),
|
|
||||||
preRun: [function () {
|
|
||||||
ENV.RUST_LOG = "info,wgpu=warn"
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script src="Pac-Man.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
16
scripts/build-wasm.sh
Executable file
16
scripts/build-wasm.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eux
|
||||||
|
|
||||||
|
|
||||||
|
echo "Building WASM with Emscripten"
|
||||||
|
cargo build --target=wasm32-unknown-emscripten --release
|
||||||
|
|
||||||
|
echo "Copying release files to dist/"
|
||||||
|
mkdir -p dist
|
||||||
|
mkdir -p dist/deps
|
||||||
|
|
||||||
|
output_folder="target/wasm32-unknown-emscripten/release"
|
||||||
|
cp $output_folder/pacman.wasm dist
|
||||||
|
cp $output_folder/pacman.js dist
|
||||||
|
cp $output_folder/deps/pacman.data dist/deps
|
||||||
|
cp assets/index.html dist
|
||||||
@@ -99,7 +99,7 @@ impl<'a> AnimatedTexture<'a> {
|
|||||||
self.tick();
|
self.tick();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Renders a specific frame of the animation. Defaults to the current frame.
|
// Renders a specific frame of the animation. Defaults to the current frame.
|
||||||
pub fn render_static(
|
pub fn render_static(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|||||||
@@ -48,4 +48,4 @@ pub const RAW_BOARD: [&str; BOARD_HEIGHT as usize] = [
|
|||||||
"#.##########.##.##########.#",
|
"#.##########.##.##########.#",
|
||||||
"#..........................#",
|
"#..........................#",
|
||||||
"############################",
|
"############################",
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -40,4 +40,4 @@ impl Direction {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,20 @@
|
|||||||
#[cfg(target_os = "emscripten")]
|
#[cfg(target_os = "emscripten")]
|
||||||
pub mod emscripten {
|
pub mod emscripten {
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::ptr::null_mut;
|
|
||||||
use std::os::raw::{c_int, c_void, c_char, c_float};
|
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::os::raw::{c_char, c_float, c_int, c_void};
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
type em_callback_func = unsafe extern "C" fn();
|
type em_callback_func = unsafe extern "C" fn();
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// void emscripten_set_main_loop(em_callback_func func, int fps, int simulate_infinite_loop)
|
// void emscripten_set_main_loop(em_callback_func func, int fps, int simulate_infinite_loop)
|
||||||
pub fn emscripten_set_main_loop(func: em_callback_func,
|
pub fn emscripten_set_main_loop(
|
||||||
fps: c_int,
|
func: em_callback_func,
|
||||||
simulate_infinite_loop: c_int);
|
fps: c_int,
|
||||||
|
simulate_infinite_loop: c_int,
|
||||||
|
);
|
||||||
|
|
||||||
pub fn emscripten_cancel_main_loop();
|
pub fn emscripten_cancel_main_loop();
|
||||||
pub fn emscripten_pause_main_loop();
|
pub fn emscripten_pause_main_loop();
|
||||||
@@ -23,17 +25,20 @@ pub mod emscripten {
|
|||||||
thread_local!(static MAIN_LOOP_CALLBACK: RefCell<*mut c_void> = RefCell::new(null_mut()));
|
thread_local!(static MAIN_LOOP_CALLBACK: RefCell<*mut c_void> = RefCell::new(null_mut()));
|
||||||
|
|
||||||
pub fn set_main_loop_callback<F>(callback: F)
|
pub fn set_main_loop_callback<F>(callback: F)
|
||||||
where F: FnMut()
|
where
|
||||||
|
F: FnMut(),
|
||||||
{
|
{
|
||||||
MAIN_LOOP_CALLBACK
|
MAIN_LOOP_CALLBACK.with(|log| {
|
||||||
.with(|log| { *log.borrow_mut() = &callback as *const _ as *mut c_void; });
|
*log.borrow_mut() = &callback as *const _ as *mut c_void;
|
||||||
|
});
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
emscripten_set_main_loop(wrapper::<F>, -1, 1);
|
emscripten_set_main_loop(wrapper::<F>, -1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe extern "C" fn wrapper<F>()
|
unsafe extern "C" fn wrapper<F>()
|
||||||
where F: FnMut()
|
where
|
||||||
|
F: FnMut(),
|
||||||
{
|
{
|
||||||
MAIN_LOOP_CALLBACK.with(|z| {
|
MAIN_LOOP_CALLBACK.with(|z| {
|
||||||
let closure = *z.borrow_mut() as *mut F;
|
let closure = *z.borrow_mut() as *mut F;
|
||||||
@@ -41,4 +46,4 @@ pub mod emscripten {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ pub trait Entity {
|
|||||||
// Returns the cell position of the entity (XY position within the grid)
|
// Returns the cell position of the entity (XY position within the grid)
|
||||||
fn cell_position(&self) -> (u32, u32);
|
fn cell_position(&self) -> (u32, u32);
|
||||||
fn internal_position(&self) -> (u32, u32);
|
fn internal_position(&self) -> (u32, u32);
|
||||||
// Tick the entity (move it, perform collision checks, etc)
|
// Tick the entity (move it, perform collision checks, etc)
|
||||||
fn tick(&mut self);
|
fn tick(&mut self);
|
||||||
}
|
}
|
||||||
|
|||||||
33
src/game.rs
33
src/game.rs
@@ -43,28 +43,14 @@ impl Game<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cell_to_pixel(cell: (u32, u32)) -> (i32, i32) {
|
|
||||||
((cell.0 as i32 * 24), ((cell.1) as i32 * 24))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn keyboard_event(&mut self, keycode: Keycode) {
|
pub fn keyboard_event(&mut self, keycode: Keycode) {
|
||||||
match keycode {
|
// Change direction
|
||||||
Keycode::D => {
|
let direction = Direction::from_keycode(keycode);
|
||||||
self.pacman.next_direction = Some(Direction::Right);
|
self.pacman.next_direction = direction;
|
||||||
}
|
|
||||||
Keycode::A => {
|
// Toggle debug mode
|
||||||
self.pacman.next_direction = Some(Direction::Left);
|
if keycode == Keycode::Space {
|
||||||
}
|
self.debug = !self.debug;
|
||||||
Keycode::W => {
|
|
||||||
self.pacman.next_direction = Some(Direction::Up);
|
|
||||||
}
|
|
||||||
Keycode::S => {
|
|
||||||
self.pacman.next_direction = Some(Direction::Down);
|
|
||||||
}
|
|
||||||
Keycode::Space => {
|
|
||||||
self.debug = !self.debug;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +74,10 @@ impl Game<'_> {
|
|||||||
if self.debug {
|
if self.debug {
|
||||||
for x in 0..BOARD_WIDTH {
|
for x in 0..BOARD_WIDTH {
|
||||||
for y in 0..BOARD_HEIGHT {
|
for y in 0..BOARD_HEIGHT {
|
||||||
let tile = self.map.get_tile((x as i32, y as i32)).unwrap_or(MapTile::Empty);
|
let tile = self
|
||||||
|
.map
|
||||||
|
.get_tile((x as i32, y as i32))
|
||||||
|
.unwrap_or(MapTile::Empty);
|
||||||
let mut color = None;
|
let mut color = None;
|
||||||
|
|
||||||
if (x, y) == self.pacman.cell_position() {
|
if (x, y) == self.pacman.cell_position() {
|
||||||
|
|||||||
62
src/main.rs
62
src/main.rs
@@ -1,24 +1,21 @@
|
|||||||
use crate::constants::{WINDOW_HEIGHT, WINDOW_WIDTH};
|
use crate::constants::{WINDOW_HEIGHT, WINDOW_WIDTH};
|
||||||
use crate::game::Game;
|
use crate::game::Game;
|
||||||
use tracing::{event};
|
use sdl2::event::Event;
|
||||||
use sdl2::event::{Event};
|
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
use spin_sleep::sleep;
|
use spin_sleep::sleep;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
use tracing::event;
|
||||||
use tracing_error::ErrorLayer;
|
use tracing_error::ErrorLayer;
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
|
|
||||||
#[cfg(target_os = "emscripten")]
|
|
||||||
pub mod emscripten;
|
|
||||||
|
|
||||||
mod animation;
|
mod animation;
|
||||||
mod constants;
|
mod constants;
|
||||||
mod direction;
|
mod direction;
|
||||||
mod entity;
|
mod entity;
|
||||||
mod game;
|
mod game;
|
||||||
mod pacman;
|
|
||||||
mod modulation;
|
|
||||||
mod map;
|
mod map;
|
||||||
|
mod modulation;
|
||||||
|
mod pacman;
|
||||||
|
|
||||||
#[cfg(target_os = "emscripten")]
|
#[cfg(target_os = "emscripten")]
|
||||||
mod emscripten;
|
mod emscripten;
|
||||||
@@ -68,11 +65,17 @@ pub fn main() {
|
|||||||
// 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();
|
||||||
let mut sleep_time = Duration::ZERO;
|
let mut sleep_time = Duration::ZERO;
|
||||||
|
let mut paused = false;
|
||||||
|
|
||||||
event!(tracing::Level::INFO, "Starting game loop ({:.3}ms)", loop_time.as_secs_f32() * 1000.0);
|
event!(
|
||||||
|
tracing::Level::INFO,
|
||||||
|
"Starting game loop ({:.3}ms)",
|
||||||
|
loop_time.as_secs_f32() * 1000.0
|
||||||
|
);
|
||||||
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
|
||||||
for event in event_pump.poll_iter() {
|
for event in event_pump.poll_iter() {
|
||||||
match event {
|
match event {
|
||||||
// Handle quitting keys or window close
|
// Handle quitting keys or window close
|
||||||
@@ -82,8 +85,19 @@ pub fn main() {
|
|||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
event!(tracing::Level::INFO, "Exit requested. Exiting...");
|
event!(tracing::Level::INFO, "Exit requested. Exiting...");
|
||||||
return false
|
return false;
|
||||||
},
|
}
|
||||||
|
Event::KeyDown {
|
||||||
|
keycode: Some(Keycode::P),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
paused = !paused;
|
||||||
|
event!(
|
||||||
|
tracing::Level::INFO,
|
||||||
|
"{}",
|
||||||
|
if paused { "Paused" } else { "Unpaused" }
|
||||||
|
);
|
||||||
|
}
|
||||||
Event::KeyDown { keycode, .. } => {
|
Event::KeyDown { keycode, .. } => {
|
||||||
game.keyboard_event(keycode.unwrap());
|
game.keyboard_event(keycode.unwrap());
|
||||||
}
|
}
|
||||||
@@ -91,8 +105,11 @@ pub fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
game.tick();
|
// TODO: Proper pausing implementation that does not interfere with statistic gathering
|
||||||
game.draw();
|
if !paused {
|
||||||
|
game.tick();
|
||||||
|
game.draw();
|
||||||
|
}
|
||||||
|
|
||||||
if start.elapsed() < loop_time {
|
if start.elapsed() < loop_time {
|
||||||
let time = loop_time - start.elapsed();
|
let time = loop_time - start.elapsed();
|
||||||
@@ -108,9 +125,11 @@ pub fn main() {
|
|||||||
|
|
||||||
tick_no += 1;
|
tick_no += 1;
|
||||||
|
|
||||||
if tick_no % (60 * 60) == 0 || tick_no == (60 * 2) {
|
const PERIOD: u32 = 60 * 60;
|
||||||
let average_fps = (tick_no % (60 * 60)) as f32 / last_averaging_time.elapsed().as_secs_f32();
|
let tick_mod = tick_no % PERIOD;
|
||||||
let average_sleep = sleep_time / tick_no;
|
if tick_mod % PERIOD == 0 {
|
||||||
|
let average_fps = PERIOD as f32 / last_averaging_time.elapsed().as_secs_f32();
|
||||||
|
let average_sleep = sleep_time / PERIOD;
|
||||||
let average_process = loop_time - average_sleep;
|
let average_process = loop_time - average_sleep;
|
||||||
|
|
||||||
event!(
|
event!(
|
||||||
@@ -118,7 +137,7 @@ pub fn main() {
|
|||||||
"Timing Averages [fps={}] [sleep={:?}] [process={:?}]",
|
"Timing Averages [fps={}] [sleep={:?}] [process={:?}]",
|
||||||
average_fps,
|
average_fps,
|
||||||
average_sleep,
|
average_sleep,
|
||||||
average_process
|
average_process
|
||||||
);
|
);
|
||||||
|
|
||||||
sleep_time = Duration::ZERO;
|
sleep_time = Duration::ZERO;
|
||||||
@@ -127,14 +146,7 @@ pub fn main() {
|
|||||||
|
|
||||||
true
|
true
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(target_os = "emscripten")]
|
|
||||||
use emscripten::emscripten;
|
|
||||||
|
|
||||||
#[cfg(target_os = "emscripten")]
|
|
||||||
emscripten::set_main_loop_callback(main_loop);
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "emscripten"))]
|
|
||||||
loop {
|
loop {
|
||||||
if !main_loop() {
|
if !main_loop() {
|
||||||
break;
|
break;
|
||||||
|
|||||||
12
src/map.rs
12
src/map.rs
@@ -2,7 +2,7 @@ use crate::constants::MapTile;
|
|||||||
use crate::constants::{BOARD_HEIGHT, BOARD_WIDTH};
|
use crate::constants::{BOARD_HEIGHT, BOARD_WIDTH};
|
||||||
|
|
||||||
pub struct Map {
|
pub struct Map {
|
||||||
inner: [[MapTile; BOARD_HEIGHT as usize]; BOARD_WIDTH as usize]
|
inner: [[MapTile; BOARD_HEIGHT as usize]; BOARD_WIDTH as usize],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Map {
|
impl Map {
|
||||||
@@ -16,7 +16,7 @@ impl Map {
|
|||||||
if x >= line.len() {
|
if x >= line.len() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let i = (y * (BOARD_WIDTH as usize) + x) as usize;
|
let i = (y * (BOARD_WIDTH as usize) + x) as usize;
|
||||||
let character = line
|
let character = line
|
||||||
.chars()
|
.chars()
|
||||||
@@ -30,7 +30,7 @@ impl Map {
|
|||||||
' ' => MapTile::Empty,
|
' ' => MapTile::Empty,
|
||||||
c @ '0' | c @ '1' | c @ '2' | c @ '3' | c @ '4' => {
|
c @ '0' | c @ '1' | c @ '2' | c @ '3' | c @ '4' => {
|
||||||
MapTile::StartingPosition(c.to_digit(10).unwrap() as u8)
|
MapTile::StartingPosition(c.to_digit(10).unwrap() as u8)
|
||||||
},
|
}
|
||||||
'=' => MapTile::Empty,
|
'=' => MapTile::Empty,
|
||||||
_ => panic!("Unknown character in board: {}", character),
|
_ => panic!("Unknown character in board: {}", character),
|
||||||
};
|
};
|
||||||
@@ -39,9 +39,7 @@ impl Map {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map {
|
Map { inner: inner }
|
||||||
inner: inner
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tile(&self, cell: (i32, i32)) -> Option<MapTile> {
|
pub fn get_tile(&self, cell: (i32, i32)) -> Option<MapTile> {
|
||||||
@@ -58,4 +56,4 @@ impl Map {
|
|||||||
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 as i32) * 24, ((cell.1 + 3) as i32) * 24)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
/// 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
|
||||||
/// and make the game less deterministic. This is why we use a speed modulator instead.
|
/// and make the game less deterministic. This is why we use a speed modulator instead.
|
||||||
/// Additionally, with small integers, lowering the speed by a percentage is not possible. For example, if we have a speed of 2,
|
/// Additionally, with small integers, lowering the speed by a percentage is not possible. For example, if we have a speed of 2,
|
||||||
/// and we want to slow it down by 10%, we would need to slow it down by 0.2. However, since we are using integers, we can't.
|
/// and we want to slow it down by 10%, we would need to slow it down by 0.2. However, since we are using integers, we can't.
|
||||||
/// The only amount you can slow it down by is 1, which is 50% of the speed.
|
/// The only amount you can slow it down by is 1, which is 50% of the speed.
|
||||||
///
|
///
|
||||||
/// The basic principle of the Speed Modulator is to instead 'skip' movement ticks every now and then.
|
/// The basic principle of the Speed Modulator is to instead 'skip' movement ticks every now and then.
|
||||||
/// At 60 ticks per second, skips could happen several times per second, or once every few seconds.
|
/// At 60 ticks per second, skips could happen several times per second, or once every few seconds.
|
||||||
/// Whatever it be, as long as the tick rate is high enough, the human eye will not be able to tell the difference.
|
/// Whatever it be, as long as the tick rate is high enough, the human eye will not be able to tell the difference.
|
||||||
///
|
///
|
||||||
/// 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 {
|
||||||
fn new(percent: f32) -> Self;
|
fn new(percent: f32) -> Self;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use tracing::event;
|
|||||||
use crate::{
|
use crate::{
|
||||||
animation::AnimatedTexture,
|
animation::AnimatedTexture,
|
||||||
constants::MapTile,
|
constants::MapTile,
|
||||||
constants::{CELL_SIZE, BOARD_OFFSET},
|
constants::{BOARD_OFFSET, CELL_SIZE},
|
||||||
direction::Direction,
|
direction::Direction,
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
map::Map,
|
map::Map,
|
||||||
@@ -48,8 +48,8 @@ impl Pacman<'_> {
|
|||||||
self.sprite
|
self.sprite
|
||||||
.render_until(canvas, self.position, self.direction, 2);
|
.render_until(canvas, self.position, self.direction, 2);
|
||||||
} else {
|
} else {
|
||||||
self.sprite.render(canvas, self.position, self.direction);
|
self.sprite.render(canvas, self.position, self.direction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_cell(&self, direction: Option<Direction>) -> (i32, i32) {
|
pub fn next_cell(&self, direction: Option<Direction>) -> (i32, i32) {
|
||||||
@@ -57,6 +57,26 @@ impl Pacman<'_> {
|
|||||||
let cell = self.cell_position();
|
let cell = self.cell_position();
|
||||||
(cell.0 as i32 + x, cell.1 as i32 + y)
|
(cell.0 as i32 + x, cell.1 as i32 + y)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_requested_direction(&mut self) {
|
||||||
|
if self.next_direction.is_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if self.next_direction.unwrap() == self.direction {
|
||||||
|
self.next_direction = None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let proposed_next_cell = self.next_cell(self.next_direction);
|
||||||
|
let proposed_next_tile = self
|
||||||
|
.map
|
||||||
|
.get_tile(proposed_next_cell)
|
||||||
|
.unwrap_or(MapTile::Empty);
|
||||||
|
if proposed_next_tile != MapTile::Wall {
|
||||||
|
self.direction = self.next_direction.unwrap();
|
||||||
|
self.next_direction = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Entity for Pacman<'_> {
|
impl Entity for Pacman<'_> {
|
||||||
@@ -72,7 +92,10 @@ impl Entity for Pacman<'_> {
|
|||||||
|
|
||||||
fn cell_position(&self) -> (u32, u32) {
|
fn cell_position(&self) -> (u32, u32) {
|
||||||
let (x, y) = self.position;
|
let (x, y) = self.position;
|
||||||
((x as u32 / CELL_SIZE) - BOARD_OFFSET.0, (y as u32 / CELL_SIZE) - BOARD_OFFSET.1)
|
(
|
||||||
|
(x as u32 / CELL_SIZE) - BOARD_OFFSET.0,
|
||||||
|
(y as u32 / CELL_SIZE) - BOARD_OFFSET.1,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn internal_position(&self) -> (u32, u32) {
|
fn internal_position(&self) -> (u32, u32) {
|
||||||
@@ -82,11 +105,9 @@ impl Entity for Pacman<'_> {
|
|||||||
|
|
||||||
fn tick(&mut self) {
|
fn tick(&mut self) {
|
||||||
let can_change = self.internal_position() == (0, 0);
|
let can_change = self.internal_position() == (0, 0);
|
||||||
|
|
||||||
if can_change {
|
if can_change {
|
||||||
if let Some(direction) = self.next_direction {
|
self.handle_requested_direction();
|
||||||
self.direction = direction;
|
|
||||||
self.next_direction = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let next = self.next_cell(None);
|
let next = self.next_cell(None);
|
||||||
let next_tile = self.map.get_tile(next).unwrap_or(MapTile::Empty);
|
let next_tile = self.map.get_tile(next).unwrap_or(MapTile::Empty);
|
||||||
|
|||||||
Reference in New Issue
Block a user