Compare commits

...

6 Commits

6 changed files with 182 additions and 30 deletions

View File

@@ -4,9 +4,13 @@ use sdl2::{
video::Window, video::Window,
}; };
use crate::direction::Direction;
pub struct AnimatedTexture<'a> { pub struct AnimatedTexture<'a> {
raw_texture: &'a Texture<'a>, raw_texture: Texture<'a>,
current_frame: u32, ticker: u32,
reversed: bool,
ticks_per_frame: u32,
frame_count: u32, frame_count: u32,
frame_width: u32, frame_width: u32,
frame_height: u32, frame_height: u32,
@@ -14,40 +18,73 @@ pub struct AnimatedTexture<'a> {
impl<'a> AnimatedTexture<'a> { impl<'a> AnimatedTexture<'a> {
pub fn new( pub fn new(
texture: &'a Texture<'a>, texture: Texture<'a>,
ticks_per_frame: u32,
frame_count: u32, frame_count: u32,
frame_width: u32, frame_width: u32,
frame_height: u32, frame_height: u32,
) -> Self { ) -> Self {
AnimatedTexture { AnimatedTexture {
raw_texture: texture, raw_texture: texture,
current_frame: 0, ticker: 0,
reversed: false,
ticks_per_frame,
frame_count, frame_count,
frame_width, frame_width,
frame_height, frame_height,
} }
} }
fn current_frame(&self) -> u32 {
self.ticker / self.ticks_per_frame
}
fn next_frame(&mut self) { fn next_frame(&mut self) {
self.current_frame = (self.current_frame + 1) % self.frame_count; if self.reversed {
self.ticker -= 1;
if self.ticker == 0 {
self.reversed = !self.reversed;
}
} else {
self.ticker += 1;
if self.ticker + 1 == self.ticks_per_frame * self.frame_count {
self.reversed = !self.reversed;
}
}
} }
fn get_frame_rect(&self) -> Rect { fn get_frame_rect(&self) -> Rect {
Rect::new( Rect::new(
(self.current_frame * self.frame_width) as i32, self.current_frame() as i32 * self.frame_width as i32,
0, 0,
self.frame_width, self.frame_width,
self.frame_height, self.frame_height,
) )
} }
pub fn render(&mut self, canvas: &mut Canvas<Window>, position: (i32, i32)) { pub fn render(
&mut self,
canvas: &mut Canvas<Window>,
position: (i32, i32),
direction: Direction,
) {
let frame_rect = self.get_frame_rect(); let frame_rect = self.get_frame_rect();
let position_rect = Rect::new(position.0, position.1, self.frame_width, self.frame_height); let position_rect = Rect::new(position.0, position.1, self.frame_width, self.frame_height);
canvas
.copy(&self.raw_texture, frame_rect, position_rect)
.expect("Could not render sprite on canvas");
self.next_frame(); canvas
.copy_ex(
&self.raw_texture,
Some(frame_rect),
Some(position_rect),
direction.angle(),
None,
false,
false,
)
.expect("Could not render texture on canvas");
self.next_frame();
} }
} }

View File

@@ -1,6 +1,18 @@
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Direction { pub enum Direction {
Up, Up,
Down, Down,
Left, Left,
Right, Right,
} }
impl Direction {
pub fn angle(&self) -> f64 {
match self {
Direction::Right => 0f64,
Direction::Down => 90f64,
Direction::Left => 180f64,
Direction::Up => 270f64,
}
}
}

View File

@@ -5,4 +5,6 @@ pub trait Entity {
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 (XY position within the grid)
fn cell_position(&self) -> (u32, u32); fn cell_position(&self) -> (u32, u32);
// Tick the entity (move it, perform collision checks, etc)
fn tick(&mut self);
} }

View File

