• src/main.rs
  • src/animator.rs
+34 -0src/animator.rs
  1
use specs::prelude::*;
  2

  3
use crate::components::*;
  4

  5
struct Animator;
  6

  7
impl<'a> System<'a> for Animator {
  8
    type SystemData = (
  9
        WriteStorage<'a, MovementAnimation>,
  10
        WriteStorage<'a, Sprite>,
  11
        ReadStorage<'a, Velocity>,
  12
    );
  13

  14
    fn run(&mut self, mut data: Self::SystemData) {
  15
        use self::Direction::*;
  16
        //TODO: This code can be made nicer and more idiomatic using more pattern matching.
  17
        // Look up "rust irrefutable patterns" and use them here.
  18
        for (anim, sprite, vel) in (&mut data.0, &mut data.1, &data.2).join() {
  19
            if vel.speed == 0 {
  20
                continue;
  21
            }
  22

  23
            let frames = match vel.direction {
  24
                Left => &anim.left_frames,
  25
                Right => &anim.right_frames,
  26
                Up => &anim.up_frames,
  27
                Down => &anim.down_frames,
  28
            };
  29

  30
            anim.current_frame = (anim.current_frame + 1) % frames.len();
  31
            *sprite = frames[anim.current_frame].clone();
  32
        }
  33
    }
  34
}
+2 -12src/main.rs
1 1
mod components;
2 2
mod physics;
  3
mod animator;
3 4

4 5
use sdl2::pixels::Color;
5 6
use sdl2::event::Event;
6 7
use sdl2::keyboard::Keycode;
7 8
use sdl2::render::{WindowCanvas, Texture};
8 9
use sdl2::rect::{Point, Rect};
9 10
// "self" imports the "image" module itself as well as everything else we listed
10 11
use sdl2::image::{self, LoadTexture, InitFlag};
11 12

12 13
use specs::prelude::*;
13 14

14 15
use std::time::Duration;
15 16

16 17
use crate::components::*;
17 18

18 19
const PLAYER_MOVEMENT_SPEED: i32 = 20;
19 20

20 21
/// Returns the row of the spritesheet corresponding to the given direction
21 22
fn direction_spritesheet_row(direction: Direction) -> i32 {
22 23
    use self::Direction::*;
23 24
    match direction {
24 25
        Up => 3,
25 26
        Down => 0,
26 27
        Left => 1,
27 28
        Right => 2,
28 29
    }
29 30
}
30 31

31 32
/// Create animation frames for the standard character spritesheet
32 33
fn character_animation_frames(spritesheet: usize, top_left_frame: Rect, direction: Direction) -> Vec<Sprite> {
33 34
    // All assumptions about the spritesheets are now encapsulated in this function instead of in
34 35
    // the design of our entire system. We can always replace this function, but replacing the
35 36
    // entire system is harder.
36 37

37 38
    let (frame_width, frame_height) = top_left_frame.size();
38 39
    let y_offset = top_left_frame.y() + frame_height as i32 * direction_spritesheet_row(direction);
39 40

40 41
    let mut frames = Vec::new();
41 42
    for i in 0..3 {
42 43
        frames.push(Sprite {
43 44
            spritesheet,
44 45
            region: Rect::new(
45 46
                top_left_frame.x() + frame_width as i32 * i,
46 47
                y_offset,
47 48
                frame_width,
48 49
                frame_height,
49 50
            ),
50 51
        })
51 52
    }
52 53

53 54
    frames
54 55
}
55 56

