• src/main.rs
  • src/components.rs
+44 -0src/components.rs
  1
use specs::prelude::*;
  2
use specs_derive::Component;
  3
use sdl2::rect::{Point, Rect};
  4

  5
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
  6
pub enum Direction {
  7
    Up,
  8
    Down,
  9
    Left,
  10
    Right,
  11
}
  12

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

  18
/// The current speed and direction of a given entity
  19
#[derive(Component, Debug)]
  20
#[storage(VecStorage)]
  21
pub struct Velocity {
  22
    pub speed: i32,
  23
    pub direction: Direction,
  24
}
  25

  26
#[derive(Component, Debug, Clone)]
  27
#[storage(VecStorage)]
  28
pub struct Sprite {
  29
    /// The specific spritesheet to render from
  30
    pub spritesheet: usize,
  31
    /// The current region of the spritesheet to be rendered
  32
    pub region: Rect,
  33
}
  34

  35
#[derive(Component, Debug)]
  36
#[storage(VecStorage)]
  37
pub struct MovementAnimation {
  38
    // The current frame in the animation of the direction this entity is moving in
  39
    pub current_frame: usize,
  40
    pub up_frames: Vec<Sprite>,
  41
    pub down_frames: Vec<Sprite>,
  42
    pub left_frames: Vec<Sprite>,
  43
    pub right_frames: Vec<Sprite>,
  44
}
+4 -42src/main.rs
  1
mod components;
  2

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

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

12 13
use std::time::Duration;
13 14

  15
use crate::components::*;
  16

14 17
const PLAYER_MOVEMENT_SPEED: i32 = 20;
15 18

16  
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17  
enum Direction {
18  
    Up,
19  
    Down,
20  
    Left,
21  
    Right,
22  
}
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, Clone)]
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  

57 19
/// Returns the row of the spritesheet corresponding to the given direction
58 20
fn direction_spritesheet_row(direction: Direction) -> i32 {
59 21
    use self::Direction::*;
60 22
    match direction {
61 23
        Up => 3,
62 24
        Down => 0,
63 25
        Left => 1,
64 26
        Right => 2,
65 27
    }
66 28
}
67 29

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

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

77 39
    let mut frames = Vec::new();
78 40
    for i in 0..3 {
79 41
        frames.push(Sprite {
80 42
            spritesheet,
81 43
            region: Rect::new(
82 44
                top_left_frame.x() + frame_width as i32 * i,
83 45
                y_offset,
84 46
                frame_width,
85 47
                frame_height,
86 48
            ),
87 49
        })
88 50
    }
89 51

90 52
    frames
91 53
}
92 54

93 55
fn render(
94 56
    canvas: &mut WindowCanvas,
95 57
    color: Color,
96 58
    texture: &Texture,
97 59
    player: &Player,
98 60
) -> Result<(), String> {
99 61
    canvas.set_draw_color(color);
100 62
    canvas.clear();
101 63

102 64
    let (width, height) = canvas.output_size()?;
103 65

104 66
    let (frame_width, frame_height) = player.sprite.size();
105 67
    let current_frame = Rect::new(
106 68
        player.sprite.x() + frame_width as i32 * player.current_frame,
107 69
        player.sprite.y() + frame_height as  i32 * direction_spritesheet_row(player.direction),
108 70
        frame_width,
109 71
        frame_height,
110 72
    );
111 73

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

117 79
    canvas.present();
118 80

119 81
    Ok(())
120 82
}
121 83

122 84
// Update player a fixed amount based on their speed.
123 85
// WARNING: Calling this function too often or at a variable speed will cause the player's speed
124 86
// to be unpredictable!
125 87
fn update_player(player: &mut Player) {
126 88
    use self::Direction::*;
127 89
    match player.direction {
128 90
        Left => {
129 91
            player.position = player.position.offset(-player.speed, 0);
130 92
        },
131 93
        Right => {
132 94
            player.position = player.position.offset(player.speed, 0);
133 95
        },
134 96
        Up => {
135 97
            player.position = player.position.offset(0, -player.speed);
136 98
        },
137 99
        Down => {
138 100
            player.position = player.position.offset(0, player.speed);
139 101
        },
140 102
    }
141 103

142 104
    // Only continue to animate if the player is moving
143 105
    if player.speed != 0 {
144 106
        // Cheat: using the fact that all animations are 3 frames (NOT extensible)
145 107
        player.current_frame = (player.current_frame + 1) % 3;
146 108
    }
147 109
}
148 110

