• src/main.rs
+47 -16src/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 8

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

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

14 14
const PLAYER_MOVEMENT_SPEED: i32 = 20;
15 15

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

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

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

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

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

57  
#[derive(Debug)]
58  
struct Player {
59  
    position: Point,
60  
    sprite: Rect,
61  
    speed: i32,
62  
    direction: Direction,
63  
    current_frame: i32,
64  
}
65  

66 57
/// Returns the row of the spritesheet corresponding to the given direction
67 58
fn direction_spritesheet_row(direction: Direction) -> i32 {
68 59
    use self::Direction::*;
69 60
    match direction {
70 61
        Up => 3,
71 62
        Down => 0,
72 63
        Left => 1,
73 64
        Right => 2,
74 65
    }
75 66
}
76 67

  68
/// Create animation frames for the standard character spritesheet
  69
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
  71
    // the design of our entire system. We can always replace this function, but replacing the
  72
    // entire system is harder.
  73

  74
    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);
  76

  77
    let mut frames = Vec::new();
  78
    for i in 0..3 {
  79
        frames.push(Sprite {
  80
            spritesheet,
  81
            region: Rect::new(
  82
                top_left_frame.x() + frame_width as i32 * i,
  83
                y_offset,
  84
                frame_width,
  85
                frame_height,
  86
            ),
  87
        })
  88
    }
  89

  90
    frames
  91
}
  92

77 93
fn render(
78 94
    canvas: &mut WindowCanvas,
79 95
    color: Color,
80 96
    texture: &Texture,
81 97
    player: &Player,
82 98
) -> Result<(), String> {
83 99
    canvas.set_draw_color(color);
84 100
    canvas.clear();
85 101

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

88 104
    let (frame_width, frame_height) = player.sprite.size();
89 105
    let current_frame = Rect::new(
90 106
        player.sprite.x() + frame_width as i32 * player.current_frame,
91 107
        player.sprite.y() + frame_height as  i32 * direction_spritesheet_row(player.direction),
92 108
        frame_width,
93 109
        frame_height,
94 110
    );
95 111

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

101 117
    canvas.present();
102 118

103 119
    Ok(())
104 120
}
105 121

106 122
// Update player a fixed amount based on their speed.
107 123
// WARNING: Calling this function too often or at a variable speed will cause the player's speed
108 124
// to be unpredictable!
109 125
fn update_player(player: &mut Player) {
110 126
    use self::Direction::*;
111 127
    match player.direction {
112 128
        Left => {
113 129
            player.position = player.position.offset(-player.speed, 0);
114 130
        },
115 131
        Right => {
116 132
            player.position = player.position.offset(player.speed, 0);
117 133
        },
118 134
        Up => {
119 135
            player.position = player.position.offset(0, -player.speed);
120 136
        },
121 137
        Down => {
122 138
            player.position = player.position.offset(0, player.speed);
123 139
        },
124 140
    }
125 141

126 142
    // Only continue to animate if the player is moving
127 143
    if player.speed != 0 {
128 144
        // Cheat: using the fact that all animations are 3 frames (NOT extensible)
129 145
        player.current_frame = (player.current_frame + 1) % 3;
130 146
    }
131 147
}
132 148

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

141 157
    let window = video_subsystem.window("game tutorial", 800, 600)
142 158
        .position_centered()
143 159
        .build()
144 160
        .expect("could not initialize video subsystem");
145 161

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

149 165
    let texture_creator = canvas.texture_creator();
150  
    let texture = texture_creator.load_texture("assets/bardo.png")?;
151 166

152  
    let mut player = Player {
153  
        position: Point::new(0, 0),
154  
        sprite: Rect::new(0, 0, 26, 36),
155  
        speed: 0,
156  
        direction: Direction::Right,
  167
    let textures = [
  168
        texture_creator.load_texture("assets/bardo.png")?,
  169
    ];
  170
    // First texture in textures array
  171
    let player_spritesheet = 0;
  172
    let player_top_left_frame = Rect::new(0, 0, 26, 36);
  173

  174
    let player_animation = MovementAnimation {
157 175
        current_frame: 0,
  176
        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),
  178
        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),
158 180
    };
159 181

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

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

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

196 227
        // Update
197 228
        i = (i + 1) % 255;
198 229
        update_player(&mut player);
199 230

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

203 234
        // Time management!
204 235
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
205 236
    }
206 237

207 238
    Ok(())
208 239
}
+47 -16src/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

8

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

11

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

13

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

15

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

23

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

28

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

36

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

45

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

56

57
#[derive(Debug)]
   
58
struct Player {
   
59
    position: Point,
   
60
    sprite: Rect,
   
61
    speed: i32,
   
62
    direction: Direction,
   
63
    current_frame: i32,
   
64
}
   
65

   
66
/// Returns the row of the spritesheet corresponding to the given direction
57
/// Returns the row of the spritesheet corresponding to the given direction
67
fn direction_spritesheet_row(direction: Direction) -> i32 {
58
fn direction_spritesheet_row(direction: Direction) -> i32 {
68
    use self::Direction::*;
59
    use self::Direction::*;
69
    match direction {
60
    match direction {
70
        Up => 3,
61
        Up => 3,
71
        Down => 0,
62
        Down => 0,
72
        Left => 1,
63
        Left => 1,
73
        Right => 2,
64
        Right => 2,
74
    }
65
    }
75
}
66
}
76