56 57
fn render(
57 58
    canvas: &mut WindowCanvas,
58 59
    color: Color,
59 60
    texture: &Texture,
60 61
    player: &Player,
61 62
) -> Result<(), String> {
62 63
    canvas.set_draw_color(color);
63 64
    canvas.clear();
64 65

65 66
    let (width, height) = canvas.output_size()?;
66 67

67 68
    let (frame_width, frame_height) = player.sprite.size();
68 69
    let current_frame = Rect::new(
69 70
        player.sprite.x() + frame_width as i32 * player.current_frame,
70 71
        player.sprite.y() + frame_height as  i32 * direction_spritesheet_row(player.direction),
71 72
        frame_width,
72 73
        frame_height,
73 74
    );
74 75

75 76
    // Treat the center of the screen as the (0, 0) coordinate
76 77
    let screen_position = player.position + Point::new(width as i32 / 2, height as i32 / 2);
77 78
    let screen_rect = Rect::from_center(screen_position, frame_width, frame_height);
78 79
    canvas.copy(texture, current_frame, screen_rect)?;
79 80

80 81
    canvas.present();
81 82

82 83
    Ok(())
83 84
}
84 85

85  
// Update player a fixed amount based on their speed.
86  
// WARNING: Calling this function too often or at a variable speed will cause the player's speed
87  
// to be unpredictable!
88  
fn update_player(player: &mut Player) {
89  
    // Only continue to animate if the player is moving
90  
    if player.speed != 0 {
91  
        // Cheat: using the fact that all animations are 3 frames (NOT extensible)
92  
        player.current_frame = (player.current_frame + 1) % 3;
93  
    }
94  
}
95  

96 86
fn main() -> Result<(), String> {
97 87
    let sdl_context = sdl2::init()?;
98 88
    let video_subsystem = sdl_context.video()?;
99 89
    // Leading "_" tells Rust that this is an unused variable that we don't care about. It has to
100 90
    // stay unused because if we don't have any variable at all then Rust will treat it as a
101 91
    // temporary value and drop it right away!
102 92
    let _image_context = image::init(InitFlag::PNG | InitFlag::JPG)?;
103 93

104 94
    let window = video_subsystem.window("game tutorial", 800, 600)
105 95
        .position_centered()
106 96
        .build()
107 97
        .expect("could not initialize video subsystem");
108 98

109 99
    let mut canvas = window.into_canvas().build()
110 100
        .expect("could not make a canvas");
111 101

112 102
    let texture_creator = canvas.texture_creator();
113 103

114 104
    let textures = [
115 105
        texture_creator.load_texture("assets/bardo.png")?,
116 106
    ];
117 107
    // First texture in textures array
118 108
    let player_spritesheet = 0;
119 109
    let player_top_left_frame = Rect::new(0, 0, 26, 36);
120 110

121 111
    let player_animation = MovementAnimation {
122 112
        current_frame: 0,
123 113
        up_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Up),
124 114
        down_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Down),
125 115
        left_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Left),
126 116
        right_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Right),
127 117
    };
128 118

129 119
    let mut world = World::new();
130 120

131 121
    world.create_entity()
132 122
        .with(Position(Point::new(0, 0)))
133 123
        .with(Velocity {speed: 0, direction: Direction::Right})
134 124
        .with(player_animation.right_frames[0].clone())
135 125
        .with(player_animation)
136 126
        .build();
137 127

138 128
    let mut event_pump = sdl_context.event_pump()?;
139 129
    let mut i = 0;
140 130
    'running: loop {
141 131
        // Handle events
142 132
        for event in event_pump.poll_iter() {
143 133
            match event {
144 134
                Event::Quit {..} |
145 135
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
146 136
                    break 'running;
147 137
                },
148 138
                Event::KeyDown { keycode: Some(Keycode::Left), repeat: false, .. } => {
149 139
                    player.speed = PLAYER_MOVEMENT_SPEED;
150 140
                    player.direction = Direction::Left;
151 141
                },
152 142
                Event::KeyDown { keycode: Some(Keycode::Right), repeat: false, .. } => {
153 143
                    player.speed = PLAYER_MOVEMENT_SPEED;
154 144
                    player.direction = Direction::Right;
155 145
                },
156 146
                Event::KeyDown { keycode: Some(Keycode::Up), repeat: false, .. } => {
157 147
                    player.speed = PLAYER_MOVEMENT_SPEED;
158 148
                    player.direction = Direction::Up;
159 149
                },
160 150
                Event::KeyDown { keycode: Some(Keycode::Down), repeat: false, .. } => {
161 151
                    player.speed = PLAYER_MOVEMENT_SPEED;
162 152
                    player.direction = Direction::Down;
163 153
                },
164 154
                Event::KeyUp { keycode: Some(Keycode::Left), repeat: false, .. } |
165 155
                Event::KeyUp { keycode: Some(Keycode::Right), repeat: false, .. } |
166 156
                Event::KeyUp { keycode: Some(Keycode::Up), repeat: false, .. } |
167 157
                Event::KeyUp { keycode: Some(Keycode::Down), repeat: false, .. } => {
168 158
                    player.speed = 0;
169 159
                },
170 160
                _ => {}
171 161
            }