149 111
fn main() -> Result<(), String> {
150 112
    let sdl_context = sdl2::init()?;
151 113
    let video_subsystem = sdl_context.video()?;
152 114
    // Leading "_" tells Rust that this is an unused variable that we don't care about. It has to
153 115
    // stay unused because if we don't have any variable at all then Rust will treat it as a
154 116
    // temporary value and drop it right away!
155 117
    let _image_context = image::init(InitFlag::PNG | InitFlag::JPG)?;
156 118

157 119
    let window = video_subsystem.window("game tutorial", 800, 600)
158 120
        .position_centered()
159 121
        .build()
160 122
        .expect("could not initialize video subsystem");
161 123

162 124
    let mut canvas = window.into_canvas().build()
163 125
        .expect("could not make a canvas");
164 126

165 127
    let texture_creator = canvas.texture_creator();
166 128

167 129
    let textures = [
168 130
        texture_creator.load_texture("assets/bardo.png")?,
169 131
    ];
170 132
    // First texture in textures array
171 133
    let player_spritesheet = 0;
172 134
    let player_top_left_frame = Rect::new(0, 0, 26, 36);
173 135

174 136
    let player_animation = MovementAnimation {
175 137
        current_frame: 0,
176 138
        up_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Up),
177 139
        down_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Down),
178 140
        left_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Left),
179 141
        right_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Right),
180 142
    };
181 143

182 144
    let mut world = World::new();
183 145

184 146
    world.create_entity()
185 147
        .with(Position(Point::new(0, 0)))
186 148
        .with(Velocity {speed: 0, direction: Direction::Right})
187 149
        .with(player_animation.right_frames[0].clone())
188 150
        .with(player_animation)
189 151
        .build();
190 152

191 153
    let mut event_pump = sdl_context.event_pump()?;
192 154
    let mut i = 0;
193 155
    'running: loop {
194 156
        // Handle events
195 157
        for event in event_pump.poll_iter() {
196 158
            match event {
197 159
                Event::Quit {..} |
198 160
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
199 161
                    break 'running;
200 162
                },
201 163
                Event::KeyDown { keycode: Some(Keycode::Left), repeat: false, .. } => {
202 164
                    player.speed = PLAYER_MOVEMENT_SPEED;
203 165
                    player.direction = Direction::Left;
204 166
                },
205 167
                Event::KeyDown { keycode: Some(Keycode::Right), repeat: false, .. } => {
206 168
                    player.speed = PLAYER_MOVEMENT_SPEED;
207 169
                    player.direction = Direction::Right;
208 170
                },
209 171
                Event::KeyDown { keycode: Some(Keycode::Up), repeat: false, .. } => {
210 172
                    player.speed = PLAYER_MOVEMENT_SPEED;
211 173
                    player.direction = Direction::Up;
212 174
                },
213 175
                Event::KeyDown { keycode: Some(Keycode::Down), repeat: false, .. } => {
214 176
                    player.speed = PLAYER_MOVEMENT_SPEED;
215 177
                    player.direction = Direction::Down;
216 178
                },
217 179
                Event::KeyUp { keycode: Some(Keycode::Left), repeat: false, .. } |
218 180
                Event::KeyUp { keycode: Some(Keycode::Right), repeat: false, .. } |
219 181
                Event::KeyUp { keycode: Some(Keycode::Up), repeat: false, .. } |
220 182
                Event::KeyUp { keycode: Some(Keycode::Down), repeat: false, .. } => {
221 183
                    player.speed = 0;
222 184
                },
223 185
                _ => {}
224 186
            }
225 187
        }
226 188

227 189
        // Update
228 190
        i = (i + 1) % 255;
229 191
        update_player(&mut player);
230 192

231 193
        // Render
232 194
        render(&mut canvas, Color::RGB(i, 64, 255 - i), &texture, &player)?;
233 195

234 196
        // Time management!
235 197
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
236 198
    }
237 199

238 200
    Ok(())
239 201
}
+44 -0src/components.rs
    1
use specs::prelude::*;
    2
use specs_derive::Component;
    3
use sdl2::rect::{Point, Rect};
    4

    5
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
    6
pub enum Direction {
    7
    Up,
    8
    Down,
    9
    Left,
    10
    Right,
    11
}
    12

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

    18
/// The current speed and direction of a given entity
    19
#[derive(Component, Debug)]
    20
#[storage(VecStorage)]
    21
