mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-07 16:07:54 -06:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c489f32908 | |||
| b91f70cf2f | |||
| 24a207be01 | |||
| 44e31d9b21 | |||
|
|
b67234765a | ||
|
|
d07498c30e |
18
.github/workflows/build.yaml
vendored
18
.github/workflows/build.yaml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Rust Toolchain
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
@@ -64,15 +64,16 @@ jobs:
|
||||
run: cargo build --release
|
||||
|
||||
- name: Acquire Package Version
|
||||
shell: bash
|
||||
id: get_version
|
||||
shell: bash # required to prevent Windows runners from failing
|
||||
run: |
|
||||
PACKAGE_VERSION=$(cargo metadata --format-version 1 --no-deps | jq '.packages[0].version' -r)
|
||||
echo "PACKAGE_VERSION=${PACKAGE_VERSION}" >> $GITHUB_ENV
|
||||
set -euo pipefail # exit on error
|
||||
echo "version=$(cargo metadata --format-version 1 --no-deps | jq '.packages[0].version' -r)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "pacman-${{ env.PACKAGE_VERSION }}-${{ matrix.target }}"
|
||||
name: "pacman-${{ steps.get_version.outputs.version }}-${{ matrix.target }}"
|
||||
path: ./target/release/${{ matrix.artifact_name }}
|
||||
retention-days: 7
|
||||
if-no-files-found: error
|
||||
@@ -83,10 +84,13 @@ jobs:
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
# concurrency group is used to prevent multiple page deployments from being attempted at the same time
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-wasm
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Setup Emscripten SDK
|
||||
uses: pyodide/setup-emsdk@v15
|
||||
@@ -98,7 +102,7 @@ jobs:
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
target: wasm32-unknown-emscripten
|
||||
toolchain: 1.86.0 # we are unfortunately pinned to 1.86.0 for some reason, bulk-memory-opt related issues
|
||||
toolchain: 1.86.0
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
|
||||
2
.github/workflows/coverage.yaml
vendored
2
.github/workflows/coverage.yaml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
|
||||
2
.github/workflows/tests.yaml
vendored
2
.github/workflows/tests.yaml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@ emsdk/
|
||||
rust-sdl2-emscripten/
|
||||
assets/site/build.css
|
||||
tailwindcss-*
|
||||
lcov.info
|
||||
|
||||
42
Cargo.lock
generated
42
Cargo.lock
generated
@@ -13,9 +13,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.98"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
@@ -79,9 +79,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.30.4"
|
||||
version = "0.30.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50a99dbe56b72736564cfa4b85bf9a33079f16ae8b74983ab06af3b1a3696b11"
|
||||
checksum = "f2d1aab06663bdce00d6ca5e5ed586ec8d18033a771906c993a1e3755b368d85"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
@@ -128,9 +128,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
version = "0.2.175"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
@@ -202,7 +202,7 @@ dependencies = [
|
||||
"spin_sleep",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"tracing-error",
|
||||
"tracing-subscriber",
|
||||
@@ -220,7 +220,7 @@ dependencies = [
|
||||
"integer-sqrt",
|
||||
"num-traits",
|
||||
"rustc-hash",
|
||||
"thiserror 2.0.12",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -380,9 +380,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.141"
|
||||
version = "1.0.142"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
|
||||
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@@ -443,33 +443,13 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||
dependencies = [
|
||||
"thiserror-impl 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -15,11 +15,11 @@ spin_sleep = "1.3.2"
|
||||
rand = { version = "0.9.2", default-features = false, features = ["small_rng", "os_rng"] }
|
||||
pathfinding = "4.14"
|
||||
once_cell = "1.21.3"
|
||||
thiserror = "1.0"
|
||||
thiserror = "2.0"
|
||||
anyhow = "1.0"
|
||||
glam = { version = "0.30.4", features = [] }
|
||||
glam = { version = "0.30.5", features = [] }
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.141"
|
||||
serde_json = "1.0.142"
|
||||
smallvec = "1.15.1"
|
||||
strum = "0.27.2"
|
||||
strum_macros = "0.27.2"
|
||||
@@ -56,4 +56,4 @@ x86_64-apple-darwin = { triplet = "x64-osx" }
|
||||
aarch64-apple-darwin = { triplet = "arm64-osx" }
|
||||
|
||||
[target.'cfg(target_os = "emscripten")'.dependencies]
|
||||
libc = "0.2.16"
|
||||
libc = "0.2.175"
|
||||
|
||||
@@ -72,6 +72,8 @@ I wanted to hit a log of goals and features, making it a 'perfect' project that
|
||||
|
||||
Since this project is still in progress, I'm only going to cover non-obvious build details. By reading the code, build scripts, and copying the online build workflows, you should be able to replicate the build process.
|
||||
|
||||
- We use rustc 1.86.0 for the build, due to bulk-memory-opt related issues on wasm32-unknown-emscripten.
|
||||
- Technically, we could probably use stable or even nightly on desktop targets, but using different versions for different targets is a pain, mainly because of clippy warnings changing between versions.
|
||||
- Install `cargo-vcpkg` with `cargo install cargo-vcpkg`, then run `cargo vcpkg build` to build the requisite dependencies via vcpkg.
|
||||
- For the WASM build, you need to have the Emscripten SDK cloned; you can do so with `git clone https://github.com/emscripten-core/emsdk.git`
|
||||
- The first time you clone, you'll need to install the appropriate SDK version with `./emsdk install 3.1.43` and then activate it with `./emsdk activate 3.1.43`. On Windows, use `./emsdk/emsdk.ps1` instead.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
[toolchain]
|
||||
# we are unfortunately pinned to 1.86.0 for some reason, bulk-memory-opt related issues on wasm32-unknown-emscripten
|
||||
channel = "1.86.0"
|
||||
components = ["rustfmt", "llvm-tools-preview", "clippy"]
|
||||
|
||||
36
src/app.rs
36
src/app.rs
@@ -4,8 +4,9 @@ use glam::Vec2;
|
||||
use sdl2::event::{Event, WindowEvent};
|
||||
use sdl2::keyboard::Keycode;
|
||||
use sdl2::render::{Canvas, ScaleMode, Texture, TextureCreator};
|
||||
use sdl2::ttf::Sdl2TtfContext;
|
||||
use sdl2::video::{Window, WindowContext};
|
||||
use sdl2::EventPump;
|
||||
use sdl2::{AudioSubsystem, EventPump, Sdl, VideoSubsystem};
|
||||
use tracing::{error, event};
|
||||
|
||||
use crate::error::{GameError, GameResult};
|
||||
@@ -14,26 +15,31 @@ use crate::constants::{CANVAS_SIZE, LOOP_TIME, SCALE};
|
||||
use crate::game::Game;
|
||||
use crate::platform::get_platform;
|
||||
|
||||
pub struct App<'a> {
|
||||
pub struct App {
|
||||
game: Game,
|
||||
canvas: Canvas<Window>,
|
||||
event_pump: EventPump,
|
||||
backbuffer: Texture<'a>,
|
||||
event_pump: &'static mut EventPump,
|
||||
backbuffer: Texture<'static>,
|
||||
paused: bool,
|
||||
last_tick: Instant,
|
||||
cursor_pos: Vec2,
|
||||
}
|
||||
|
||||
impl App<'_> {
|
||||
impl App {
|
||||
pub fn new() -> GameResult<Self> {
|
||||
let sdl_context: &'static Sdl = Box::leak(Box::new(sdl2::init().map_err(|e| GameError::Sdl(e.to_string()))?));
|
||||
let video_subsystem: &'static VideoSubsystem =
|
||||
Box::leak(Box::new(sdl_context.video().map_err(|e| GameError::Sdl(e.to_string()))?));
|
||||
let audio_subsystem: &'static AudioSubsystem =
|
||||
Box::leak(Box::new(sdl_context.audio().map_err(|e| GameError::Sdl(e.to_string()))?));
|
||||
let ttf_context: &'static Sdl2TtfContext =
|
||||
Box::leak(Box::new(sdl2::ttf::init().map_err(|e| GameError::Sdl(e.to_string()))?));
|
||||
let event_pump: &'static mut EventPump =
|
||||
Box::leak(Box::new(sdl_context.event_pump().map_err(|e| GameError::Sdl(e.to_string()))?));
|
||||
|
||||
// Initialize platform-specific console
|
||||
get_platform().init_console()?;
|
||||
|
||||
let sdl_context = sdl2::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 ttf_context = sdl2::ttf::init().map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||
|
||||
let window = video_subsystem
|
||||
.window(
|
||||
"Pac-Man",
|
||||
@@ -50,18 +56,16 @@ impl App<'_> {
|
||||
.set_logical_size(CANVAS_SIZE.x, CANVAS_SIZE.y)
|
||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||
|
||||
let texture_creator_static: &'static TextureCreator<WindowContext> = Box::leak(Box::new(canvas.texture_creator()));
|
||||
let texture_creator: &'static TextureCreator<WindowContext> = Box::leak(Box::new(canvas.texture_creator()));
|
||||
|
||||
let mut game = Game::new(texture_creator_static, &ttf_context, &audio_subsystem)?;
|
||||
game.audio.set_mute(cfg!(debug_assertions));
|
||||
let mut game = Game::new(texture_creator, ttf_context, audio_subsystem)?;
|
||||
// game.audio.set_mute(cfg!(debug_assertions));
|
||||
|
||||
let mut backbuffer = texture_creator_static
|
||||
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);
|
||||
|
||||
let event_pump = sdl_context.event_pump().map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||
|
||||
// Initial draw
|
||||
game.draw(&mut canvas, &mut backbuffer)
|
||||
.map_err(|e| GameError::Sdl(e.to_string()))?;
|
||||
|
||||
24
src/game.rs
24
src/game.rs
@@ -25,7 +25,7 @@ use crate::{
|
||||
},
|
||||
map::Map,
|
||||
texture::{
|
||||
sprite::{self, AtlasMapper, AtlasTile, SpriteAtlas},
|
||||
sprite::{AtlasMapper, AtlasTile, SpriteAtlas},
|
||||
text::TextTexture,
|
||||
},
|
||||
};
|
||||
@@ -59,7 +59,7 @@ pub struct Game {
|
||||
|
||||
impl Game {
|
||||
pub fn new(
|
||||
texture_creator: &TextureCreator<WindowContext>,
|
||||
texture_creator: &'static TextureCreator<WindowContext>,
|
||||
_ttf_context: &sdl2::ttf::Sdl2TtfContext,
|
||||
_audio_subsystem: &sdl2::AudioSubsystem,
|
||||
) -> GameResult<Game> {
|
||||
@@ -74,19 +74,16 @@ impl Game {
|
||||
.ok_or_else(|| GameError::NotFound("Pac-Man starting position not found in graph".to_string()))?;
|
||||
|
||||
let atlas_bytes = get_asset_bytes(Asset::Atlas)?;
|
||||
let atlas_texture = unsafe {
|
||||
let texture = texture_creator.load_texture_bytes(&atlas_bytes).map_err(|e| {
|
||||
if e.to_string().contains("format") || e.to_string().contains("unsupported") {
|
||||
GameError::Texture(TextureError::InvalidFormat(format!("Unsupported texture format: {e}")))
|
||||
} else {
|
||||
GameError::Texture(TextureError::LoadFailed(e.to_string()))
|
||||
}
|
||||
})?;
|
||||
sprite::texture_to_static(texture)
|
||||
};
|
||||
let atlas_texture = Box::leak(Box::new(texture_creator.load_texture_bytes(&atlas_bytes).map_err(|e| {
|
||||
if e.to_string().contains("format") || e.to_string().contains("unsupported") {
|
||||
GameError::Texture(TextureError::InvalidFormat(format!("Unsupported texture format: {e}")))
|
||||
} else {
|
||||
GameError::Texture(TextureError::LoadFailed(e.to_string()))
|
||||
}
|
||||
})?));
|
||||
let atlas_json = get_asset_bytes(Asset::AtlasJson)?;
|
||||
let atlas_mapper: AtlasMapper = serde_json::from_slice(&atlas_json)?;
|
||||
let atlas = SpriteAtlas::new(atlas_texture, atlas_mapper);
|
||||
let atlas = SpriteAtlas::new(unsafe { std::mem::transmute_copy(atlas_texture) }, atlas_mapper);
|
||||
|
||||
let mut map_texture = SpriteAtlas::get_tile(&atlas, "maze/full.png")
|
||||
.ok_or_else(|| GameError::Texture(TextureError::AtlasTileNotFound("maze/full.png".to_string())))?;
|
||||
@@ -255,6 +252,7 @@ impl Game {
|
||||
if !item.is_collected() {
|
||||
item.collect();
|
||||
self.score += item.get_score();
|
||||
self.audio.eat();
|
||||
|
||||
// Handle energizer effects
|
||||
if matches!(item.item_type, crate::entity::item::ItemType::Energizer) {
|
||||
|
||||
@@ -138,20 +138,3 @@ impl SpriteAtlas {
|
||||
self.default_color
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a `Texture` to a `Texture<'static>` using transmute.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because it uses `std::mem::transmute` to change the lifetime
|
||||
/// of the texture from the original lifetime to `'static`. The caller must ensure that:
|
||||
///
|
||||
/// - The original `Texture` will live for the entire duration of the program
|
||||
/// - No references to the original texture exist that could become invalid
|
||||
/// - The texture is not dropped while still being used as a `'static` reference
|
||||
///
|
||||
/// This is typically used when you have a texture that you know will live for the entire
|
||||
/// program duration and need to store it in a structure that requires a `'static` lifetime.
|
||||
pub unsafe fn texture_to_static(texture: Texture) -> Texture<'static> {
|
||||
std::mem::transmute(texture)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ fn test_fruit_kind_increasing_score() {
|
||||
.collect::<Vec<_>>();
|
||||
kinds.sort_unstable_by_key(|(index, _)| *index);
|
||||
|
||||
assert_eq!(kinds.len(), FruitKind::COUNT as usize);
|
||||
assert_eq!(kinds.len(), FruitKind::COUNT);
|
||||
|
||||
// Check that the score increases as expected
|
||||
for window in kinds.windows(2) {
|
||||
|
||||
Reference in New Issue
Block a user