172 162
        }
173 163

174 164
        // Update
175 165
        i = (i + 1) % 255;
176  
        update_player(&mut player);
  166
        //TODO: Do with specs!
177 167

178 168
        // Render
179 169
        render(&mut canvas, Color::RGB(i, 64, 255 - i), &texture, &player)?;
180 170

181 171
        // Time management!
182 172
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
183 173
    }
184 174

185 175
    Ok(())
186 176
}
+34 -0src/animator.rs
    1
use specs::prelude::*;
    2

    3
use crate::components::*;
    4

    5
struct Animator;
    6

    7
impl<'a> System<'a> for Animator {
    8
    type SystemData = (
    9
        WriteStorage<'a, MovementAnimation>,
    10
        WriteStorage<'a, Sprite>,
    11
        ReadStorage<'a, Velocity>,
    12
    );
    13

    14
    fn run(&mut self, mut data: Self::SystemData) {
    15
        use self::Direction::*;
    16
        //TODO: This code can be made nicer and more idiomatic using more pattern matching.
    17
        // Look up "rust irrefutable patterns" and use them here.
    18
        for (anim, sprite, vel) in (&mut data.0, &mut data.1, &data.2).join() {
    19
            if vel.speed == 0 {
    20
                continue;
    21
            }
    22

    23
            let frames = match vel.direction {
    24
                Left => &anim.left_frames,
    25
                Right => &anim.right_frames,
    26
                Up => &anim.up_frames,
    27
                Down => &anim.down_frames,
    28
            };
    29

    30
            anim.current_frame = (anim.current_frame + 1) % frames.len();
    31
            *sprite = frames[anim.current_frame].clone();
    32
        }
    33
    }
    34
}
+2 -12src/main.rs
1
mod components;
1
mod components;
2
mod physics;
2
mod physics;
    3
mod animator;
3

4

4
use sdl2::pixels::Color;
5
use sdl2::pixels::Color;
5
use sdl2::event::Event;
6
use sdl2::event::Event;
6
use sdl2::keyboard::Keycode;
7
use sdl2::keyboard::Keycode;
7
use sdl2::render::{WindowCanvas, Texture};
8
use sdl2::render::{WindowCanvas, Texture};
8
use sdl2::rect::{Point, Rect};
9
use sdl2::rect::{Point, Rect};
9
// "self" imports the "image" module itself as well as everything else we listed
10
// "self" imports the "image" module itself as well as everything else we listed
10
use sdl2::image::{self, LoadTexture, InitFlag};
11
use sdl2::image::{self, LoadTexture, InitFlag};
11

12

12
use specs::prelude::*;
13
use specs::prelude::*;
13

14

14
use std::time::Duration;
15
use std::time::Duration;
15

16

16
use crate::components::*;
17
use crate::components::*;
17

18

18
const PLAYER_MOVEMENT_SPEED: i32 = 20;
19
const PLAYER_MOVEMENT_SPEED: i32 = 20;
19

20

20
/// Returns the row of the spritesheet corresponding to the given direction
21
/// Returns the row of the spritesheet corresponding to the given direction
21
fn direction_spritesheet_row(direction: Direction) -> i32 {
22
fn direction_spritesheet_row(direction: Direction) -> i32 {
22
    use self::Direction::*;
23
    use self::Direction::*;
23
    match direction {
24
    match direction {
24
        Up => 3,
25
        Up => 3,
25
        Down => 0,
26
        Down => 0,
26
        Left => 1,
27
        Left => 1,
27
        Right => 2,
28
        Right => 2,
28
    }
29
    }
29
}
30
}
30

