• src/main.rs
+37 -0src/main.rs
1 1
use sdl2::pixels::Color;
2 2
use sdl2::event::Event;
3 3
use sdl2::keyboard::Keycode;
4 4
use sdl2::render::{WindowCanvas, Texture};
5 5
use sdl2::rect::{Point, Rect};
6 6
// "self" imports the "image" module itself as well as everything else we listed
7 7
use sdl2::image::{self, LoadTexture, InitFlag};
  8

  9
use specs::prelude::*;
  10
use specs_derive::Component;
  11

8 12
use std::time::Duration;
9 13

10 14
const PLAYER_MOVEMENT_SPEED: i32 = 20;
11 15

12 16
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13 17
enum Direction {
14 18
    Up,
15 19
    Down,
16 20
    Left,
17 21
    Right,
18 22
}
19 23

  24
/// The current position of a given entity
  25
#[derive(Component, Debug)]
  26
#[storage(VecStorage)]
  27
struct Position(Point);
  28

  29
/// The current speed and direction of a given entity
  30
#[derive(Component, Debug)]
  31
#[storage(VecStorage)]
  32
struct Velocity {
  33
    speed: i32,
  34
    direction: Direction,
  35
}
  36

  37
#[derive(Component, Debug)]
  38
#[storage(VecStorage)]
  39
struct Sprite {
  40
    /// The specific spritesheet to render from
  41
    spritesheet: usize,
  42
    /// The current region of the spritesheet to be rendered
  43
    region: Rect,
  44
}
  45

  46
#[derive(Component, Debug)]
  47
#[storage(VecStorage)]
  48
struct MovementAnimation {
  49
    // The current frame in the animation of the direction this entity is moving in
  50
    current_frame: usize,
  51
    up_frames: Vec<Sprite>,
  52
    down_frames: Vec<Sprite>,
  53
    left_frames: Vec<Sprite>,
  54
    right_frames: Vec<Sprite>,
  55
}
  56

20 57
#[derive(Debug)]
21 58
struct Player {
22 59
    position: Point,
23 60
    sprite: Rect,
24 61
    speed: i32,
25 62
    direction: Direction,
26 63
    current_frame: i32,
27 64
}
28 65

29 66
/// Returns the row of the spritesheet corresponding to the given direction
30 67
fn direction_spritesheet_row(direction: Direction) -> i32 {
31 68
    use self::Direction::*;
32 69
    match direction {
33 70
        Up => 3,
34 71
        Down => 0,
35 72
        Left => 1,
36 73
        Right => 2,
37 74
    }
38 75
}
39 76

40 77
fn render(
41 78
    canvas: &mut WindowCanvas,
42 79
    color: Color,
43 80
    texture: &Texture,
44 81
    player: &Player,
45 82
) -> Result<(), String> {
46 83
    canvas.set_draw_color(color);
47 84
    canvas.clear();
48 85

49 86
    let (width, height) = canvas.output_size()?;
50 87

51 88
    let (frame_width, frame_height) = player.sprite.size();
52 89
    let current_frame = Rect::new(
53 90
        player.sprite.x() + frame_width as i32 * player.current_frame,
54 91
        player.sprite.y() + frame_height as  i32 * direction_spritesheet_row(player.direction),
55 92
        frame_width,
56 93
        frame_height,
57 94
    );
58 95

59 96
    // Treat the center of the screen as the (0, 0) coordinate
60 97
    let screen_position = player.position + Point::new(width as i32 / 2, height as i32 / 2);
61 98
    let screen_rect = Rect::from_center(screen_position, frame_width, frame_height);
62 99
    canvas.copy(texture, current_frame, screen_rect)?;
63 100

64 101
    canvas.present();
65 102

66 103
    Ok(())
67 104
}
68 105

