Split apart into files and also add crappy interaction component
Getty Ritter
5 years ago
654 | 654 | dependencies = [ |
655 | 655 | "ggez 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", |
656 | 656 | "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", |
657 | "sdl2 0.31.0 (registry+https://github.com/rust-lang/crates.io-index)", | |
657 | 658 | "specs 0.14.3 (registry+https://github.com/rust-lang/crates.io-index)", |
658 | 659 | "specs-derive 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", |
659 | 660 | "tiled 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", |
9 | 9 | tiled = "0.8" |
10 | 10 | itertools = "0.8" |
11 | 11 | specs = "0.14" |
12 |
specs-derive = "0.4" |
|
12 | specs-derive = "0.4" | |
13 | sdl2 = "0.31"⏎ |
1 | use specs::{Component, VecStorage, NullStorage}; | |
2 | ||
3 | pub fn register(world: &mut specs::World) { | |
4 | world.register::<Position>(); | |
5 | world.register::<Velocity>(); | |
6 | world.register::<Sprite>(); | |
7 | world.register::<Background>(); | |
8 | world.register::<Foreground>(); | |
9 | world.register::<Decoration>(); | |
10 | world.register::<Movable>(); | |
11 | } | |
12 | ||
13 | #[derive(Component, Debug)] | |
14 | #[storage(VecStorage)] | |
15 | pub struct Position { | |
16 | pub x: f32, | |
17 | pub y: f32, | |
18 | } | |
19 | ||
20 | impl Position { | |
21 | pub fn to_point(&self) -> ggez::nalgebra::Point2<f32> { | |
22 | ggez::nalgebra::Point2::new(self.x * 3.0, self.y * 3.0) | |
23 | } | |
24 | } | |
25 | ||
26 | #[derive(Component, Debug)] | |
27 | #[storage(VecStorage)] | |
28 | pub struct Velocity { | |
29 | pub dx: f32, | |
30 | pub dy: f32, | |
31 | } | |
32 | ||
33 | impl Velocity { | |
34 | pub fn to_point(&self) -> ggez::nalgebra::Point2<f32> { | |
35 | ggez::nalgebra::Point2::new(self.dx * 3.0, self.dy * 3.0) | |
36 | } | |
37 | } | |
38 | ||
39 | #[derive(Component, Debug)] | |
40 | #[storage(VecStorage)] | |
41 | pub struct Sprite { | |
42 | pub u: u8, | |
43 | pub v: u8, | |
44 | } | |
45 | ||
46 | impl Sprite { | |
47 | pub fn to_rect(&self) -> ggez::graphics::Rect { | |
48 | ggez::graphics::Rect { | |
49 | x: (1.0 / 32.0) * self.u as f32, | |
50 | y: (1.0 / 32.0) * self.v as f32, | |
51 | w: 1.0 / 32.0, | |
52 | h: 1.0 / 32.0, | |
53 | } | |
54 | } | |
55 | } | |
56 | ||
57 | ||
58 | #[derive(Component, Default, Debug)] | |
59 | #[storage(NullStorage)] | |
60 | pub struct Background; | |
61 | ||
62 | #[derive(Component, Default, Debug)] | |
63 | #[storage(NullStorage)] | |
64 | pub struct Foreground; | |
65 | ||
66 | #[derive(Component, Default, Debug)] | |
67 | #[storage(NullStorage)] | |
68 | pub struct Decoration; | |
69 | ||
70 | #[derive(Component, Default, Debug)] | |
71 | #[storage(NullStorage)] | |
72 | pub struct Movable; |
1 | #![allow(dead_code)] | |
2 | ||
3 | // constants used elsewhere | |
4 | pub const SPEED: u32 = 2; | |
5 | pub const TILE_SIZE: f32 = 24.0; | |
6 | pub const HALF_TILE: f32 = TILE_SIZE / 2.0; | |
7 | pub const QUARTER_TILE: f32 = TILE_SIZE / 4.0; | |
8 | pub const BOARD_WIDTH: usize = 16; | |
9 | pub const BOARD_HEIGHT: usize = 12; | |
10 | pub const WINDOW_WIDTH: u32 = BOARD_WIDTH as u32 * (TILE_SIZE * SCALE) as u32; | |
11 | pub const WINDOW_HEIGHT: u32 = BOARD_HEIGHT as u32 * (TILE_SIZE * SCALE) as u32; | |
12 | pub const SCALE: f32 = 3.0; | |
13 | pub const TILED_TRUE: tiled::PropertyValue = | |
14 | tiled::PropertyValue::BoolValue(true); |
4 | 4 | use ggez::event::{self, EventHandler}; |
5 | 5 | use ggez::graphics; |
6 | 6 | |
7 | use std::path::Path; | |
8 | use specs::{Component, RunNow, System, NullStorage, VecStorage}; | |
9 |
use specs:: |
|
7 | use specs::RunNow; | |
10 | 8 | |
11 | #[allow(dead_code)] | |
12 | mod consts { | |
13 | pub const SPEED: u32 = 2; | |
14 | pub const TILE_SIZE: u32 = 24; | |
15 | pub const HALF_TILE: u32 = TILE_SIZE / 2; | |
16 | pub const QUARTER_TILE: u32 = TILE_SIZE / 4; | |
17 | pub const BOARD_WIDTH: usize = 16; | |
18 | pub const BOARD_HEIGHT: usize = 12; | |
19 | pub const SCALE: u32 = 3; | |
20 | pub const TILED_TRUE: tiled::PropertyValue = | |
21 | tiled::PropertyValue::BoolValue(true); | |
22 | } | |
9 | use sdl2::keyboard as sdl; | |
23 | 10 | |
24 | #[derive(Component, Debug)] | |
25 | #[storage(VecStorage)] | |
26 | struct Position { | |
27 | x: f32, | |
28 | y: f32, | |
29 | } | |
30 | ||
31 | impl Position { | |
32 | fn to_point(&self) -> ggez::nalgebra::Point2<f32> { | |
33 | ggez::nalgebra::Point2::new(self.x, self.y) | |
34 | } | |
35 | } | |
36 | ||
37 | #[derive(Component, Debug)] | |
38 | #[storage(VecStorage)] | |
39 | struct Sprite { | |
40 | u: u8, | |
41 | v: u8, | |
42 | } | |
43 | ||
44 | impl Sprite { | |
45 | fn to_rect(&self) -> graphics::Rect { | |
46 | graphics::Rect { | |
47 | x: (1.0 / 32.0) * self.u as f32, | |
48 | y: (1.0 / 32.0) * self.v as f32, | |
49 | w: 1.0 / 32.0, | |
50 | h: 1.0 / 32.0, | |
51 | } | |
52 | } | |
53 | } | |
54 | ||
55 | #[derive(Debug, Copy, Clone)] | |
56 | enum DrawLayer { | |
57 | Background, | |
58 | Foreground, | |
59 | Decoration, | |
60 | } | |
61 | ||
62 | static DRAW_LAYERS: [DrawLayer;3] = [ | |
63 | DrawLayer::Background, | |
64 | DrawLayer::Foreground, | |
65 | DrawLayer::Decoration, | |
66 | ]; | |
67 | ||
68 | #[derive(Component, Default, Debug)] | |
69 | #[storage(NullStorage)] | |
70 | struct Background; | |
71 | ||
72 | #[derive(Component, Default, Debug)] | |
73 | #[storage(NullStorage)] | |
74 | struct Foreground; | |
75 | ||
76 | #[derive(Component, Default, Debug)] | |
77 | #[storage(NullStorage)] | |
78 | struct Decoration; | |
79 | ||
80 | fn world_from_file(w: &mut specs::World, path: &Path) { | |
81 | let tiled::Map { | |
82 | layers, | |
83 | .. | |
84 | } = tiled::parse_file(path).unwrap(); | |
85 | ||
86 | for (phase, layer) in DRAW_LAYERS.iter().zip(layers) { | |
87 | for (row, y) in layer.tiles.iter().zip(0..) { | |
88 | for (&n, x) in row.iter().zip(0..) { | |
89 | if n != 0 { | |
90 | let x = x as f32 * 24.0; | |
91 | let y = y as f32 * 24.0; | |
92 | let u = ((n - 1) % 32) as u8; | |
93 | let v = ((n - u as u32 - 1) / 32) as u8; | |
94 | let mut e = w.create_entity() | |
95 | .with(Position { x, y }) | |
96 | .with(Sprite { u, v }); | |
97 | e = match phase { | |
98 | DrawLayer::Background => e.with(Background), | |
99 | DrawLayer::Foreground => e.with(Foreground), | |
100 | DrawLayer::Decoration => e.with(Decoration), | |
101 | }; | |
102 | e.build(); | |
103 | } | |
104 | } | |
105 | } | |
106 | } | |
107 | } | |
11 | pub mod consts; | |
12 | pub mod components; | |
13 | pub mod res; | |
14 | pub mod sys; | |
108 | 15 | |
109 | 16 | struct MyGame { |
110 | 17 | world: specs::World, |
111 | 18 | sprites: graphics::spritebatch::SpriteBatch, |
112 | 19 | } |
113 | 20 | |
114 | struct Draw<'t, Phase> { | |
115 | ctx: &'t mut Context, | |
116 | sprites: &'t mut graphics::spritebatch::SpriteBatch, | |
117 | _phase: Phase, | |
118 | } | |
21 | impl EventHandler for MyGame { | |
119 | 22 | |
120 | impl<'a, 't, Phase: Component> System<'a> for Draw<'t, Phase> { | |
121 | type SystemData = ( | |
122 | specs::ReadStorage<'a, Position>, | |
123 | specs::ReadStorage<'a, Sprite>, | |
124 | specs::ReadStorage<'a, Phase>, | |
125 | ); | |
126 | ||
127 | fn run(&mut self, (positions, sprites, phase): Self::SystemData) { | |
128 | use specs::Join; | |
129 | ||
130 | for (pos, spr, _) in (&positions, &sprites, &phase).join() { | |
131 | let param = graphics::DrawParam { | |
132 | src: spr.to_rect(), | |
133 | dest: pos.to_point(), | |
134 | ..Default::default() | |
135 | }; | |
136 | self.sprites.add(param); | |
137 | graphics::draw( | |
138 | self.ctx, | |
139 | self.sprites, | |
140 | ggez::nalgebra::Point2::new(0.0, 0.0), | |
141 | 0.0).unwrap(); | |
142 | self.sprites.clear(); | |
143 | } | |
144 | } | |
145 | } | |
146 | ||
147 | impl EventHandler for MyGame { | |
148 | 23 | fn update(&mut self, _ctx: &mut Context) -> GameResult<()> { |
149 | 24 | Ok(()) |
150 | 25 | } |
151 | 26 | |
152 | 27 | fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { |
153 | use ggez::graphics as g; | |
154 | use ggez::nalgebra as n; | |
155 | g::set_background_color(ctx, g::BLACK); | |
156 | g::clear(ctx); | |
28 | ggez::graphics::set_background_color(ctx, ggez::graphics::BLACK); | |
29 | ggez::graphics::clear(ctx); | |
157 | 30 | |
158 | Draw { ctx, sprites: &mut self.sprites, _phase: Background }.run_now(&self.world.res); | |
159 | Draw { ctx, sprites: &mut self.sprites, _phase: Foreground }.run_now(&self.world.res); | |
160 |
|
|
31 | sys::Draw { | |
32 | ctx, | |
33 | sprites: &mut self.sprites, | |
34 | _phase: components::Background, | |
35 | }.run_now(&self.world.res); | |
36 | sys::Draw { | |
37 | ctx, | |
38 | sprites: &mut self.sprites, | |
39 | _phase: components::Foreground, | |
40 | }.run_now(&self.world.res); | |
41 | sys::Draw { | |
42 | ctx, | |
43 | sprites: &mut self.sprites, | |
44 | _phase: components::Decoration, | |
45 | }.run_now(&self.world.res); | |
161 | 46 | |
162 |
g |
|
47 | ggez::graphics::draw( | |
48 | ctx, | |
49 | &self.sprites, | |
50 | ggez::nalgebra::Point2::new(0.0, 0.0), | |
51 | 0.0 | |
52 | )?; | |
163 | 53 | self.sprites.clear(); |
164 |
g |
|
54 | ggez::graphics::present(ctx); | |
165 | 55 | Ok(()) |
56 | } | |
57 | ||
58 | fn key_down_event( | |
59 | &mut self, | |
60 | ctx: &mut Context, | |
61 | keycode: sdl::Keycode, | |
62 | _keymod: sdl::Mod, | |
63 | _repeat: bool, | |
64 | ) { | |
65 | if keycode == sdl::Keycode::Escape { | |
66 | ctx.quit().expect("Should never fail"); | |
67 | } | |
68 | sys::Move { keycode }.run_now(&self.world.res); | |
166 | 69 | } |
167 | 70 | } |
168 | 71 | |
169 | 72 | fn main() -> Result<(), ggez::error::GameError> { |
170 | 73 | let mut world = specs::World::new(); |
171 | world.register::<Position>(); | |
172 | world.register::<Sprite>(); | |
173 | world.register::<Background>(); | |
174 | world.register::<Foreground>(); | |
175 |
|
|
74 | components::register(&mut world); | |
176 | 75 | |
177 |
|
|
76 | res::world_from_file(&mut world, "assets/main.tmx"); | |
178 | 77 | |
179 | 78 | // Make a Context and an EventLoop. |
180 | 79 | let mut ctx = ContextBuilder::new("game", "me") |
184 | 83 | path.push("assets"); |
185 | 84 | path |
186 | 85 | }) |
86 | .window_mode(ggez::conf::WindowMode { | |
87 | width: consts::WINDOW_WIDTH, | |
88 | height: consts::WINDOW_HEIGHT, | |
89 | ..Default::default() | |
90 | }) | |
187 | 91 | .build()?; |
188 | 92 | let image = graphics::Image::new(&mut ctx, "/spritesheet.png")?; |
189 |
let |
|
93 | let mut sprites = graphics::spritebatch::SpriteBatch::new(image); | |
94 | sprites.set_filter(ggez::graphics::FilterMode::Nearest); | |
190 | 95 | |
191 | 96 | let mut my_game = MyGame { |
192 | 97 | world, |
1 | use crate::consts; | |
2 | use crate::components::*; | |
3 | ||
4 | use specs::world::Builder; | |
5 | use std::path::Path; | |
6 | ||
7 | ||
8 | #[derive(Debug, Copy, Clone)] | |
9 | enum DrawLayer { | |
10 | Background, | |
11 | Foreground, | |
12 | Decoration, | |
13 | } | |
14 | ||
15 | static DRAW_LAYERS: [DrawLayer;3] = [ | |
16 | DrawLayer::Background, | |
17 | DrawLayer::Foreground, | |
18 | DrawLayer::Decoration, | |
19 | ]; | |
20 | ||
21 | ||
22 | pub fn world_from_file<P: AsRef<Path>>(w: &mut specs::World, path: P) { | |
23 | let tiled::Map { | |
24 | layers, | |
25 | .. | |
26 | } = tiled::parse_file(path.as_ref()).unwrap(); | |
27 | ||
28 | for (phase, layer) in DRAW_LAYERS.iter().zip(layers) { | |
29 | for (row, y) in layer.tiles.iter().zip(0..) { | |
30 | for (&n, x) in row.iter().zip(0..) { | |
31 | if n != 0 { | |
32 | let x = x as f32 * consts::TILE_SIZE; | |
33 | let y = y as f32 * consts::TILE_SIZE; | |
34 | let u = ((n - 1) % 32) as u8; | |
35 | let v = ((n - u as u32 - 1) / 32) as u8; | |
36 | let mut e = w.create_entity() | |
37 | .with(Position { x, y }) | |
38 | .with(Sprite { u, v }); | |
39 | e = match phase { | |
40 | DrawLayer::Background => e.with(Background), | |
41 | DrawLayer::Foreground => e.with(Foreground), | |
42 | DrawLayer::Decoration => e.with(Decoration), | |
43 | }; | |
44 | e.build(); | |
45 | } | |
46 | } | |
47 | } | |
48 | } | |
49 | ||
50 | // create the player | |
51 | w.create_entity() | |
52 | .with(Position { | |
53 | x: 3.0 * consts::TILE_SIZE, | |
54 | y: 3.0 * consts::TILE_SIZE, | |
55 | }) | |
56 | .with(Sprite { u: 8, v: 0 }) | |
57 | .with(Velocity { dx: 0.0, dy: 0.0 }) | |
58 | .with(Foreground) | |
59 | .with(Movable) | |
60 | .build(); | |
61 | } |
1 | use crate::consts; | |
2 | use crate::components::{Position,Sprite}; | |
3 | ||
4 | use ggez::{ | |
5 | Context, | |
6 | graphics::spritebatch::SpriteBatch, | |
7 | }; | |
8 | use ggez::graphics; | |
9 | ||
10 | pub struct Draw<'t, Phase> { | |
11 | pub ctx: &'t mut Context, | |
12 | pub sprites: &'t mut SpriteBatch, | |
13 | pub _phase: Phase, | |
14 | } | |
15 | ||
16 | impl<'a, 't, Phase: specs::Component> specs::System<'a> for Draw<'t, Phase> { | |
17 | type SystemData = ( | |
18 | specs::ReadStorage<'a, Position>, | |
19 | specs::ReadStorage<'a, Sprite>, | |
20 | specs::ReadStorage<'a, Phase>, | |
21 | ); | |
22 | ||
23 | fn run(&mut self, (positions, sprites, phase): Self::SystemData) { | |
24 | use specs::Join; | |
25 | ||
26 | for (pos, spr, _) in (&positions, &sprites, &phase).join() { | |
27 | let param = graphics::DrawParam { | |
28 | src: spr.to_rect(), | |
29 | dest: pos.to_point(), | |
30 | scale: ggez::nalgebra::Point2::new(consts::SCALE, consts::SCALE), | |
31 | ..Default::default() | |
32 | }; | |
33 | self.sprites.add(param); | |
34 | graphics::draw( | |
35 | self.ctx, | |
36 | self.sprites, | |
37 | ggez::nalgebra::Point2::new(0.0, 0.0), | |
38 | 0.0).unwrap(); | |
39 | self.sprites.clear(); | |
40 | } | |
41 | } | |
42 | } |
1 | use crate::components::{Movable, Position}; | |
2 | ||
3 | use sdl2::keyboard as sdl; | |
4 | ||
5 | pub struct Move { | |
6 | pub keycode: sdl::Keycode, | |
7 | } | |
8 | ||
9 | impl<'a> specs::System<'a> for Move { | |
10 | type SystemData = ( | |
11 | specs::ReadStorage<'a, Movable>, | |
12 | specs::WriteStorage<'a, Position>, | |
13 | ); | |
14 | ||
15 | fn run(&mut self, (movable, mut position): Self::SystemData) { | |
16 | use specs::Join; | |
17 | ||
18 | for (_, pos) in (&movable, &mut position).join() { | |
19 | match self.keycode { | |
20 | sdl::Keycode::W => pos.y -= 1.0, | |
21 | sdl::Keycode::A => pos.x -= 1.0, | |
22 | sdl::Keycode::S => pos.y += 1.0, | |
23 | sdl::Keycode::D => pos.x += 1.0, | |
24 | _ => (), | |
25 | } | |
26 | } | |
27 | } | |
28 | } |