mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-07 03:15:49 -06:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8808a1aa3b | |||
| 62b2c607a9 |
BIN
assets/font/konami.ttf
Normal file
BIN
assets/font/konami.ttf
Normal file
Binary file not shown.
164
src/game.rs
164
src/game.rs
@@ -3,8 +3,10 @@ use std::rc::Rc;
|
|||||||
use sdl2::image::LoadTexture;
|
use sdl2::image::LoadTexture;
|
||||||
use sdl2::keyboard::Keycode;
|
use sdl2::keyboard::Keycode;
|
||||||
use sdl2::render::{Texture, TextureCreator};
|
use sdl2::render::{Texture, TextureCreator};
|
||||||
|
use sdl2::ttf::{Font, FontStyle};
|
||||||
use sdl2::video::WindowContext;
|
use sdl2::video::WindowContext;
|
||||||
use sdl2::{pixels::Color, render::Canvas, video::Window};
|
use sdl2::{pixels::Color, render::Canvas, video::Window};
|
||||||
|
use tracing::event;
|
||||||
|
|
||||||
use crate::constants::{MapTile, BOARD_HEIGHT, BOARD_WIDTH, RAW_BOARD};
|
use crate::constants::{MapTile, BOARD_HEIGHT, BOARD_WIDTH, RAW_BOARD};
|
||||||
use crate::direction::Direction;
|
use crate::direction::Direction;
|
||||||
@@ -15,22 +17,38 @@ use crate::pacman::Pacman;
|
|||||||
pub struct Game<'a> {
|
pub struct Game<'a> {
|
||||||
canvas: &'a mut Canvas<Window>,
|
canvas: &'a mut Canvas<Window>,
|
||||||
map_texture: Texture<'a>,
|
map_texture: Texture<'a>,
|
||||||
|
pellet_texture: Texture<'a>,
|
||||||
|
power_pellet_texture: Texture<'a>,
|
||||||
|
font: Font<'a, 'static>,
|
||||||
pacman: Pacman<'a>,
|
pacman: Pacman<'a>,
|
||||||
map: Rc<Map>,
|
map: Rc<std::cell::RefCell<Map>>,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
|
score: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game<'_> {
|
impl Game<'_> {
|
||||||
pub fn new<'a>(
|
pub fn new<'a>(
|
||||||
canvas: &'a mut Canvas<Window>,
|
canvas: &'a mut Canvas<Window>,
|
||||||
texture_creator: &'a TextureCreator<WindowContext>,
|
texture_creator: &'a TextureCreator<WindowContext>,
|
||||||
|
ttf_context: &'a sdl2::ttf::Sdl2TtfContext,
|
||||||
) -> Game<'a> {
|
) -> Game<'a> {
|
||||||
let map = Rc::new(Map::new(RAW_BOARD));
|
let map = Rc::new(std::cell::RefCell::new(Map::new(RAW_BOARD)));
|
||||||
let pacman_atlas = texture_creator
|
let pacman_atlas = texture_creator
|
||||||
.load_texture("assets/32/pacman.png")
|
.load_texture("assets/32/pacman.png")
|
||||||
.expect("Could not load pacman texture");
|
.expect("Could not load pacman texture");
|
||||||
let pacman = Pacman::new((1, 1), pacman_atlas, Rc::clone(&map));
|
let pacman = Pacman::new((1, 1), pacman_atlas, Rc::clone(&map));
|
||||||
|
|
||||||
|
let pellet_texture = texture_creator
|
||||||
|
.load_texture("assets/24/pellet.png")
|
||||||
|
.expect("Could not load pellet texture");
|
||||||
|
let power_pellet_texture = texture_creator
|
||||||
|
.load_texture("assets/24/energizer.png")
|
||||||
|
.expect("Could not load power pellet texture");
|
||||||
|
|
||||||
|
let font = ttf_context
|
||||||
|
.load_font("assets/font/konami.ttf", 24)
|
||||||
|
.expect("Could not load font");
|
||||||
|
|
||||||
Game {
|
Game {
|
||||||
canvas,
|
canvas,
|
||||||
pacman: pacman,
|
pacman: pacman,
|
||||||
@@ -38,7 +56,11 @@ impl Game<'_> {
|
|||||||
map: map,
|
map: map,
|
||||||
map_texture: texture_creator
|
map_texture: texture_creator
|
||||||
.load_texture("assets/map.png")
|
.load_texture("assets/map.png")
|
||||||
.expect("Could not load pacman texture"),
|
.expect("Could not load map texture"),
|
||||||
|
pellet_texture,
|
||||||
|
power_pellet_texture,
|
||||||
|
font,
|
||||||
|
score: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,8 +75,57 @@ impl Game<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_score(&mut self, points: u32) {
|
||||||
|
self.score += points;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn tick(&mut self) {
|
pub fn tick(&mut self) {
|
||||||
self.pacman.tick();
|
self.pacman.tick();
|
||||||
|
self.check_pellet_eating();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_pellet_eating(&mut self) {
|
||||||
|
let cell_pos = self.pacman.cell_position();
|
||||||
|
|
||||||
|
// Check if there's a pellet at the current position
|
||||||
|
let tile = {
|
||||||
|
let map = self.map.borrow();
|
||||||
|
map.get_tile((cell_pos.0 as i32, cell_pos.1 as i32))
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(tile) = tile {
|
||||||
|
match tile {
|
||||||
|
MapTile::Pellet => {
|
||||||
|
// Eat the pellet and add score
|
||||||
|
{
|
||||||
|
let mut map = self.map.borrow_mut();
|
||||||
|
map.set_tile((cell_pos.0 as i32, cell_pos.1 as i32), MapTile::Empty);
|
||||||
|
}
|
||||||
|
self.add_score(10);
|
||||||
|
event!(
|
||||||
|
tracing::Level::DEBUG,
|
||||||
|
"Pellet eaten at ({}, {})",
|
||||||
|
cell_pos.0,
|
||||||
|
cell_pos.1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
MapTile::PowerPellet => {
|
||||||
|
// Eat the power pellet and add score
|
||||||
|
{
|
||||||
|
let mut map = self.map.borrow_mut();
|
||||||
|
map.set_tile((cell_pos.0 as i32, cell_pos.1 as i32), MapTile::Empty);
|
||||||
|
}
|
||||||
|
self.add_score(50);
|
||||||
|
event!(
|
||||||
|
tracing::Level::DEBUG,
|
||||||
|
"Power pellet eaten at ({}, {})",
|
||||||
|
cell_pos.0,
|
||||||
|
cell_pos.1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&mut self) {
|
pub fn draw(&mut self) {
|
||||||
@@ -62,21 +133,27 @@ impl Game<'_> {
|
|||||||
self.canvas.set_draw_color(Color::RGB(0, 0, 0));
|
self.canvas.set_draw_color(Color::RGB(0, 0, 0));
|
||||||
self.canvas.clear();
|
self.canvas.clear();
|
||||||
|
|
||||||
|
// Render the map
|
||||||
// Render the map
|
|
||||||
self.canvas
|
self.canvas
|
||||||
.copy(&self.map_texture, None, None)
|
.copy(&self.map_texture, None, None)
|
||||||
.expect("Could not render texture on canvas");
|
.expect("Could not render texture on canvas");
|
||||||
|
|
||||||
|
// Render pellets
|
||||||
|
self.render_pellets();
|
||||||
|
|
||||||
// Render the pacman
|
// Render the pacman
|
||||||
self.pacman.render(self.canvas);
|
self.pacman.render(self.canvas);
|
||||||
|
|
||||||
// Draw a grid
|
// Render score
|
||||||
|
self.render_score();
|
||||||
|
|
||||||
|
// Draw the debug grid
|
||||||
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
|
let tile = self
|
||||||
.map
|
.map
|
||||||
|
.borrow()
|
||||||
.get_tile((x as i32, y as i32))
|
.get_tile((x as i32, y as i32))
|
||||||
.unwrap_or(MapTile::Empty);
|
.unwrap_or(MapTile::Empty);
|
||||||
let mut color = None;
|
let mut color = None;
|
||||||
@@ -110,6 +187,7 @@ impl Game<'_> {
|
|||||||
|
|
||||||
fn draw_cell(&mut self, cell: (u32, u32), color: Color) {
|
fn draw_cell(&mut self, cell: (u32, u32), color: Color) {
|
||||||
let position = Map::cell_to_pixel(cell);
|
let position = Map::cell_to_pixel(cell);
|
||||||
|
|
||||||
self.canvas.set_draw_color(color);
|
self.canvas.set_draw_color(color);
|
||||||
self.canvas
|
self.canvas
|
||||||
.draw_rect(sdl2::rect::Rect::new(
|
.draw_rect(sdl2::rect::Rect::new(
|
||||||
@@ -120,4 +198,78 @@ impl Game<'_> {
|
|||||||
))
|
))
|
||||||
.expect("Could not draw rectangle");
|
.expect("Could not draw rectangle");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn render_pellets(&mut self) {
|
||||||
|
for x in 0..BOARD_WIDTH {
|
||||||
|
for y in 0..BOARD_HEIGHT {
|
||||||
|
let tile = self
|
||||||
|
.map
|
||||||
|
.borrow()
|
||||||
|
.get_tile((x as i32, y as i32))
|
||||||
|
.unwrap_or(MapTile::Empty);
|
||||||
|
|
||||||
|
match tile {
|
||||||
|
MapTile::Pellet => {
|
||||||
|
let position = Map::cell_to_pixel((x, y));
|
||||||
|
let dst_rect = sdl2::rect::Rect::new(position.0, position.1, 24, 24);
|
||||||
|
self.canvas
|
||||||
|
.copy(&self.pellet_texture, None, Some(dst_rect))
|
||||||
|
.expect("Could not render pellet");
|
||||||
|
}
|
||||||
|
MapTile::PowerPellet => {
|
||||||
|
let position = Map::cell_to_pixel((x, y));
|
||||||
|
let dst_rect = sdl2::rect::Rect::new(position.0, position.1, 24, 24);
|
||||||
|
self.canvas
|
||||||
|
.copy(&self.power_pellet_texture, None, Some(dst_rect))
|
||||||
|
.expect("Could not render power pellet");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_score(&mut self) {
|
||||||
|
let lives = 3;
|
||||||
|
let score_text = format!("{:02}", self.score);
|
||||||
|
|
||||||
|
let x_offset = 12;
|
||||||
|
let y_offset = 2;
|
||||||
|
let lives_offset = 3;
|
||||||
|
let score_offset = 7 - (score_text.len() as i32);
|
||||||
|
let gap_offset = 6;
|
||||||
|
|
||||||
|
self.render_text(
|
||||||
|
&format!("{}UP HIGH SCORE ", lives),
|
||||||
|
(24 * lives_offset + x_offset, y_offset),
|
||||||
|
Color::WHITE,
|
||||||
|
);
|
||||||
|
self.render_text(
|
||||||
|
&score_text,
|
||||||
|
(24 * score_offset + x_offset, 24 + y_offset + gap_offset),
|
||||||
|
Color::WHITE,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_text(&mut self, text: &str, position: (i32, i32), color: Color) {
|
||||||
|
let surface = self
|
||||||
|
.font
|
||||||
|
.render(text)
|
||||||
|
.blended(color)
|
||||||
|
.expect("Could not render text surface");
|
||||||
|
|
||||||
|
let texture_creator = self.canvas.texture_creator();
|
||||||
|
let texture = texture_creator
|
||||||
|
.create_texture_from_surface(&surface)
|
||||||
|
.expect("Could not create texture from surface");
|
||||||
|
|
||||||
|
let query = texture.query();
|
||||||
|
|
||||||
|
let dst_rect =
|
||||||
|
sdl2::rect::Rect::new(position.0, position.1, query.width + 4, query.height + 4);
|
||||||
|
|
||||||
|
self.canvas
|
||||||
|
.copy(&texture, None, Some(dst_rect))
|
||||||
|
.expect("Could not render text texture");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
src/main.rs
11
src/main.rs
@@ -20,6 +20,7 @@ mod pacman;
|
|||||||
pub fn main() {
|
pub fn main() {
|
||||||
let sdl_context = sdl2::init().unwrap();
|
let sdl_context = sdl2::init().unwrap();
|
||||||
let video_subsystem = sdl_context.video().unwrap();
|
let video_subsystem = sdl_context.video().unwrap();
|
||||||
|
let ttf_context = sdl2::ttf::init().unwrap();
|
||||||
|
|
||||||
// Setup tracing
|
// Setup tracing
|
||||||
let subscriber = tracing_subscriber::fmt()
|
let subscriber = tracing_subscriber::fmt()
|
||||||
@@ -46,7 +47,7 @@ pub fn main() {
|
|||||||
.expect("Could not set logical size");
|
.expect("Could not set logical size");
|
||||||
|
|
||||||
let texture_creator = canvas.texture_creator();
|
let texture_creator = canvas.texture_creator();
|
||||||
let mut game = Game::new(&mut canvas, &texture_creator);
|
let mut game = Game::new(&mut canvas, &texture_creator, &ttf_context);
|
||||||
|
|
||||||
let mut event_pump = sdl_context
|
let mut event_pump = sdl_context
|
||||||
.event_pump()
|
.event_pump()
|
||||||
@@ -150,14 +151,6 @@ pub fn main() {
|
|||||||
let average_sleep = sleep_time / PERIOD;
|
let average_sleep = sleep_time / PERIOD;
|
||||||
let average_process = loop_time - average_sleep;
|
let average_process = loop_time - average_sleep;
|
||||||
|
|
||||||
event!(
|
|
||||||
tracing::Level::DEBUG,
|
|
||||||
"Timing Averages [fps={}] [sleep={:?}] [process={:?}]",
|
|
||||||
average_fps,
|
|
||||||
average_sleep,
|
|
||||||
average_process
|
|
||||||
);
|
|
||||||
|
|
||||||
sleep_time = Duration::ZERO;
|
sleep_time = Duration::ZERO;
|
||||||
last_averaging_time = Instant::now();
|
last_averaging_time = Instant::now();
|
||||||
}
|
}
|
||||||
|
|||||||
12
src/map.rs
12
src/map.rs
@@ -53,6 +53,18 @@ impl Map {
|
|||||||
Some(self.inner[x][y])
|
Some(self.inner[x][y])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_tile(&mut self, cell: (i32, i32), tile: MapTile) -> bool {
|
||||||
|
let x = cell.0 as usize;
|
||||||
|
let y = cell.1 as usize;
|
||||||
|
|
||||||
|
if x >= BOARD_WIDTH as usize || y >= BOARD_HEIGHT as usize {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inner[x][y] = tile;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
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,3 +1,4 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use sdl2::{
|
use sdl2::{
|
||||||
@@ -22,14 +23,18 @@ pub struct Pacman<'a> {
|
|||||||
pub direction: Direction,
|
pub direction: Direction,
|
||||||
pub next_direction: Option<Direction>,
|
pub next_direction: Option<Direction>,
|
||||||
pub stopped: bool,
|
pub stopped: bool,
|
||||||
map: Rc<Map>,
|
map: Rc<RefCell<Map>>,
|
||||||
speed: u32,
|
speed: u32,
|
||||||
modulation: SimpleTickModulator,
|
modulation: SimpleTickModulator,
|
||||||
sprite: AnimatedTexture<'a>,
|
sprite: AnimatedTexture<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pacman<'_> {
|
impl Pacman<'_> {
|
||||||
pub fn new<'a>(starting_position: (u32, u32), atlas: Texture<'a>, map: Rc<Map>) -> Pacman<'a> {
|
pub fn new<'a>(
|
||||||
|
starting_position: (u32, u32),
|
||||||
|
atlas: Texture<'a>,
|
||||||
|
map: Rc<RefCell<Map>>,
|
||||||
|
) -> Pacman<'a> {
|
||||||
Pacman {
|
Pacman {
|
||||||
position: Map::cell_to_pixel(starting_position),
|
position: Map::cell_to_pixel(starting_position),
|
||||||
direction: Direction::Right,
|
direction: Direction::Right,
|
||||||
@@ -70,6 +75,7 @@ impl Pacman<'_> {
|
|||||||
let proposed_next_cell = self.next_cell(self.next_direction);
|
let proposed_next_cell = self.next_cell(self.next_direction);
|
||||||
let proposed_next_tile = self
|
let proposed_next_tile = self
|
||||||
.map
|
.map
|
||||||
|
.borrow()
|
||||||
.get_tile(proposed_next_cell)
|
.get_tile(proposed_next_cell)
|
||||||
.unwrap_or(MapTile::Empty);
|
.unwrap_or(MapTile::Empty);
|
||||||
if proposed_next_tile != MapTile::Wall {
|
if proposed_next_tile != MapTile::Wall {
|
||||||
@@ -79,7 +85,7 @@ impl Pacman<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn internal_position_even(&self) -> (u32, u32) {
|
fn internal_position_even(&self) -> (u32, u32) {
|
||||||
let (x, y ) = self.internal_position();
|
let (x, y) = self.internal_position();
|
||||||
((x / 2u32) * 2u32, (y / 2u32) * 2u32)
|
((x / 2u32) * 2u32, (y / 2u32) * 2u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,7 +121,7 @@ impl Entity for Pacman<'_> {
|
|||||||
self.handle_requested_direction();
|
self.handle_requested_direction();
|
||||||
|
|
||||||
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.borrow().get_tile(next).unwrap_or(MapTile::Empty);
|
||||||
|
|
||||||
if !self.stopped && next_tile == MapTile::Wall {
|
if !self.stopped && next_tile == MapTile::Wall {
|
||||||
event!(tracing::Level::DEBUG, "Wall collision. Stopping.");
|
event!(tracing::Level::DEBUG, "Wall collision. Stopping.");
|
||||||
@@ -125,7 +131,7 @@ impl Entity for Pacman<'_> {
|
|||||||
self.stopped = false;
|
self.stopped = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.stopped && self.modulation.next() {
|
if !self.stopped && self.modulation.next() {
|
||||||
let speed = self.speed as i32;
|
let speed = self.speed as i32;
|
||||||
match self.direction {
|
match self.direction {
|
||||||
|
|||||||
Reference in New Issue
Block a user