69 106
// Update player a fixed amount based on their speed.
70 107
// WARNING: Calling this function too often or at a variable speed will cause the player's speed
71 108
// to be unpredictable!
72 109
fn update_player(player: &mut Player) {
73 110
    use self::Direction::*;
74 111
    match player.direction {
75 112
        Left => {
76 113
            player.position = player.position.offset(-player.speed, 0);
77 114
        },
78 115
        Right => {
79 116
            player.position = player.position.offset(player.speed, 0);
80 117
        },
81 118
        Up => {
82 119
            player.position = player.position.offset(0, -player.speed);
83 120
        },
84 121
        Down => {
85 122
            player.position = player.position.offset(0, player.speed);
86 123
        },
87 124
    }
88 125

89 126
    // Only continue to animate if the player is moving
90 127
    if player.speed != 0 {
91 128
        // Cheat: using the fact that all animations are 3 frames (NOT extensible)
92 129
        player.current_frame = (player.current_frame + 1) % 3;
93 130
    }
94 131
}
95 132

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

104 141
    let window = video_subsystem.window("game tutorial", 800, 600)
105 142
        .position_centered()
106 143
        .build()
107 144
        .expect("could not initialize video subsystem");
108 145

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

112 149
    let texture_creator = canvas.texture_creator();
113 150
    let texture = texture_creator.load_texture("assets/bardo.png")?;
114 151

115 152
    let mut player = Player {
116 153
        position: Point::new(0, 0),
117 154
        sprite: Rect::new(0, 0, 26, 36),
118 155
        speed: 0,
119 156
        direction: Direction::Right,
120 157
        current_frame: 0,
121 158
    };
122 159

123 160
    let mut event_pump = sdl_context.event_pump()?;
124 161
    let mut i = 0;
125 162
    'running: loop {
126 163
        // Handle events
127 164
        for event in event_pump.poll_iter() {
128 165
            match event {
129 166
                Event::Quit {..} |
130 167
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
131 168
                    break 'running;
132 169
                },
133 170
                Event::KeyDown { keycode: Some(Keycode::Left), repeat: false, .. } => {
134 171
                    player.speed = PLAYER_MOVEMENT_SPEED;
135 172
                    player.direction = Direction::Left;
136 173
                },
137 174
                Event::KeyDown { keycode: Some(Keycode::Right), repeat: false, .. } => {
138 175
                    player.speed = PLAYER_MOVEMENT_SPEED;
139 176
                    player.direction = Direction::Right;
140 177
                },
141 178
                Event::KeyDown { keycode: Some(Keycode::Up), repeat: false, .. } => {
142 179
                    player.speed = PLAYER_MOVEMENT_SPEED;
143 180
                    player.direction = Direction::Up;
144 181
                },
145 182
                Event::KeyDown { keycode: Some(Keycode::Down), repeat: false, .. } => {
146 183
                    player.speed = PLAYER_MOVEMENT_SPEED;
147 184
                    player.direction = Direction::Down;
148 185
                },
149 186
                Event::KeyUp { keycode: Some(Keycode::Left), repeat: false, .. } |
150 187
                Event::KeyUp { keycode: Some(Keycode::Right), repeat: false, .. } |
151 188
                Event::KeyUp { keycode: Some(Keycode::Up), repeat: false, .. } |
152 189
                Event::KeyUp { keycode: Some(Keycode::Down), repeat: false, .. } => {
153 190
                    player.speed = 0;
154 191
                },
155 192
                _ => {}
156 193
            }
157 194
        }
158 195

159 196
        // Update
160 197
        i = (i + 1) % 255;
161 198
        update_player(&mut player);
162 199

163 200
        // Render
164 201
        render(&mut canvas, Color::RGB(i, 64, 255 - i), &texture, &player)?;
165 202

166 203
        // Time management!
167 204
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
168 205
    }
169 206

170 207
    Ok(())
171 208
}
+37 -0src/main.rs
1
use sdl2::pixels::Color;
1
use sdl2::pixels::Color;
2
use sdl2::event::Event;
2
use sdl2::event::Event;
3
use sdl2::keyboard::Keycode;
3
use sdl2::keyboard::Keycode;
4
use sdl2::render::{WindowCanvas, Texture};
4
use sdl2::render::{WindowCanvas, Texture};
5
use sdl2::rect::{Point, Rect};
5
use sdl2::rect::{Point, Rect};
6
// "self" imports the "image" module itself as well as everything else we listed
6
// "self" imports the "image" module itself as well as everything else we listed
7
use sdl2::image::{self, LoadTexture, InitFlag};
7
use sdl2::image::{self, LoadTexture, InitFlag};
    8

    9
