mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-06 09:15:46 -06:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 86ffc931e8 | |||
| d72f47d66c | |||
| 7a6182cb85 |
@@ -11,9 +11,16 @@ pub const BOARD_CELL_SIZE: UVec2 = UVec2::new(28, 31);
|
||||
pub const SCALE: f32 = 2.6;
|
||||
|
||||
/// The offset of the game board from the top-left corner of the window, in cells.
|
||||
pub const BOARD_OFFSET: UVec2 = UVec2::new(0, 0);
|
||||
pub const BOARD_CELL_OFFSET: UVec2 = UVec2::new(0, 3);
|
||||
/// The offset of the game board from the top-left corner of the window, in pixels.
|
||||
pub const BOARD_PIXEL_OFFSET: UVec2 = UVec2::new(BOARD_CELL_OFFSET.x * CELL_SIZE, BOARD_CELL_OFFSET.y * CELL_SIZE);
|
||||
/// The size of the game board, in pixels.
|
||||
pub const BOARD_PIXEL_SIZE: UVec2 = UVec2::new(BOARD_CELL_SIZE.x * CELL_SIZE, BOARD_CELL_SIZE.y * CELL_SIZE);
|
||||
/// The size of the canvas, in pixels.
|
||||
pub const CANVAS_SIZE: UVec2 = UVec2::new(
|
||||
(BOARD_CELL_SIZE.x + BOARD_CELL_OFFSET.x) * CELL_SIZE,
|
||||
(BOARD_CELL_SIZE.y + BOARD_CELL_OFFSET.y) * CELL_SIZE,
|
||||
);
|
||||
|
||||
/// An enum representing the different types of tiles on the map.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
|
||||
@@ -63,13 +63,13 @@ impl Renderable for Edible {
|
||||
let pos = self.base.pixel_position;
|
||||
let dest = match &mut self.sprite {
|
||||
EdibleSprite::Pellet(sprite) => {
|
||||
let mut tile = sprite.current_tile();
|
||||
let tile = sprite.current_tile();
|
||||
let x = pos.x + ((crate::constants::CELL_SIZE as i32 - tile.size.x as i32) / 2);
|
||||
let y = pos.y + ((crate::constants::CELL_SIZE as i32 - tile.size.y as i32) / 2);
|
||||
sdl2::rect::Rect::new(x, y, tile.size.x as u32, tile.size.y as u32)
|
||||
}
|
||||
EdibleSprite::PowerPellet(sprite) => {
|
||||
let mut tile = sprite.animation.current_tile();
|
||||
let tile = sprite.animation.current_tile();
|
||||
let x = pos.x + ((crate::constants::CELL_SIZE as i32 - tile.size.x as i32) / 2);
|
||||
let y = pos.y + ((crate::constants::CELL_SIZE as i32 - tile.size.y as i32) / 2);
|
||||
sdl2::rect::Rect::new(x, y, tile.size.x as u32, tile.size.y as u32)
|
||||
|
||||
@@ -6,7 +6,7 @@ pub mod pacman;
|
||||
pub mod speed;
|
||||
|
||||
use crate::{
|
||||
constants::{MapTile, BOARD_CELL_SIZE, BOARD_OFFSET, CELL_SIZE},
|
||||
constants::{MapTile, BOARD_CELL_OFFSET, BOARD_CELL_SIZE, CELL_SIZE},
|
||||
entity::{direction::Direction, speed::SimpleTickModulator},
|
||||
map::Map,
|
||||
};
|
||||
@@ -145,8 +145,8 @@ impl Moving for MovableEntity {
|
||||
}
|
||||
fn update_cell_position(&mut self) {
|
||||
self.base.cell_position = UVec2::new(
|
||||
(self.base.pixel_position.x as u32 / CELL_SIZE) - BOARD_OFFSET.x,
|
||||
(self.base.pixel_position.y as u32 / CELL_SIZE) - BOARD_OFFSET.y,
|
||||
(self.base.pixel_position.x as u32 / CELL_SIZE) - BOARD_CELL_OFFSET.x,
|
||||
(self.base.pixel_position.y as u32 / CELL_SIZE) - BOARD_CELL_OFFSET.y,
|
||||
);
|
||||
}
|
||||
fn next_cell(&self, direction: Option<Direction>) -> IVec2 {
|
||||
@@ -162,13 +162,18 @@ impl Moving for MovableEntity {
|
||||
let at_left_tunnel = x == 0;
|
||||
let at_right_tunnel = x == BOARD_CELL_SIZE.x - 1;
|
||||
|
||||
// Reset tunnel state if we're not at a tunnel position
|
||||
if !at_left_tunnel && !at_right_tunnel {
|
||||
self.in_tunnel = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we're already in a tunnel, stay in tunnel state
|
||||
if self.in_tunnel {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Enter the tunnel and teleport to the other side
|
||||
let new_x = if at_left_tunnel { BOARD_CELL_SIZE.x - 2 } else { 1 };
|
||||
self.base.cell_position.x = new_x;
|
||||
self.base.pixel_position = Map::cell_to_pixel(self.base.cell_position);
|
||||
|
||||
24
src/game.rs
24
src/game.rs
@@ -4,12 +4,13 @@ use std::ops::Not;
|
||||
use std::rc::Rc;
|
||||
|
||||
use anyhow::Result;
|
||||
use glam::{IVec2, UVec2};
|
||||
use glam::UVec2;
|
||||
use rand::rngs::SmallRng;
|
||||
use rand::seq::IteratorRandom;
|
||||
use rand::SeedableRng;
|
||||
use sdl2::image::LoadTexture;
|
||||
use sdl2::keyboard::Keycode;
|
||||
|
||||
use sdl2::render::{Texture, TextureCreator};
|
||||
use sdl2::video::WindowContext;
|
||||
use sdl2::{pixels::Color, render::Canvas, video::Window};
|
||||
@@ -80,7 +81,9 @@ impl Game {
|
||||
Rc::clone(&map),
|
||||
)));
|
||||
let blinky = Blinky::new(UVec2::new(13, 11), Rc::clone(&atlas), Rc::clone(&map), Rc::clone(&pacman));
|
||||
let map_texture = get_atlas_tile(&atlas, "maze/full.png");
|
||||
let mut map_texture = get_atlas_tile(&atlas, "maze/full.png");
|
||||
map_texture.color = Some(Color::RGB(0x20, 0x20, 0xf9));
|
||||
|
||||
let edibles = reconstruct_edibles(
|
||||
Rc::clone(&map),
|
||||
AnimatedTexture::new(vec![get_atlas_tile(&atlas, "maze/pellet.png")], 0),
|
||||
@@ -268,7 +271,6 @@ impl Game {
|
||||
}
|
||||
let _ = this.pacman.borrow_mut().render(texture_canvas);
|
||||
let _ = this.blinky.render(texture_canvas);
|
||||
this.render_ui_on(texture_canvas);
|
||||
match this.debug_mode {
|
||||
DebugMode::Grid => {
|
||||
DebugRenderer::draw_debug_grid(
|
||||
@@ -290,10 +292,11 @@ impl Game {
|
||||
})
|
||||
.map_err(|e| anyhow::anyhow!(format!("Failed to render to backbuffer: {e}")))
|
||||
}
|
||||
pub fn present_backbuffer(&self, canvas: &mut Canvas<Window>, backbuffer: &Texture) -> Result<()> {
|
||||
pub fn present_backbuffer(&mut self, canvas: &mut Canvas<Window>, backbuffer: &Texture) -> Result<()> {
|
||||
canvas.set_draw_color(Color::BLACK);
|
||||
canvas.clear();
|
||||
canvas.copy(backbuffer, None, None).map_err(anyhow::Error::msg)?;
|
||||
self.render_ui_on(canvas);
|
||||
canvas.present();
|
||||
Ok(())
|
||||
}
|
||||
@@ -301,22 +304,21 @@ impl Game {
|
||||
fn render_ui_on<C: sdl2::render::RenderTarget>(&mut self, canvas: &mut sdl2::render::Canvas<C>) {
|
||||
let lives = 3;
|
||||
let score_text = format!("{:02}", self.score);
|
||||
let x_offset = 12;
|
||||
let x_offset = 4;
|
||||
let y_offset = 2;
|
||||
let lives_offset = 3;
|
||||
let score_offset = 7 - (score_text.len() as i32);
|
||||
let gap_offset = 6;
|
||||
self.text_texture.set_scale(2.0);
|
||||
self.text_texture.render(
|
||||
self.text_texture.set_scale(1.0);
|
||||
let _ = self.text_texture.render(
|
||||
canvas,
|
||||
&format!("{lives}UP HIGH SCORE "),
|
||||
UVec2::new(24 * lives_offset as u32 + x_offset, y_offset),
|
||||
UVec2::new(8 * lives_offset as u32 + x_offset, y_offset),
|
||||
Color::WHITE,
|
||||
);
|
||||
self.text_texture.render(
|
||||
let _ = self.text_texture.render(
|
||||
canvas,
|
||||
&score_text,
|
||||
UVec2::new(24 * score_offset as u32 + x_offset, 24 + y_offset + gap_offset),
|
||||
UVec2::new(8 * score_offset as u32 + x_offset, 8 + y_offset),
|
||||
Color::WHITE,
|
||||
);
|
||||
|
||||
|
||||
12
src/main.rs
12
src/main.rs
@@ -1,6 +1,6 @@
|
||||
#![windows_subsystem = "windows"]
|
||||
|
||||
use crate::constants::{BOARD_PIXEL_SIZE, SCALE};
|
||||
use crate::constants::{CANVAS_SIZE, SCALE};
|
||||
use crate::game::Game;
|
||||
use sdl2::event::{Event, WindowEvent};
|
||||
use sdl2::keyboard::Keycode;
|
||||
@@ -105,8 +105,8 @@ pub fn main() {
|
||||
let window = video_subsystem
|
||||
.window(
|
||||
"Pac-Man",
|
||||
(BOARD_PIXEL_SIZE.x as f32 * SCALE).round() as u32,
|
||||
(BOARD_PIXEL_SIZE.y as f32 * SCALE).round() as u32,
|
||||
(CANVAS_SIZE.x as f32 * SCALE).round() as u32,
|
||||
(CANVAS_SIZE.y as f32 * SCALE).round() as u32,
|
||||
)
|
||||
.resizable()
|
||||
.position_centered()
|
||||
@@ -116,7 +116,7 @@ pub fn main() {
|
||||
let mut canvas = window.into_canvas().build().expect("Could not build canvas");
|
||||
|
||||
canvas
|
||||
.set_logical_size(BOARD_PIXEL_SIZE.x, BOARD_PIXEL_SIZE.y)
|
||||
.set_logical_size(CANVAS_SIZE.x, CANVAS_SIZE.y)
|
||||
.expect("Could not set logical size");
|
||||
|
||||
let texture_creator = canvas.texture_creator();
|
||||
@@ -127,7 +127,7 @@ pub fn main() {
|
||||
|
||||
// Create a backbuffer texture for drawing
|
||||
let mut backbuffer = texture_creator_static
|
||||
.create_texture_target(None, BOARD_PIXEL_SIZE.x, BOARD_PIXEL_SIZE.y)
|
||||
.create_texture_target(None, CANVAS_SIZE.x, CANVAS_SIZE.y)
|
||||
.expect("Could not create backbuffer texture");
|
||||
|
||||
let mut event_pump = sdl_context.event_pump().expect("Could not get SDL EventPump");
|
||||
@@ -154,7 +154,7 @@ pub fn main() {
|
||||
let mut last_frame_time = Instant::now();
|
||||
|
||||
event!(tracing::Level::INFO, "Starting game loop ({:?})", loop_time);
|
||||
let mut main_loop = || {
|
||||
let mut main_loop = move || {
|
||||
let start = Instant::now();
|
||||
let current_frame_time = Instant::now();
|
||||
let frame_duration = current_frame_time.duration_since(last_frame_time);
|
||||
|
||||
14
src/map.rs
14
src/map.rs
@@ -3,7 +3,7 @@ use rand::rngs::SmallRng;
|
||||
use rand::seq::IteratorRandom;
|
||||
use rand::SeedableRng;
|
||||
|
||||
use crate::constants::{MapTile, BOARD_CELL_SIZE, BOARD_OFFSET, CELL_SIZE};
|
||||
use crate::constants::{MapTile, BOARD_CELL_OFFSET, BOARD_CELL_SIZE, BOARD_PIXEL_OFFSET, BOARD_PIXEL_SIZE, CELL_SIZE};
|
||||
use crate::texture::sprite::AtlasTile;
|
||||
use glam::{IVec2, UVec2};
|
||||
use once_cell::sync::OnceCell;
|
||||
@@ -104,7 +104,10 @@ impl Map {
|
||||
///
|
||||
/// * `cell` - The cell coordinates, in grid coordinates.
|
||||
pub fn cell_to_pixel(cell: UVec2) -> IVec2 {
|
||||
IVec2::new((cell.x * CELL_SIZE) as i32, ((cell.y + BOARD_OFFSET.y) * CELL_SIZE) as i32)
|
||||
IVec2::new(
|
||||
(cell.x * CELL_SIZE) as i32,
|
||||
((cell.y + BOARD_CELL_OFFSET.y) * CELL_SIZE) as i32,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns a reference to a cached vector of all valid playable positions in the maze.
|
||||
@@ -162,7 +165,12 @@ impl Map {
|
||||
|
||||
/// Renders the map to the given canvas using the provided map texture.
|
||||
pub fn render(&self, canvas: &mut Canvas<Window>, map_texture: &mut AtlasTile) {
|
||||
let dest = Rect::new(0, 0, CELL_SIZE * BOARD_CELL_SIZE.x, CELL_SIZE * BOARD_CELL_SIZE.y);
|
||||
let dest = Rect::new(
|
||||
BOARD_PIXEL_OFFSET.x as i32,
|
||||
BOARD_PIXEL_OFFSET.y as i32,
|
||||
BOARD_PIXEL_SIZE.x,
|
||||
BOARD_PIXEL_SIZE.y,
|
||||
);
|
||||
let _ = map_texture.render(canvas, dest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! This module provides a simple animation and atlas system for textures.
|
||||
use anyhow::Result;
|
||||
use sdl2::{pixels::Color, render::WindowCanvas};
|
||||
use sdl2::render::WindowCanvas;
|
||||
|
||||
use crate::texture::sprite::AtlasTile;
|
||||
|
||||
@@ -43,7 +43,7 @@ impl AnimatedTexture {
|
||||
}
|
||||
|
||||
pub fn render(&mut self, canvas: &mut WindowCanvas, dest: sdl2::rect::Rect) -> Result<()> {
|
||||
let mut tile = self.current_tile();
|
||||
let tile = self.current_tile();
|
||||
tile.render(canvas, dest)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
use anyhow::Result;
|
||||
use glam::UVec2;
|
||||
use sdl2::pixels::Color;
|
||||
use sdl2::rect::Rect;
|
||||
use sdl2::render::{Canvas, RenderTarget};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
Reference in New Issue
Block a user