31

31
/// Create animation frames for the standard character spritesheet
32
/// Create animation frames for the standard character spritesheet
32
fn character_animation_frames(spritesheet: usize, top_left_frame: Rect, direction: Direction) -> Vec<Sprite> {
33
fn character_animation_frames(spritesheet: usize, top_left_frame: Rect, direction: Direction) -> Vec<Sprite> {
33
    // All assumptions about the spritesheets are now encapsulated in this function instead of in
34
    // All assumptions about the spritesheets are now encapsulated in this function instead of in
34
    // the design of our entire system. We can always replace this function, but replacing the
35
    // the design of our entire system. We can always replace this function, but replacing the
35
    // entire system is harder.
36
    // entire system is harder.
36

37

37
    let (frame_width, frame_height) = top_left_frame.size();
38
    let (frame_width, frame_height) = top_left_frame.size();
38
    let y_offset = top_left_frame.y() + frame_height as i32 * direction_spritesheet_row(direction);
39
    let y_offset = top_left_frame.y() + frame_height as i32 * direction_spritesheet_row(direction);
39

40

40
    let mut frames = Vec::new();
41
    let mut frames = Vec::new();
41
    for i in 0..3 {
42
    for i in 0..3 {
42
        frames.push(Sprite {
43
        frames.push(Sprite {
43
            spritesheet,
44
            spritesheet,
44
            region: Rect::new(
45
            region: Rect::new(
45
                top_left_frame.x() + frame_width as i32 * i,
46
                top_left_frame.x() + frame_width as i32 * i,
46
                y_offset,
47
                y_offset,
47
                frame_width,
48
                frame_width,
48
                frame_height,
49
                frame_height,
49
            ),
50
            ),
50
        })
51
        })
51
    }
52
    }
52

53

53
    frames
54
    frames
54
}
55
}
55

56

56
fn render(
57
fn render(
57
    canvas: &mut WindowCanvas,
58
    canvas: &mut WindowCanvas,
58
    color: Color,
59
    color: Color,
59
    texture: &Texture,
60
    texture: &Texture,
60
    player: &Player,
61
    player: &Player,
61
) -> Result<(), String> {
62
) -> Result<(), String> {
62
    canvas.set_draw_color(color);
63
    canvas.set_draw_color(color);
63
    canvas.clear();
64
    canvas.clear();
64

65

65
    let (width, height) = canvas.output_size()?;
66
    let (width, height) = canvas.output_size()?;
66

67

67
    let (frame_width, frame_height) = player.sprite.size();
68
    let (frame_width, frame_height) = player.sprite.size();
68
    let current_frame = Rect::new(
69
    let current_frame = Rect::new(
69
        player.sprite.x() + frame_width as i32 * player.current_frame,
70
        player.sprite.x() + frame_width as i32 * player.current_frame,
70
        player.sprite.y() + frame_height as  i32 * direction_spritesheet_row(player.direction),
71
        player.sprite.y() + frame_height as  i32 * direction_spritesheet_row(player.direction),
71
        frame_width,
72
        frame_width,
72
        frame_height,
73
        frame_height,
73
    );
74
    );
74

75

75
    // Treat the center of the screen as the (0, 0) coordinate
76
    // Treat the center of the screen as the (0, 0) coordinate
76
    let screen_position = player.position + Point::new(width as i32 / 2, height as i32 / 2);
77
    let screen_position = player.position + Point::new(width as i32 / 2, height as i32 / 2);
77
    let screen_rect = Rect::from_center(screen_position, frame_width, frame_height);
78
    let screen_rect = Rect::from_center(screen_position, frame_width, frame_height);
78
    canvas.copy(texture, current_frame, screen_rect)?;
79
    canvas.copy(texture, current_frame, screen_rect)?;
79

80

80
    canvas.present();
81
    canvas.present();
81

82

82
    Ok(())
83
    Ok(())
83
}
84
}
84

85

85
// Update player a fixed amount based on their speed.
   