pub struct Velocity {
    22
    pub speed: i32,
    23
    pub direction: Direction,
    24
}
    25

    26
#[derive(Component, Debug, Clone)]
    27
#[storage(VecStorage)]
    28
pub struct Sprite {
    29
    /// The specific spritesheet to render from
    30
    pub spritesheet: usize,
    31
    /// The current region of the spritesheet to be rendered
    32
    pub region: Rect,
    33
}
    34

    35
#[derive(Component, Debug)]
    36
#[storage(VecStorage)]
    37
pub struct MovementAnimation {
    38
    // The current frame in the animation of the direction this entity is moving in
    39
    pub current_frame: usize,
    40
    pub up_frames: Vec<Sprite>,
    41
    pub down_frames: Vec<Sprite>,
    42
    pub left_frames: Vec<Sprite>,
    43
    pub right_frames: Vec<Sprite>,
    44
}
+4 -42src/main.rs
    1
mod components;
    2

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

10

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

12

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

14

    15
use crate::components::*;
    16

14
const PLAYER_MOVEMENT_SPEED: i32 = 20;
17
const PLAYER_MOVEMENT_SPEED: i32 = 20;
15

18

16
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
   
17
enum Direction {
   
18
    Up,
   
19
    Down,
   
20
    Left,
   
21
    Right,
   
22
}
   
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, Clone)]
   
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

   
57
/// Returns the row of the spritesheet corresponding to the given direction
19
/// Returns the row of the spritesheet corresponding to the given direction
58
fn direction_spritesheet_row(direction: Direction) -> i32 {
20
fn direction_spritesheet_row(direction: Direction) -> i32 {
59
    use self::Direction::*;
21
    use self::Direction::*;
60
    match direction {
22
    match direction {
61
        Up => 3,
23
        Up => 3,
62
        Down => 0,
24
        Down => 0,
63
        Left => 1,
25
        Left => 1,
64
        Right => 2,
26
        Right => 2,
65
    }
27
    }
66
}
28
}
67

29

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

35

74
    let (frame_width, frame_height) = top_left_frame.size();
36
    let (frame_width, frame_height) = top_left_frame.size();
75
    let y_offset = top_left_frame.y() + frame_height as i32 * direction_spritesheet_row(direction);
37
    let y_offset = top_left_frame.y() + frame_height as i32 * direction_spritesheet_row(direction);
76

38

77
    let mut frames = Vec::new();
39
    let mut frames = Vec::new();
78
    for i in 0..3 {
40
    for i in 0..3 {
79
        frames.push(Sprite {
41
        frames.push(Sprite {
80
            spritesheet,
42
            spritesheet,
81
            region: Rect::new(
43
            region: Rect::new(
82
                top_left_frame.x() + frame_width as i32 * i,
44
                top_left_frame.x() + frame_width as i32 * i,
83
                y_offset,
45
                y_offset,
84
                frame_width,
46
                frame_width,
85
                frame_height,
47
                frame_height,
86
            ),
48
            ),
87
        })
49
        })
88
    }
50
    }
89

51

90
    frames
52
    frames
91
}
53
}
92

54

93
fn render(
55
fn render(
94
    canvas: &mut WindowCanvas,
56
    canvas: &mut WindowCanvas,
95
    color: Color,
57
    color: Color,
96
    texture: &Texture,
58
    texture: &Texture,
97
    player: &Player,
59
    player: &Player,
98
) -> Result<(), String> {
60
) -> Result<(), String> {
99
    canvas.set_draw_color(color);
61
    canvas.set_draw_color(color);
100
    canvas.clear();
62
    canvas.clear();
101

63

102
    let (width, height) = canvas.output_size()?;
64
    let (width, height) = canvas.output_size()?;
103

65

104
    let (frame_width, frame_height) = player.sprite.size();
66
    let (frame_width, frame_height) = player.sprite.size();
105
    let current_frame = Rect::new(
67
    let current_frame = Rect::new(
106
        player.sprite.x() + frame_width as i32 * player.current_frame,
68
        player.sprite.x() + frame_width as i32 * player.current_frame,
107
        player.sprite.y() + frame_height as  i32 * direction_spritesheet_row(player.direction),
69
        player.sprite.y() + frame_height as  i32 * direction_spritesheet_row(player.direction),
108
        frame_width,
70
        frame_width,
109
        frame_height,
71
        frame_height,
110
    );
72
    );
111

73

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

78

117
    canvas.present();
79
    canvas.present();
118

80

119
    Ok(())
81
    Ok(())
120
}
82
}
121