use specs::prelude::*;
    10
use specs_derive::Component;
    11

8
use std::time::Duration;
12
use std::time::Duration;
9

13

10
const PLAYER_MOVEMENT_SPEED: i32 = 20;
14
const PLAYER_MOVEMENT_SPEED: i32 = 20;
11

15

12
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13
enum Direction {
17
enum Direction {
14
    Up,
18
    Up,
15
    Down,
19
    Down,
16
    Left,
20
    Left,
17
    Right,
21
    Right,
18
}
22
}
19

23

    24
/// The current position of a given entity
    25
#[derive(Component, Debug)]
    26
#[storage(VecStorage)]
    27
struct Position(Point);
    28

    29
/// The current speed and direction of a given entity
    30
#[derive(Component, Debug)]
    31
#[storage(VecStorage)]
    32
struct Velocity {
    33
    speed: i32,
    34
    direction: Direction,
    35
}
    36

    37
#[derive(Component, Debug)]
    38
#[storage(VecStorage)]
    39
struct Sprite {
    40
    /// The specific spritesheet to render from
    41
    spritesheet: usize,
    42
    /// The current region of the spritesheet to be rendered
    43
    region: Rect,
    44
}
    45

    46
#[derive(Component, Debug)]
    47
#[storage(VecStorage)]
    48
struct MovementAnimation {
    49
    // The current frame in the animation of the direction this entity is moving in
    50
    current_frame: usize,
    51
    up_frames: Vec<Sprite>,
    52
    down_frames: Vec<Sprite>,
    53
    left_frames: Vec<Sprite>,
    54
    right_frames: Vec<Sprite>,
    55
}
    56

20
#[derive(Debug)]
57
#[derive(Debug)]
21
struct Player {
58
struct Player {
22
    position: Point,
59
    position: Point,
23
    sprite: Rect,
60
    sprite: Rect,
24
    speed: i32,
61
    speed: i32,
25
    direction: Direction,
62
    direction: Direction,
26
    current_frame: i32,
63
    current_frame: i32,
27
}
64
}
28

65

29
/// Returns the row of the spritesheet corresponding to the given direction
66
/// Returns the row of the spritesheet corresponding to the given direction
30
fn direction_spritesheet_row(direction: Direction) -> i32 {
67
fn direction_spritesheet_row(direction: Direction) -> i32 {
31
    use self::Direction::*;
68
    use self::Direction::*;
32
    match direction {
69
    match direction {
33
        Up => 3,
70
        Up => 3,
34
        Down => 0,
71
        Down => 0,
35
        Left => 1,
72
        Left => 1,
36
        Right => 2,
73
        Right => 2,
37
    }
74
    }
38
}
75
}
39

76

40
fn render(
77
fn render(
41
    canvas: &mut WindowCanvas,
78
    canvas: &mut WindowCanvas,
42
    color: Color,
79
    color: Color,
43
    texture: &Texture,
80
    texture: &Texture,
44
    player: &Player,
81
    player: &Player,
45
) -> Result<(), String> {
82
) -> Result<(), String> {
46
    canvas.set_draw_color(color);
83
    canvas.set_draw_color(color);
47
    canvas.clear();
84
    canvas.clear();
48

85

49
    let (width, height) = canvas.output_size()?;
86
    let (width, height) = canvas.output_size()?;
50

87

51
    let (frame_width, frame_height) = player.sprite.size();
88
    let (frame_width, frame_height) = player.sprite.size();
52
    let current_frame = Rect::new(
89
    let current_frame = Rect::new(
53
        player.sprite.x() + frame_width as i32 * player.current_frame,
90
        player.sprite.x() + frame_width as i32 * player.current_frame,
54
        player.sprite.y() + frame_height as  i32 * direction_spritesheet_row(player.direction),
91
        player.sprite.y() + frame_height as  i32 * direction_spritesheet_row(player.direction),
55
        frame_width,
92
        frame_width,
56
        frame_height,
93
        frame_height,
57
    );
94
    );
58

95

59
    // Treat the center of the screen as the (0, 0) coordinate
96
    // Treat the center of the screen as the (0, 0) coordinate
60
    let screen_position = player.position + Point::new(width as i32 / 2, height as i32 / 2);
97
    let screen_position = player.position + Point::new(width as i32 / 2, height as i32 / 2);
61
    let screen_rect = Rect::from_center(screen_position, frame_width, frame_height);
98
    let screen_rect = Rect::from_center(screen_position, frame_width, frame_height);
62
    canvas.copy(texture, current_frame, screen_rect)?;
99
    canvas.copy(texture, current_frame, screen_rect)?;
63

100

64
    canvas.present();
101
    canvas.present();
65

102

66
    Ok(())
103
    Ok(())
67
}
104
}
68