86
// WARNING: Calling this function too often or at a variable speed will cause the player's speed
   
87
// to be unpredictable!
   
88
fn update_player(player: &mut Player) {
   
89
    // Only continue to animate if the player is moving
   
90
    if player.speed != 0 {
   
91
        // Cheat: using the fact that all animations are 3 frames (NOT extensible)
   
92
        player.current_frame = (player.current_frame + 1) % 3;
   
93
    }
   
94
}
   
95

   
96
fn main() -> Result<(), String> {
86
fn main() -> Result<(), String> {
97
    let sdl_context = sdl2::init()?;
87
    let sdl_context = sdl2::init()?;
98
    let video_subsystem = sdl_context.video()?;
88
    let video_subsystem = sdl_context.video()?;
99
    // Leading "_" tells Rust that this is an unused variable that we don't care about. It has to
89
    // Leading "_" tells Rust that this is an unused variable that we don't care about. It has to
100
    // stay unused because if we don't have any variable at all then Rust will treat it as a
90
    // stay unused because if we don't have any variable at all then Rust will treat it as a
101
    // temporary value and drop it right away!
91
    // temporary value and drop it right away!
102
    let _image_context = image::init(InitFlag::PNG | InitFlag::JPG)?;
92
    let _image_context = image::init(InitFlag::PNG | InitFlag::JPG)?;
103

93

104
    let window = video_subsystem.window("game tutorial", 800, 600)
94
    let window = video_subsystem.window("game tutorial", 800, 600)
105
        .position_centered()
95
        .position_centered()
106
        .build()
96
        .build()
107
        .expect("could not initialize video subsystem");
97
        .expect("could not initialize video subsystem");
108

98

109
    let mut canvas = window.into_canvas().build()
99
    let mut canvas = window.into_canvas().build()
110
        .expect("could not make a canvas");
100
        .expect("could not make a canvas");
111

101

112
    let texture_creator = canvas.texture_creator();
102
    let texture_creator = canvas.texture_creator();
113

103

114
    let textures = [
104
    let textures = [
115
        texture_creator.load_texture("assets/bardo.png")?,
105
        texture_creator.load_texture("assets/bardo.png")?,
116
    ];
106
    ];
117
    // First texture in textures array
107
    // First texture in textures array
118
    let player_spritesheet = 0;
108
    let player_spritesheet = 0;
119
    let player_top_left_frame = Rect::new(0, 0, 26, 36);
109
    let player_top_left_frame = Rect::new(0, 0, 26, 36);
120

110

121
    let player_animation = MovementAnimation {
111
    let player_animation = MovementAnimation {
122
        current_frame: 0,
112
        current_frame: 0,
123
        up_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Up),
113
        up_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Up),
124
        down_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Down),
114
        down_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Down),
125
        left_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Left),
115
        left_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Left),
126
        right_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Right),
116
        right_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Right),
127
    };
117
    };
128

118

129
    let mut world = World::new();
119
    let mut world = World::new();
130

120

131
    world.create_entity()
121
    world.create_entity()
132
        .with(Position(Point::new(0, 0)))
122
        .with(Position(Point::new(0, 0)))
133
        .with(Velocity {speed: 0, direction: Direction::Right})
123
        .with(Velocity {speed: 0, direction: Direction::Right})
134
        .with(player_animation.right_frames[0].clone())
124
        .with(player_animation.right_frames[0].clone())
135
        .with(player_animation)
125
        .with(player_animation)
136
        .build();
126
        .build();
137

127

138
    let mut event_pump = sdl_context.event_pump()?;
128
    let mut event_pump = sdl_context.event_pump()?;
139
    let mut i = 0;
129
    let mut i = 0;
140
    'running: loop {
130
    'running: loop {
141
        // Handle events
131
        // Handle events