83

122
// Update player a fixed amount based on their speed.
84
// Update player a fixed amount based on their speed.
123
// WARNING: Calling this function too often or at a variable speed will cause the player's speed
85
// WARNING: Calling this function too often or at a variable speed will cause the player's speed
124
// to be unpredictable!
86
// to be unpredictable!
125
fn update_player(player: &mut Player) {
87
fn update_player(player: &mut Player) {
126
    use self::Direction::*;
88
    use self::Direction::*;
127
    match player.direction {
89
    match player.direction {
128
        Left => {
90
        Left => {
129
            player.position = player.position.offset(-player.speed, 0);
91
            player.position = player.position.offset(-player.speed, 0);
130
        },
92
        },
131
        Right => {
93
        Right => {
132
            player.position = player.position.offset(player.speed, 0);
94
            player.position = player.position.offset(player.speed, 0);
133
        },
95
        },
134
        Up => {
96
        Up => {
135
            player.position = player.position.offset(0, -player.speed);
97
            player.position = player.position.offset(0, -player.speed);
136
        },
98
        },
137
        Down => {
99
        Down => {
138
            player.position = player.position.offset(0, player.speed);
100
            player.position = player.position.offset(0, player.speed);
139
        },
101
        },
140
    }
102
    }
141

103

142
    // Only continue to animate if the player is moving
104
    // Only continue to animate if the player is moving
143
    if player.speed != 0 {
105
    if player.speed != 0 {
144
        // Cheat: using the fact that all animations are 3 frames (NOT extensible)
106
        // Cheat: using the fact that all animations are 3 frames (NOT extensible)
145
        player.current_frame = (player.current_frame + 1) % 3;
107
        player.current_frame = (player.current_frame + 1) % 3;
146
    }
108
    }
147
}
109
}
148

110

149
fn main() -> Result<(), String> {
111
fn main() -> Result<(), String> {
150
    let sdl_context = sdl2::init()?;
112
    let sdl_context = sdl2::init()?;
151
    let video_subsystem = sdl_context.video()?;
113
    let video_subsystem = sdl_context.video()?;
152
    // Leading "_" tells Rust that this is an unused variable that we don't care about. It has to
114
    // Leading "_" tells Rust that this is an unused variable that we don't care about. It has to
153
    // stay unused because if we don't have any variable at all then Rust will treat it as a
115
    // stay unused because if we don't have any variable at all then Rust will treat it as a
154
    // temporary value and drop it right away!
116
    // temporary value and drop it right away!
155
    let _image_context = image::init(InitFlag::PNG | InitFlag::JPG)?;
117
    let _image_context = image::init(InitFlag::PNG | InitFlag::JPG)?;
156

118

157
    let window = video_subsystem.window("game tutorial", 800, 600)
119
    let window = video_subsystem.window("game tutorial", 800, 600)
158
        .position_centered()
120
        .position_centered()
159
        .build()
121
        .build()
160
        .expect("could not initialize video subsystem");
122
        .expect("could not initialize video subsystem");
161

123

162
    let mut canvas = window.into_canvas().build()
124
    let mut canvas = window.into_canvas().build()
163
        .expect("could not make a canvas");
125
        .expect("could not make a canvas");
164

126

165
    let texture_creator = canvas.texture_creator();
127
    let texture_creator = canvas.texture_creator();
166

128

167
    let textures = [
129
    let textures = [
168
        texture_creator.load_texture("assets/bardo.png")?,
130
        texture_creator.load_texture("assets/bardo.png")?,
169
    ];
131
    ];
170
    // First texture in textures array
132
    // First texture in textures array
171
    let player_spritesheet = 0;
133
    let player_spritesheet = 0;
172
    let player_top_left_frame = Rect::new(0, 0, 26, 36);
134
    let player_top_left_frame = Rect::new(0, 0, 26, 36);
173

135

174
    let player_animation = MovementAnimation {
136
    let player_animation = MovementAnimation {
175
        current_frame: 0,
137
        current_frame: 0,
176
        up_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Up),
138
        up_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Up),
177
        down_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Down),
139
        down_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Down),
178
        left_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Left),
140
        left_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Left),
179
        right_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Right),
141
        right_frames: character_animation_frames(player_spritesheet, player_top_left_frame, Direction::Right),
180
    };
142
    };
181

143

182
    let mut world = World::new();
144
    let mut world = World::new();
183

145

184
    world.create_entity()