105

69
// Update player a fixed amount based on their speed.
106
// Update player a fixed amount based on their speed.
70
// WARNING: Calling this function too often or at a variable speed will cause the player's speed
107
// WARNING: Calling this function too often or at a variable speed will cause the player's speed
71
// to be unpredictable!
108
// to be unpredictable!
72
fn update_player(player: &mut Player) {
109
fn update_player(player: &mut Player) {
73
    use self::Direction::*;
110
    use self::Direction::*;
74
    match player.direction {
111
    match player.direction {
75
        Left => {
112
        Left => {
76
            player.position = player.position.offset(-player.speed, 0);
113
            player.position = player.position.offset(-player.speed, 0);
77
        },
114
        },
78
        Right => {
115
        Right => {
79
            player.position = player.position.offset(player.speed, 0);
116
            player.position = player.position.offset(player.speed, 0);
80
        },
117
        },
81
        Up => {
118
        Up => {
82
            player.position = player.position.offset(0, -player.speed);
119
            player.position = player.position.offset(0, -player.speed);
83
        },
120
        },
84
        Down => {
121
        Down => {
85
            player.position = player.position.offset(0, player.speed);
122
            player.position = player.position.offset(0, player.speed);
86
        },
123
        },
87
    }
124
    }
88

125

89
    // Only continue to animate if the player is moving
126
    // Only continue to animate if the player is moving
90
    if player.speed != 0 {
127
    if player.speed != 0 {
91
        // Cheat: using the fact that all animations are 3 frames (NOT extensible)
128
        // Cheat: using the fact that all animations are 3 frames (NOT extensible)
92
        player.current_frame = (player.current_frame + 1) % 3;
129
        player.current_frame = (player.current_frame + 1) % 3;
93
    }
130
    }
94
}
131
}
95

132

