[package]
name = "game-tutorial"
version = "0.1.0"
authors = ["Sunjay Varma <[email protected]>"]
edition = "2018"
[dependencies]
specs = "0.14"
specs-derive = "0.4"
[dependencies.sdl2]
version = "0.32.1"
default-features = false
features = ["image"]
use sdl2::pixels::Color;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::render::{WindowCanvas, Texture};
use sdl2::rect::{Point, Rect};
// "self" imports the "image" module itself as well as everything else we listed
use sdl2::image::{self, LoadTexture, InitFlag};
use std::time::Duration;
const PLAYER_MOVEMENT_SPEED: i32 = 20;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Direction {
Up,
Down,
Left,
Right,
}
#[derive(Debug)]
struct Player {
position: Point,
sprite: Rect,
speed: i32,
direction: Direction,
current_frame: i32,
}
/// Returns the row of the spritesheet corresponding to the given direction
fn direction_spritesheet_row(direction: Direction) -> i32 {
use self::Direction::*;
match direction {
Up => 3,
Down => 0,
Left => 1,
Right => 2,
}
}
fn render(
canvas: &mut WindowCanvas,
color: Color,
texture: &Texture,
player: &Player,
) -> Result<(), String> {
canvas.set_draw_color(color);
canvas.clear();
let (width, height) = canvas.output_size()?;
let (frame_width, frame_height) = player.sprite.size();
let current_frame = Rect::new(
player.sprite.x() + frame_width as i32 * player.current_frame,
player.sprite.y() + frame_height as i32 * direction_spritesheet_row(player.direction),
frame_width,
frame_height,
);
// Treat the center of the screen as the (0, 0) coordinate
let screen_position = player.position + Point::new(width as i32 / 2, height as i32 / 2);
let screen_rect = Rect::from_center(screen_position, frame_width, frame_height);
canvas.copy(texture, current_frame, screen_rect)?;
canvas.present();
Ok(())
}
// Update player a fixed amount based on their speed.
// WARNING: Calling this function too often or at a variable speed will cause the player's speed
// to be unpredictable!
fn update_player(player: &mut Player) {
use self::Direction::*;
match player.direction {
Left => {
player.position = player.position.offset(-player.speed, 0);
},
Right => {
player.position = player.position.offset(player.speed, 0);
},
Up => {
player.position = player.position.offset(0, -player.speed);
},
Down => {
player.position = player.position.offset(0, player.speed);
},
}
// Only continue to animate if the player is moving
if player.speed != 0 {
// Cheat: using the fact that all animations are 3 frames (NOT extensible)
player.current_frame = (player.current_frame + 1) % 3;
}
}
fn main() -> Result<(), String> {
let sdl_context = sdl2::init()?;
let video_subsystem = sdl_context.video()?;
// Leading "_" tells Rust that this is an unused variable that we don't care about. It has to
// stay unused because if we don't have any variable at all then Rust will treat it as a
// temporary value and drop it right away!
let _image_context = image::init(InitFlag::PNG | InitFlag::JPG)?;
let window = video_subsystem.window("game tutorial", 800, 600)
.position_centered()
.build()
.expect("could not initialize video subsystem");
let mut canvas = window.into_canvas().build()
.expect("could not make a canvas");
let texture_creator = canvas.texture_creator();
let texture = texture_creator.load_texture("assets/bardo.png")?;
let mut player = Player {
position: Point::new(0, 0),
sprite: Rect::new(0, 0, 26, 36),
speed: 0,
direction: Direction::Right,
current_frame: 0,
};
let mut event_pump = sdl_context.event_pump()?;
let mut i = 0;
'running: loop {
// Handle events
for event in event_pump.poll_iter() {
match event {
Event::Quit {..} |
Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
break 'running;
},
Event::KeyDown { keycode: Some(Keycode::Left), repeat: false, .. } => {
player.speed = PLAYER_MOVEMENT_SPEED;
player.direction = Direction::Left;
},
Event::KeyDown { keycode: Some(Keycode::Right), repeat: false, .. } => {
player.speed = PLAYER_MOVEMENT_SPEED;
player.direction = Direction::Right;
},
Event::KeyDown { keycode: Some(Keycode::Up), repeat: false, .. } => {
player.speed = PLAYER_MOVEMENT_SPEED;
player.direction = Direction::Up;
},
Event::KeyDown { keycode: Some(Keycode::Down), repeat: false, .. } => {
player.speed = PLAYER_MOVEMENT_SPEED;
player.direction = Direction::Down;
},
Event::KeyUp { keycode: Some(Keycode::Left), repeat: false, .. } |
Event::KeyUp { keycode: Some(Keycode::Right), repeat: false, .. } |
Event::KeyUp { keycode: Some(Keycode::Up), repeat: false, .. } |
Event::KeyUp { keycode: Some(Keycode::Down), repeat: false, .. } => {
player.speed = 0;
},
_ => {}
}
}
// Update
i = (i + 1) % 255;
update_player(&mut player);
// Render
render(&mut canvas, Color::RGB(i, 64, 255 - i), &texture, &player)?;
// Time management!
::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
}
Ok(())
}