146
    world.create_entity()
185
        .with(Position(Point::new(0, 0)))
147
        .with(Position(Point::new(0, 0)))
186
        .with(Velocity {speed: 0, direction: Direction::Right})
148
        .with(Velocity {speed: 0, direction: Direction::Right})
187
        .with(player_animation.right_frames[0].clone())
149
        .with(player_animation.right_frames[0].clone())
188
        .with(player_animation)
150
        .with(player_animation)
189
        .build();
151
        .build();
190

152

191
    let mut event_pump = sdl_context.event_pump()?;
153
    let mut event_pump = sdl_context.event_pump()?;
192
    let mut i = 0;
154
    let mut i = 0;
193
    'running: loop {
155
    'running: loop {
194
        // Handle events
156
        // Handle events
195
        for event in event_pump.poll_iter() {
157
        for event in event_pump.poll_iter() {
196
            match event {
158
            match event {
197
                Event::Quit {..} |
159
                Event::Quit {..} |
198
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
160
                Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
199
                    break 'running;
161
                    break 'running;
200
                },
162
                },
201
                Event::KeyDown { keycode: Some(Keycode::Left), repeat: false, .. } => {
163
                Event::KeyDown { keycode: Some(Keycode::Left), repeat: false, .. } => {
202
                    player.speed = PLAYER_MOVEMENT_SPEED;
164
                    player.speed = PLAYER_MOVEMENT_SPEED;
203
                    player.direction = Direction::Left;
165
                    player.direction = Direction::Left;
204
                },
166
                },
205
                Event::KeyDown { keycode: Some(Keycode::Right), repeat: false, .. } => {
167
                Event::KeyDown { keycode: Some(Keycode::Right), repeat: false, .. } => {
206
                    player.speed = PLAYER_MOVEMENT_SPEED;
168
                    player.speed = PLAYER_MOVEMENT_SPEED;
207
                    player.direction = Direction::Right;
169
                    player.direction = Direction::Right;
208
                },
170
                },
209
                Event::KeyDown { keycode: Some(Keycode::Up), repeat: false, .. } => {
171
                Event::KeyDown { keycode: Some(Keycode::Up), repeat: false, .. } => {
210
                    player.speed = PLAYER_MOVEMENT_SPEED;
172
                    player.speed = PLAYER_MOVEMENT_SPEED;
211
                    player.direction = Direction::Up;
173
                    player.direction = Direction::Up;
212
                },
174
                },
213
                Event::KeyDown { keycode: Some(Keycode::Down), repeat: false, .. } => {
175
                Event::KeyDown { keycode: Some(Keycode::Down), repeat: false, .. } => {
214
                    player.speed = PLAYER_MOVEMENT_SPEED;
176
                    player.speed = PLAYER_MOVEMENT_SPEED;
215
                    player.direction = Direction::Down;
177
                    player.direction = Direction::Down;
216
                },
178
                },
217
                Event::KeyUp { keycode: Some(Keycode::Left), repeat: false, .. } |
179
                Event::KeyUp { keycode: Some(Keycode::Left), repeat: false, .. } |
218
                Event::KeyUp { keycode: Some(Keycode::Right), repeat: false, .. } |
180
                Event::KeyUp { keycode: Some(Keycode::Right), repeat: false, .. } |
219
                Event::KeyUp { keycode: Some(Keycode::Up), repeat: false, .. } |
181
                Event::KeyUp { keycode: Some(Keycode::Up), repeat: false, .. } |
220
                Event::KeyUp { keycode: Some(Keycode::Down), repeat: false, .. } => {
182
                Event::KeyUp { keycode: Some(Keycode::Down), repeat: false, .. } => {
221
                    player.speed = 0;
183
                    player.speed = 0;
222
                },
184
                },
223
                _ => {}
185
                _ => {}
224
            }
186
            }
225
        }
187
        }
226

188

227
        // Update
189
        // Update
228
        i = (i + 1) % 255;
190
        i = (i + 1) % 255;
229
        update_player(&mut player);
191
        update_player(&mut player);
230

192

231
        // Render
193
        // Render
232
        render(&mut canvas, Color::RGB(i, 64, 255 - i), &texture, &player)?;
194
        render(&mut canvas, Color::RGB(i, 64, 255 - i), &texture, &player)?;
233

195

234
        // Time management!
196
        // Time management!
235
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
197
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
236
    }
198
    }
237

199

238
    Ok(())
200
    Ok(())
239
}
201
}
/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 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;

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

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