142
        for event in event_pump.poll_iter() {
132
        for event in event_pump.poll_iter() {
143
            match event {
133
            match event {
144
                Event::Quit {..} |
134
                Event::Quit {..} |
145
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
135
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
146
                    break 'running;
136
                    break 'running;
147
                },
137
                },
148
                Event::KeyDown { keycode: Some(Keycode::Left), repeat: false, .. } => {
138
                Event::KeyDown { keycode: Some(Keycode::Left), repeat: false, .. } => {
149
                    player.speed = PLAYER_MOVEMENT_SPEED;
139
                    player.speed = PLAYER_MOVEMENT_SPEED;
150
                    player.direction = Direction::Left;
140
                    player.direction = Direction::Left;
151
                },
141
                },
152
                Event::KeyDown { keycode: Some(Keycode::Right), repeat: false, .. } => {
142
                Event::KeyDown { keycode: Some(Keycode::Right), repeat: false, .. } => {
153
                    player.speed = PLAYER_MOVEMENT_SPEED;
143
                    player.speed = PLAYER_MOVEMENT_SPEED;
154
                    player.direction = Direction::Right;
144
                    player.direction = Direction::Right;
155
                },
145
                },
156
                Event::KeyDown { keycode: Some(Keycode::Up), repeat: false, .. } => {
146
                Event::KeyDown { keycode: Some(Keycode::Up), repeat: false, .. } => {
157
                    player.speed = PLAYER_MOVEMENT_SPEED;
147
                    player.speed = PLAYER_MOVEMENT_SPEED;
158
                    player.direction = Direction::Up;
148
                    player.direction = Direction::Up;
159
                },
149
                },
160
                Event::KeyDown { keycode: Some(Keycode::Down), repeat: false, .. } => {
150
                Event::KeyDown { keycode: Some(Keycode::Down), repeat: false, .. } => {
161
                    player.speed = PLAYER_MOVEMENT_SPEED;
151
                    player.speed = PLAYER_MOVEMENT_SPEED;
162
                    player.direction = Direction::Down;
152
                    player.direction = Direction::Down;
163
                },
153
                },
164
                Event::KeyUp { keycode: Some(Keycode::Left), repeat: false, .. } |
154
                Event::KeyUp { keycode: Some(Keycode::Left), repeat: false, .. } |
165
                Event::KeyUp { keycode: Some(Keycode::Right), repeat: false, .. } |
155
                Event::KeyUp { keycode: Some(Keycode::Right), repeat: false, .. } |
166
                Event::KeyUp { keycode: Some(Keycode::Up), repeat: false, .. } |
156
                Event::KeyUp { keycode: Some(Keycode::Up), repeat: false, .. } |
167
                Event::KeyUp { keycode: Some(Keycode::Down), repeat: false, .. } => {
157
                Event::KeyUp { keycode: Some(Keycode::Down), repeat: false, .. } => {
168
                    player.speed = 0;
158
                    player.speed = 0;
169
                },
159
                },
170
                _ => {}
160
                _ => {}
171
            }
161
            }
172
        }
162
        }
173

163

174
        // Update
164
        // Update
175
        i = (i + 1) % 255;
165
        i = (i + 1) % 255;
176
        update_player(&mut player);
166
        //TODO: Do with specs!
177

167

178
        // Render
168
        // Render
179
        render(&mut canvas, Color::RGB(i, 64, 255 - i), &texture, &player)?;
169
        render(&mut canvas, Color::RGB(i, 64, 255 - i), &texture, &player)?;
180

170

181
        // Time management!
171
        // Time management!
182
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
172
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
183
    }
173
    }
184

174

185
    Ok(())
175
    Ok(())