96
fn main() -> Result<(), String> {
133
fn main() -> Result<(), String> {
97
    let sdl_context = sdl2::init()?;
134
    let sdl_context = sdl2::init()?;
98
    let video_subsystem = sdl_context.video()?;
135
    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
136
    // 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
137
    // 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!
138
    // temporary value and drop it right away!
102
    let _image_context = image::init(InitFlag::PNG | InitFlag::JPG)?;
139
    let _image_context = image::init(InitFlag::PNG | InitFlag::JPG)?;
103

140

104
    let window = video_subsystem.window("game tutorial", 800, 600)
141
    let window = video_subsystem.window("game tutorial", 800, 600)
105
        .position_centered()
142
        .position_centered()
106
        .build()
143
        .build()
107
        .expect("could not initialize video subsystem");
144
        .expect("could not initialize video subsystem");
108

145

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

148

112
    let texture_creator = canvas.texture_creator();
149
    let texture_creator = canvas.texture_creator();
113
    let texture = texture_creator.load_texture("assets/bardo.png")?;
150
    let texture = texture_creator.load_texture("assets/bardo.png")?;
114

151

115
    let mut player = Player {
152
    let mut player = Player {
116
        position: Point::new(0, 0),
153
        position: Point::new(0, 0),
117
        sprite: Rect::new(0, 0, 26, 36),
154
        sprite: Rect::new(0, 0, 26, 36),
118
        speed: 0,
155
        speed: 0,
119
        direction: Direction::Right,
156
        direction: Direction::Right,
120
        current_frame: 0,
157
        current_frame: 0,
121
    };
158
    };
122

159

123
    let mut event_pump = sdl_context.event_pump()?;
160
    let mut event_pump = sdl_context.event_pump()?;
124
    let mut i = 0;
161
    let mut i = 0;
125
    'running: loop {
162
    'running: loop {
126
        // Handle events
163
        // Handle events
127
        for event in event_pump.poll_iter() {
164
        for event in event_pump.poll_iter() {
128
            match event {
165
            match event {
129
                Event::Quit {..} |
166
                Event::Quit {..} |
130
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
167
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
131
                    break 'running;
168
                    break 'running;
132
                },
169
                },
133
                Event::KeyDown { keycode: Some(Keycode::Left), repeat: false, .. } => {
170
                Event::KeyDown { keycode: Some(Keycode::Left), repeat: false, .. } => {
134
                    player.speed = PLAYER_MOVEMENT_SPEED;
171
                    player.speed = PLAYER_MOVEMENT_SPEED;
135
                    player.direction = Direction::Left;
172
                    player.direction = Direction::Left;
136
                },
173
                },
137
                Event::KeyDown { keycode: Some(Keycode::Right), repeat: false, .. } => {
174
                Event::KeyDown { keycode: Some(Keycode::Right), repeat: false, .. } => {
138
                    player.speed = PLAYER_MOVEMENT_SPEED;
175
                    player.speed = PLAYER_MOVEMENT_SPEED;
139
                    player.direction = Direction::Right;
176
                    player.direction = Direction::Right;
140
                },
177
                },
141
                Event::KeyDown { keycode: Some(Keycode::Up), repeat: false, .. } => {
178
                Event::KeyDown { keycode: Some(Keycode::Up), repeat: false, .. } => {
142
                    player.speed = PLAYER_MOVEMENT_SPEED;
179
                    player.speed = PLAYER_MOVEMENT_SPEED;
143
                    player.direction = Direction::Up;
180
                    player.direction = Direction::Up;
144
                },
181
                },
145
                Event::KeyDown { keycode: Some(Keycode::Down), repeat: false, .. } => {
182
                Event::KeyDown { keycode: Some(Keycode::Down), repeat: false, .. } => {
146
                    player.speed = PLAYER_MOVEMENT_SPEED;
183
                    player.speed = PLAYER_MOVEMENT_SPEED;
147
                    player.direction = Direction::Down;
184
                    player.direction = Direction::Down;
148
                },
185
                },
149
                Event::KeyUp { keycode: Some(Keycode::Left), repeat: false, .. } |
186
                Event::KeyUp { keycode: Some(Keycode::Left), repeat: false, .. } |
150
                Event::KeyUp { keycode: Some(Keycode::Right), repeat: false, .. } |
187
                Event::KeyUp { keycode: Some(Keycode::Right), repeat: false, .. } |
151
                Event::KeyUp { keycode: Some(Keycode::Up), repeat: false, .. } |
188
                Event::KeyUp { keycode: Some(Keycode::Up), repeat: false, .. } |
152
                Event::KeyUp { keycode: Some(Keycode::Down), repeat: false, .. } => {
189
                Event::KeyUp { keycode: Some(Keycode::Down), repeat: false, .. } => {
153
                    player.speed = 0;
190
                    player.speed = 0;
154
                },
191
                },
155
                _ => {}
192
                _ => {}
156
            }
193
            }
157
        }
194
        }
158

195

159
        // Update
196
        // Update
160
        i = (i + 1) % 255;
197
        i = (i + 1) % 255;
161
        update_player(&mut player);
198
        update_player(&mut player);
162

199

163
        // Render
200
        // Render
164
        render(&mut canvas, Color::RGB(i, 64, 255 - i), &texture, &player)?;
201
        render(&mut canvas, Color::RGB(i, 64, 255 - i), &texture, &player)?;
165

202

166
        // Time management!
203
        // Time management!
167
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
204
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
168
    }
205
    }
169

206

170
    Ok(())
207
    Ok(())
171
}
208
}
/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 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 specs_derive::Component;

use std::time::Duration;

const PLAYER_MOVEMENT_SPEED: i32 = 20;

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

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

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

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

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

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