67

    68
/// Create animation frames for the standard character spritesheet
    69
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
    71
    // the design of our entire system. We can always replace this function, but replacing the
    72
    // entire system is harder.
    73

    74
    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);
    76

    77
    let mut frames = Vec::new();
    78
    for i in 0..3 {
    79
        frames.push(Sprite {
    80
            spritesheet,
    81
            region: Rect::new(
    82
                top_left_frame.x() + frame_width as i32 * i,
    83
                y_offset,
    84
                frame_width,
    85
                frame_height,
    86
            ),
    87
        })
    88
    }
    89

    90
    frames
    91
}
    92

77
fn render(
93
fn render(
78
    canvas: &mut WindowCanvas,
94
    canvas: &mut WindowCanvas,
79
    color: Color,
95
    color: Color,
80
    texture: &Texture,
96
    texture: &Texture,
81
    player: &Player,
97
    player: &Player,
82
) -> Result<(), String> {
98
) -> Result<(), String> {
83
    canvas.set_draw_color(color);
99
    canvas.set_draw_color(color);
84
    canvas.clear();
100
    canvas.clear();
85

101

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

103

88
    let (frame_width, frame_height) = player.sprite.size();
104
    let (frame_width, frame_height) = player.sprite.size();
89
    let current_frame = Rect::new(
105
    let current_frame = Rect::new(
90
        player.sprite.x() + frame_width as i32 * player.current_frame,
106
        player.sprite.x() + frame_width as i32 * player.current_frame,
91
        player.sprite.y() + frame_height as  i32 * direction_spritesheet_row(player.direction),
107
        player.sprite.y() + frame_height as  i32 * direction_spritesheet_row(player.direction),
92
        frame_width,
108
        frame_width,
93
        frame_height,
109
        frame_height,
94
    );
110
    );
95

111

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

116

101
    canvas.present();
117
    canvas.present();
102

118

103
    Ok(())
119
    Ok(())
104
}
120
}
105

121

106
// Update player a fixed amount based on their speed.
122
// Update player a fixed amount based on their speed.
107
// WARNING: Calling this function too often or at a variable speed will cause the player's speed
123
// WARNING: Calling this function too often or at a variable speed will cause the player's speed
108
// to be unpredictable!
124
// to be unpredictable!
109
fn update_player(player: &mut Player) {
125
fn update_player(player: &mut Player) {
110
    use self::Direction::*;
126
    use self::Direction::*;
111
    match player.direction {
127
    match player.direction {
112
        Left => {
128
        Left => {
113
            player.position = player.position.offset(-player.speed, 0);
129
            player.position = player.position.offset(-player.speed, 0);
114
        },
130
        },
115
        Right => {
131
        Right => {
116
            player.position = player.position.offset(player.speed, 0);
132
            player.position = player.position.offset(player.speed, 0);
117
        },
133
        },
118
        Up => {
134
        Up => {
119
            player.position = player.position.offset(0, -player.speed);
135
            player.position = player.position.offset(0, -player.speed);
120
        },
136
        },
121
        Down => {
137
        Down => {
122
            player.position = player.position.offset(0, player.speed);
138
            player.position = player.position.offset(0, player.speed);
123
        },
139
        },
124
    }
140
    }
125

141

126
    // Only continue to animate if the player is moving
142
    // Only continue to animate if the player is moving
127
    if player.speed != 0 {
143
    if player.speed != 0 {
128
        // Cheat: using the fact that all animations are 3 frames (NOT extensible)
144
        // Cheat: using the fact that all animations are 3 frames (NOT extensible)
129
        player.current_frame = (player.current_frame + 1) % 3;
145
        player.current_frame = (player.current_frame + 1) % 3;
130
    }
146
    }
131
}
147
}
132

148

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

156

141
    let window = video_subsystem.window("game tutorial", 800, 600)
157
    let window = video_subsystem.window("game tutorial", 800, 600)
142
        .position_centered()
158
        .position_centered()
143
        .build()
159
        .build()
144
        .expect("could not initialize video subsystem");
160
        .expect("could not initialize video subsystem");
145

161

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

164

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

166

152
    let mut player = Player {
167
    let textures = [
153
        position: Point::new(0, 0),
168
        texture_creator.load_texture("assets/bardo.png")?,
154
        sprite: Rect::new(0, 0, 26, 36),
169
    ];
155
        speed: 0,
170
    // First texture in textures array
156
        direction: Direction::Right,
171
    let player_spritesheet = 0;
    172
    let player_top_left_frame = Rect::new(0, 0, 26, 36);
    173

    174
    let player_animation = MovementAnimation {
157
        current_frame: 0,
175
        current_frame: 0,
    176
        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),
    178
        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),
158
    };
180
    };
159

181

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

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

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

226

196
        // Update
227
        // Update
197
        i = (i + 1) % 255;
228
        i = (i + 1) % 255;
198
        update_player(&mut player);
229
        update_player(&mut player);
199

230

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

233

203
        // Time management!
234
        // Time management!
204
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
235
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 20));
205
    }
236
    }
206

237

207
    Ok(())
238
    Ok(())
208
}
239
}
/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, Clone)]
#[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>,
}

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