Sunjay Varma
Link to slides:
slides.com/sunjay/learn-game-dev/fullscreen
Short URL: bit.ly/2P5lQoL
Lucio Franco
Link to slides:
slides.com/sunjay/learn-game-dev/fullscreen
Short URL: bit.ly/2P5lQoL
These pictures are from ~10 years ago!
Professional compiler enthusiast
developer
Professional .clone() caller
Link to slides:
slides.com/sunjay/learn-game-dev/fullscreen
Short URL: bit.ly/2P5lQoL
If you haven't already, clone this repo:
Image: xkcd.com/1349
// Create a window, initialize game state, etc.
initialize();
// Run forever (or until the user exits)
loop {
// Handle ALL the events that are available
// **right now**, or just keep going
handleEvents();
// Update the state of players, enemies, etc.
update();
// Draw the entire current state of the game
render();
// Ensure that 1 second is always 1 second
// regardless of the speed of the processor
time_management();
}
// Save game state, clean up temporary files, etc.
cleanup();
Event enum - defines the various types of input events that can occur
'running:
loop {
for event in event_pump.poll_iter() {
match event {
// Quit the game if the window is closed or if the escape key is pressed
Event::Quit {..} |
Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
break 'running
},
Event::KeyDown { keycode: Some(Keycode::Up), repeat: false, .. } => {
println!("The up arrow key pressed down!");
},
Event::KeyUp { keycode: Some(Keycode::Up), repeat: false, .. } => {
println!("The up arrow key released!");
},
_ => {},
}
}
// Update, render, etc.
}
Fill in the event handling section of the game loop. You will need to use methods on the Player struct. One of the cases is already filled out for you. When you are finished, you should be able to completely control the player's movement. The player should move in the direction of the arrow key you hold down and stop when you release the key.
Relevant Files:
Make sure you resolve all of the TODO(EX#1) comments.
let player_bounds = Rect::new(0, 0, 5, 5);
let enemy_bounds = Rect::new(1, 1, 7, 6);
println!("{:?}", player_bounds.intersection(enemy_bounds));
Intersection
No Intersection
pixels = (pixels / μs) * μs
If you walk at a speed of 5 miles per hour for 2 hours, you will walk 10 miles.
With the people sitting around you, go through the update method in the Player struct line by line and convince each other that the implementation is correct. You may ignore the code that has to do with the "frame" and "frame_timer" fields since that code has to do with animations. Make sure everyone understands the entire method and everything that is going on.
Relevant Files:
Sprite/Frame
Animation
Looks like walking
Down
Left
Right
Up
Frame 0
Frame 1
Frame 2
Screen Coordinates
World Coordinates
cargo doc --open
Canvas struct - provides an API for drawing in 2D
WindowCanvas type - alias for Canvas<Window>
clear() - clears the screen so you can draw from a blank state
fill_rect() - fills in a rectangular area of the screen
set_draw_color() - sets the color used to clear the screen or draw rectangles, lines, etc.
present() - puts everything you've drawn on the screen
Texture struct - represents an image
texture_creator - method on Canvas for fetching the TextureCreator struct
Relevant Files:
Make sure you resolve all of the TODO(EX#3) comments.
All of us in this workshop right now
Image source: xkcd.com/1906/ XKCD "Making Progress"
(in one slide)
ID | Position | Velocity | Sprite | Animation |
---|---|---|---|---|
0 | (3,-4) | 1 Up | 0 | frame 1 |
1 | (-17,8) | -1 Left | 1 | |
2 | (-1,0) | 0 Right | ||
... | ... | ... | ... | ... |
Entities
Components
Created before game loop starts
Systems
Physics
Animator
Game AI
Keyboard
Called during "update" step
Each system processes only the columns it needs
Passed in
Updated
Executes in parallel
use specs::{World, WorldExt, Builder};
let mut World = World::new();
dispatcher.setup(&mut world);
world.create_entity()
.with(Player::Hero)
.with(Position {x: 32, y: 21})
.with(Velocity {x: -1, y: 0})
.with(Health(32))
.build();
use specs::{Component, VecStorage};
#[derive(Component)]
#[storage(VecStorage)]
struct Position {
x: i32,
y: i32,
}
#[derive(Component)]
#[storage(VecStorage)]
struct Health(u32);
#[derive(Component)]
#[storage(VecStorage)]
enum Player {
Hero,
Sidekick,
}
Create entities and components for the randomly generated enemies. When you start, there should only be a player and the goal on the screen. The player won't move yet (see the next exercise). Once this exercise is complete, the enemies will show up in random positions on the screen but they will not move (also a future exercise).
Relevant Files:
Make sure you resolve all of the TODO(EX#4) comments.
Use a resource to pass input to the Keyboard system. The resource you need to use is already defined in resources.rs. You will need to modify the event handling step of the game loop to send the appropriate value (or no value) to the system. When you are done, the player should move in response to pressing the arrow keys.
Relevant Files:
Make sure you resolve all of the TODO(EX#5) comments.
use specs::{System, SystemData, WriteStorage, Join, World, prelude::ResourceId};
pub struct AI;
#[derive(SystemData)]
pub struct AIData<'a> {
enemies: WriteStorage<'a, Enemy>,
velocities: WriteStorage<'a, Velocity>,
}
impl<'a> System<'a> for AI {
type SystemData = AIData<'a>;
fn run(&mut self, data: Self::SystemData) {
let AIData {mut enemies, mut velocities} = data;
// TODO
}
}
fn main() {
let mut dispatcher = DispatcherBuilder::new()
.with(systems::Keyboard, "Keyboard", &[])
.with(systems::Movement {world_bounds}, "Movement", &["Keyboard"])
.with(systems::WinLoseChecker, "WinLoseChecker", &["Movement"])
.with(systems::Animator, "Animator", &["Keyboard"])
.build();
}
pub struct WinLoseChecker;
#[derive(SystemData)]
pub struct WinLoseCheckerData<'a> {
players: ReadStorage<'a, Player>,
enemies: ReadStorage<'a, Enemy>,
bounding_boxes: ReadStorage<'a, BoundingBox>,
game_status: WriteExpect<'a, GameStatus>,
}
impl<'a> System<'a> for WinLoseChecker {
type SystemData = WinLoseCheckerData<'a>;
fn run(&mut self, data: Self::SystemData) {
let WinLoseCheckerData {players, enemies, goals, bounding_boxes, mut game_status} = data;
for (_, BoundingBox(player_bounds)) in (&players, &bounding_boxes).join() {
for (_, &BoundingBox(enemy_bounds)) in (&enemies, &bounding_boxes).join() {
// If the player collides with any enemies, they lose
if player_bounds.has_intersection(enemy_bounds) {
*game_status = GameStatus::Lose;
return;
}
}
// ...
}
}
}
Keyboard
AI
Movement
Animator
WinLoseChecker
Renderer
This System Runs Before
A
This System Runs After
B
Legend
The Dispatcher struct
Allow enemies to move on their own by implementing the AI system based on enemy.rs. You will need to add the system to the dispatcher, determine the system data you need, and use the Join trait. Once you are done, the enemies will move on their own and the entire game should be completely playable.
Relevant Files:
Make sure you resolve all of the TODO(EX#6) comments.
Image source: xkcd.com/554
These exercises are for when you finish early and need something to do
There are bonus exercises in the code labelled with "BONUS". The next few slides provide even more bonus exercises in case you finish the ones below.
There is far less detail for these exercises. Please ask for help if anything is unclear or if you get stuck.
Please ask for help if anything is unclear about the exercises or if you get stuck.
Please ask for help if anything is unclear about the exercises or if you get stuck.
Please ask for help if anything is unclear about the exercises or if you get stuck.
For further help on your game:
Rust Users Forum: users.rust-lang.org
Follow us on Twitter:
twitter.com/sunjay03
twitter.com/lucio_d_franco
Link to slides:
slides.com/sunjay/learn-game-dev/fullscreen
Short URL: bit.ly/2P5lQoL