186
}
176
}
/target
**/*.rs.bk
[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 specs::prelude::*;

use crate::components::*;

struct Animator;

impl<'a> System<'a> for Animator {
    type SystemData = (
        WriteStorage<'a, MovementAnimation>,
        WriteStorage<'a, Sprite>,
        ReadStorage<'a, Velocity>,
    );

    fn run(&mut self, mut data: Self::SystemData) {
        use self::Direction::*;
        //TODO: This code can be made nicer and more idiomatic using more pattern matching.
        // Look up "rust irrefutable patterns" and use them here.
        for (anim, sprite, vel) in (&mut data.0, &mut data.1, &data.2).join() {
            if vel.speed == 0 {
                continue;
            }

            let frames = match vel.direction {
                Left => &anim.left_frames,
                Right => &anim.right_frames,
                Up => &anim.up_frames,
                Down => &anim.down_frames,
            };

            anim.current_frame = (anim.current_frame + 1) % frames.len();
            *sprite = frames[anim.current_frame].clone();
        }
    }
}
use specs::prelude::*;
use specs_derive::Component;
use sdl2::rect::{Point, Rect};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
    Up,
    Down,
    Left,
    Right,
}

/// The current position of a given entity
#[derive(Component, Debug)]
#[storage(VecStorage)]
pub struct Position(pub Point);

/// The current speed and direction of a given entity
#[derive(Component, Debug)]
#[storage(VecStorage)]
pub struct Velocity {
    pub speed: i32,
    pub direction: Direction,
}

#[derive(Component, Debug, Clone)]
#[storage(VecStorage)]
pub struct Sprite {
    /// The specific spritesheet to render from
    pub spritesheet: usize,
    /// The current region of the spritesheet to be rendered
    pub region: Rect,
}

#[derive(Component, Debug)]
#[storage(VecStorage)]
pub struct MovementAnimation {
    // The current frame in the animation of the direction this entity is moving in
    pub current_frame: usize,
    pub up_frames: Vec<Sprite>,
    pub down_frames: Vec<Sprite>,
    pub left_frames: Vec<Sprite>,
    pub right_frames: Vec<Sprite>,
}
mod components;
mod physics;
mod animator;

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 specs::prelude::*;

use std::time::Duration;

use crate::components::*;

const PLAYER_MOVEMENT_SPEED: i32 = 20;

/// 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,
    }
}

/// Create animation frames for the standard character spritesheet
fn character_animation_frames(spritesheet: usize, top_left_frame: Rect, direction: Direction) -> Vec<Sprite> {
    // All assumptions about the spritesheets are now encapsulated in this function instead of in
    // the design of our entire system. We can always replace this function, but replacing the
    // entire system is harder.

    let (frame_width, frame_height) = top_left_frame.size();
    let y_offset = top_left_frame.y() + frame_height as i32 * direction_spritesheet_row(direction);

    let mut frames = Vec::new();
    for i in 0..3 {
        frames.push(Sprite {
            spritesheet,
            region: Rect::new(
                top_left_frame.x() + frame_width as i32 * i,
                y_offset,
                frame_width,
                frame_height,
            ),
        })
    }

    frames
}

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(())
}

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 textures = [
        texture_creator.load_texture("assets/bardo.png")?,
    ];
    // First texture in textures array
    let player_spritesheet = 0;
    let player_top_left_frame = Rect::new(0, 0, 26, 36);

    let player_animation = MovementAnimation {
        current_frame: 0,
        up_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Up),
        down_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Down),
        left_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Left),
        right_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Right),
    };

    let mut world = World::new();

    world.create_entity()
        .with(Position(Point::new(0, 0)))
        .with(Velocity {speed: 0, direction: Direction::Right})
        .with(player_animation.right_frames[0].clone())
        .with(player_animation)
        .build();

    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;
        //TODO: Do with specs!

        // 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(())
}
use specs::prelude::*;

use crate::components::*;

struct Physics;

impl<'a> System<'a> for Physics {
    type SystemData = (WriteStorage<'a, Position>, ReadStorage<'a, Velocity>);

    fn run(&mut self, mut data: Self::SystemData) {
        use self::Direction::*;
        //TODO: This code can be made nicer and more idiomatic using more pattern matching.
        // Look up "rust irrefutable patterns" and use them here.
        for (pos, vel) in (&mut data.0, &data.1).join() {
            match vel.direction {
                Left => {
                    pos.0 = pos.0.offset(-vel.speed, 0);
                },
                Right => {
                    pos.0 = pos.0.offset(vel.speed, 0);
                },
                Up => {
                    pos.0 = pos.0.offset(0, -vel.speed);
                },
                Down => {
                    pos.0 = pos.0.offset(0, vel.speed);
                },
            }
        }
    }
}