@@ -1,12 +1,17 @@
use sdl2::image::LoadTexture;
use sdl2::keyboard::Keycode;
use sdl2::render::{TextureCreator, Texture};
use sdl2::video::WindowContext;
use sdl2::{pixels::Color, render::Canvas, video::Window}; use sdl2::{pixels::Color, render::Canvas, video::Window};
use crate::constants::{MapTile, BOARD, BOARD_HEIGHT, BOARD_WIDTH}; use crate::constants::{MapTile, BOARD, BOARD_HEIGHT, BOARD_WIDTH};
use crate::pacman::Pacman; use crate::direction::Direction;
use crate::textures::TextureManager; use crate::entity::Entity;
use crate::pacman::{Pacman};
pub struct Game<'a> { pub struct Game<'a> {
pub textures: TextureManager<'a>,
canvas: &'a mut Canvas<Window>, canvas: &'a mut Canvas<Window>,
map_texture: Texture<'a>,
pacman: Pacman<'a>, pacman: Pacman<'a>,
debug: bool, debug: bool,
} }
@@ -14,19 +19,47 @@ pub struct Game<'a> {
impl Game<'_> { impl Game<'_> {
pub fn new<'a>( pub fn new<'a>(
canvas: &'a mut Canvas<Window>, canvas: &'a mut Canvas<Window>,
texture_manager: TextureManager<'a>, texture_creator: &'a TextureCreator<WindowContext>,
) -> Game<'a> { ) -> Game<'a> {
let pacman = Pacman::new(None, &texture_manager.pacman); let pacman_atlas = texture_creator
.load_texture("assets/32/pacman.png")
.expect("Could not load pacman texture");
let pacman = Pacman::new(None, pacman_atlas);
Game { Game {
canvas, canvas,
textures: texture_manager,
pacman: pacman, pacman: pacman,
debug: true, debug: true,
map_texture: texture_creator
.load_texture("assets/map.png")
.expect("Could not load pacman texture"),
} }
} }
pub fn tick(&mut self) {} pub fn keyboard_event(&mut self, keycode: Keycode) {
match keycode {
Keycode::D => {
self.pacman.direction = Direction::Right;
}
Keycode::A => {
self.pacman.direction = Direction::Left;
}
Keycode::W => {
self.pacman.direction = Direction::Up;
}
Keycode::S => {
self.pacman.direction = Direction::Down;
}
Keycode::Space => {
self.debug = !self.debug;
}
_ => {}
}
}
pub fn tick(&mut self) {
self.pacman.tick();
}
pub fn draw(&mut self) { pub fn draw(&mut self) {
// Clear the screen (black) // Clear the screen (black)
@@ -34,9 +67,12 @@ impl Game<'_> {
self.canvas.clear(); self.canvas.clear();
self.canvas self.canvas
.copy(&self.textures.map, None, None) .copy(&self.map_texture, None, None)
.expect("Could not render texture on canvas"); .expect("Could not render texture on canvas");
// Render the pacman
self.pacman.render(self.canvas);
// Draw a grid // Draw a grid
for x in 0..BOARD_WIDTH { for x in 0..BOARD_WIDTH {
for y in 0..BOARD_HEIGHT { for y in 0..BOARD_HEIGHT {

View File

@@ -46,7 +46,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, TextureManager::new(&texture_creator)); let mut game = Game::new(&mut canvas, &texture_creator);
let mut event_pump = sdl_context let mut event_pump = sdl_context
.event_pump() .event_pump()
@@ -55,7 +55,11 @@ pub fn main() {
game.draw(); game.draw();
game.tick(); game.tick();
let loop_time = Duration::from_millis(1000 / 60);
let mut main_loop = || { let mut main_loop = || {
let start = Instant::now();
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
@@ -64,9 +68,9 @@ pub fn main() {
keycode: Some(Keycode::Escape) | Some(Keycode::Q), keycode: Some(Keycode::Escape) | Some(Keycode::Q),
.. ..
} => return false, } => return false,
event @ Event::KeyDown { .. } => { Event::KeyDown { keycode , .. } => {
println!("{:?}", event); game.keyboard_event(keycode.unwrap());
} },
_ => {} _ => {}
} }
} }
@@ -83,15 +87,12 @@ pub fn main() {
start.elapsed() start.elapsed()
}; };
// Alert if tick time exceeds 10ms if start.elapsed() < loop_time {
if tick_time > Duration::from_millis(3) { ::std::thread::sleep(loop_time - start.elapsed());
println!("Tick took: {:?}", tick_time); } else {
} println!("Game loop behind schedule by: {:?}", start.elapsed() - loop_time);
if draw_time > Duration::from_millis(3) {
println!("Draw took: {:?}", draw_time);
} }
::std::thread::sleep(Duration::from_millis(10));
true true
}; };

64
src/pacman.rs Normal file
View File

@@ -0,0 +1,64 @@
use sdl2::{
render::{Canvas, Texture},
video::Window,
};
use crate::{animation::AnimatedTexture, direction::Direction, entity::Entity};
pub struct Pacman<'a> {
// Absolute position on the board (precise)
pub position: (i32, i32),
pub direction: Direction,
speed: u32,
sprite: AnimatedTexture<'a>,
}
impl Pacman<'_> {
pub fn new<'a>(starting_position: Option<(i32, i32)>, atlas: Texture<'a>) -> Pacman<'a> {
Pacman {
position: starting_position.unwrap_or((0i32, 0i32)),
direction: Direction::Right,
speed: 2,
sprite: AnimatedTexture::new(atlas, 4, 3, 32, 32),
}
}
pub fn render(&mut self, canvas: &mut Canvas<Window>) {
self.sprite.render(canvas, self.position, self.direction);
}
}
impl Entity for Pacman<'_> {
fn is_colliding(&self, other: &dyn Entity) -> bool {
let (x, y) = self.position();
let (other_x, other_y) = other.position();
x == other_x && y == other_y
}
fn position(&self) -> (i32, i32) {
self.position
}
fn cell_position(&self) -> (u32, u32) {
let (x, y) = self.position();
(x as u32 / 24, y as u32 / 24)
}
fn tick(&mut self) {
let speed = self.speed as i32;
match self.direction {
Direction::Right => {
self.position.0 += speed;
}
Direction::Left => {
self.position.0 -= speed;
}
Direction::Up => {
self.position.1 -= speed;
}
Direction::Down => {
self.position.1 += speed;
}
}
}
}