From c80e135dd41e2848b73305ed9d5b62bc1dc4c602 Mon Sep 17 00:00:00 2001 From: Pau Costa Date: Thu, 9 Nov 2023 22:29:05 +0100 Subject: [PATCH] :building_construction: Moved to ecs based rendering --- .gitignore | 3 + Cargo.lock | 189 ++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + src/components.rs | 10 ++ src/main.rs | 46 ++++++--- src/map.rs | 30 ------ src/player.rs | 38 ------- src/spawner.rs | 12 +++ src/systems/entity_render.rs | 18 ++++ src/systems/map_render.rs | 22 ++++ src/systems/mod.rs | 13 +++ src/systems/player_input.rs | 31 ++++++ 12 files changed, 324 insertions(+), 89 deletions(-) create mode 100644 src/components.rs delete mode 100644 src/player.rs create mode 100644 src/spawner.rs create mode 100644 src/systems/entity_render.rs create mode 100644 src/systems/map_render.rs create mode 100644 src/systems/mod.rs create mode 100644 src/systems/player_input.rs diff --git a/.gitignore b/.gitignore index ea8c4bf..06af902 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ /target +.idea/ +debug/ +target/ diff --git a/Cargo.lock b/Cargo.lock index bbbb346..1ca350b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,6 +41,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + [[package]] name = "bitflags" version = "1.3.2" @@ -411,6 +426,40 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "crossbeam-channel" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87" +dependencies = [ + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils 0.8.16", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils 0.8.16", + "memoffset 0.9.0", + "scopeguard", +] + [[package]] name = "crossbeam-utils" version = "0.7.2" @@ -422,6 +471,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "crossfont" version = "0.5.1" @@ -486,6 +544,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -512,6 +581,7 @@ name = "dungeoncrawl" version = "0.1.0" dependencies = [ "bracket-lib", + "legion", ] [[package]] @@ -528,12 +598,27 @@ dependencies = [ "wio", ] +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "erased-serde" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" +dependencies = [ + "serde", +] + [[package]] name = "expat-sys" version = "2.1.6" @@ -787,6 +872,15 @@ dependencies = [ "web-sys", ] +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + [[package]] name = "jni-sys" version = "0.3.0" @@ -820,6 +914,40 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "legion" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa40f1a5f64dfdc1830657e0e7dd2c28087c4e32a2a85fcf63a286c429edefc" +dependencies = [ + "bit-set", + "crossbeam-channel", + "derivative", + "downcast-rs", + "erased-serde", + "itertools", + "legion_codegen", + "parking_lot 0.11.2", + "paste", + "rayon", + "serde", + "smallvec", + "thiserror", + "uuid", +] + +[[package]] +name = "legion_codegen" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c24e58060e656eae6b87f83f14f41080656a930fba7ef299122e40eb8ccd307f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "thiserror", +] + [[package]] name = "libc" version = "0.2.149" @@ -871,6 +999,12 @@ dependencies = [ "libc", ] +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + [[package]] name = "memchr" version = "2.6.4" @@ -904,6 +1038,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1034,7 +1177,7 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "memoffset", + "memoffset 0.6.5", ] [[package]] @@ -1046,7 +1189,7 @@ dependencies = [ "bitflags", "cfg-if 1.0.0", "libc", - "memoffset", + "memoffset 0.6.5", ] [[package]] @@ -1059,7 +1202,7 @@ dependencies = [ "bitflags", "cfg-if 1.0.0", "libc", - "memoffset", + "memoffset 0.6.5", ] [[package]] @@ -1204,6 +1347,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "percent-encoding" version = "2.3.0" @@ -1296,6 +1445,26 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils 0.8.16", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -1589,7 +1758,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" dependencies = [ - "crossbeam-utils", + "crossbeam-utils 0.7.2", "futures", ] @@ -1599,7 +1768,7 @@ version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" dependencies = [ - "crossbeam-utils", + "crossbeam-utils 0.7.2", "futures", "slab", "tokio-executor", @@ -1637,6 +1806,16 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom", + "serde", +] + [[package]] name = "vec_map" version = "0.8.2" diff --git a/Cargo.toml b/Cargo.toml index b71e327..38c833b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] bracket-lib = "~0.8.1" +legion = "=0.3.1" diff --git a/src/components.rs b/src/components.rs new file mode 100644 index 0000000..50dd738 --- /dev/null +++ b/src/components.rs @@ -0,0 +1,10 @@ +pub use crate::prelude::*; + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Render { + pub color: ColorPair, + pub glyph: FontCharType, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct Player; diff --git a/src/main.rs b/src/main.rs index 922d5f1..3571691 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,36 +1,49 @@ +use prelude::*; + mod camera; +mod components; mod map; mod map_builder; -mod player; +mod spawner; +mod systems; mod prelude { pub use bracket_lib::prelude::*; + pub use legion::world::SubWorld; + pub use legion::*; + + pub use crate::camera::*; + pub use crate::components::*; + pub use crate::map::*; + pub use crate::map_builder::*; + pub use crate::spawner::*; + pub use crate::systems::*; + pub const SCREEN_WIDTH: i32 = 80; pub const SCREEN_HEIGHT: i32 = 50; pub const DISPLAY_WIDTH: i32 = SCREEN_WIDTH / 2; pub const DISPLAY_HEIGHT: i32 = SCREEN_HEIGHT / 2; - pub use crate::camera::*; - pub use crate::map::*; - pub use crate::player::*; - pub use crate::map_builder::*; } -use prelude::*; - struct State { - map: Map, - player: Player, - camera: Camera, + ecs: World, + resources: Resources, + systems: Schedule, } impl State { fn new() -> Self { + let mut ecs = World::default(); + let mut resources = Resources::default(); let mut rng = RandomNumberGenerator::new(); let map_builder = MapBuilder::new(&mut rng); + spawn_player(&mut ecs, map_builder.player_start); + resources.insert(map_builder.map); + resources.insert(Camera::new(map_builder.player_start)); Self { - map: map_builder.map, - player: Player::new(map_builder.player_start), - camera: Camera::new(map_builder.player_start), + ecs, + resources, + systems: build_scheduler(), } } } @@ -41,9 +54,10 @@ impl GameState for State { ctx.cls(); ctx.set_active_console(1); ctx.cls(); - self.player.update(ctx, &self.map, &mut self.camera); - self.map.render(ctx, &self.camera); - self.player.render(ctx, &self.camera); + self.resources.insert(ctx.key); + self.systems.execute(&mut self.ecs, &mut self.resources); + self.systems.execute(&mut self.ecs, &mut self.resources); + render_draw_buffer(ctx).expect("Render error"); } } diff --git a/src/map.rs b/src/map.rs index 67360bd..41bbf5e 100644 --- a/src/map.rs +++ b/src/map.rs @@ -22,36 +22,6 @@ impl Map { tiles: vec![TileType::Floor; NUM_TILES], } } - pub fn render(&self, ctx: &mut BTerm, camera: &Camera) { - ctx.set_active_console(0); - for y in camera.top_y..camera.bottom_y { - for x in camera.left_x..camera.right_x { - if self.in_bounds(Point::new(x, y)) { - let idx = map_idx(x, y); - match self.tiles[idx] { - TileType::Floor => { - ctx.set( - x - camera.left_x, - y - camera.top_y, - WHITE, - BLACK, - to_cp437('.') - ); - } - TileType::Wall => { - ctx.set( - x - camera.left_x, - y - camera.top_y, - WHITE, - BLACK, - to_cp437('#') - ); - } - } - } - } - } - } pub fn in_bounds(&self, point: Point) -> bool { point.x >= 0 && point.x < SCREEN_WIDTH && point.y >= 0 && point.y < SCREEN_HEIGHT } diff --git a/src/player.rs b/src/player.rs deleted file mode 100644 index 8d4bd66..0000000 --- a/src/player.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::prelude::*; - -pub struct Player { - pub position: Point, -} - -impl Player { - pub fn new(position: Point) -> Self { - Self { position } - } - pub fn render(&self, ctx: &mut BTerm, camera: &Camera) { - ctx.set_active_console(1); - ctx.set( - self.position.x - camera.left_x, - self.position.y - camera.top_y, - WHITE, - BLACK, - to_cp437('@'), - ); - } - pub fn update(&mut self, ctx: &mut BTerm, map: &Map, camera: &mut Camera) { - if let Some(key) = ctx.key { - let delta = match key { - VirtualKeyCode::W => Point::new(0, -1), - VirtualKeyCode::A => Point::new(-1, 0), - VirtualKeyCode::S => Point::new(0, 1), - VirtualKeyCode::D => Point::new(1, 0), - _ => Point::zero(), - }; - - let new_position = self.position + delta; - if map.can_enter_tile(new_position) { - self.position = new_position; - camera.on_player_move(new_position); - } - } - } -} diff --git a/src/spawner.rs b/src/spawner.rs new file mode 100644 index 0000000..5790995 --- /dev/null +++ b/src/spawner.rs @@ -0,0 +1,12 @@ +use crate::prelude::*; + +pub fn spawn_player(ecs: &mut World, pos: Point) { + ecs.push(( + Player, + pos, + Render { + color: ColorPair::new(WHITE, BLACK), + glyph: to_cp437('@'), + }, + )); +} diff --git a/src/systems/entity_render.rs b/src/systems/entity_render.rs new file mode 100644 index 0000000..1780860 --- /dev/null +++ b/src/systems/entity_render.rs @@ -0,0 +1,18 @@ +use crate::prelude::*; + +#[system] +#[read_component(Point)] +#[read_component(Render)] +pub fn entity_render(ecs: &SubWorld, #[resource] camera: &Camera) { + let mut draw_batch = DrawBatch::new(); + draw_batch.target(1); + let offset = Point::new(camera.left_x, camera.top_y); + + <(&Point, &Render)>::query() + .iter(ecs) + .for_each(|(pos, render)| { + draw_batch.set(*pos - offset, render.color, render.glyph); + }); + + draw_batch.submit(5000).expect("Entity draw batch error"); +} diff --git a/src/systems/map_render.rs b/src/systems/map_render.rs new file mode 100644 index 0000000..3024fa2 --- /dev/null +++ b/src/systems/map_render.rs @@ -0,0 +1,22 @@ +use crate::prelude::*; + +#[system] +pub fn map_render(#[resource] map: &Map, #[resource] camera: &Camera) { + let mut draw_batch = DrawBatch::new(); + draw_batch.target(0); + for y in camera.top_y..=camera.bottom_y { + for x in camera.left_x..camera.right_x { + let pt = Point::new(x, y); + let offset = Point::new(camera.left_x, camera.top_y); + if map.in_bounds(pt) { + let idx = map_idx(x, y); + let glyph = match map.tiles[idx] { + TileType::Floor => to_cp437('.'), + TileType::Wall => to_cp437('#'), + }; + draw_batch.set(pt - offset, ColorPair::new(WHITE, BLACK), glyph); + } + } + } + draw_batch.submit(0).expect("Map Draw Batch error"); +} diff --git a/src/systems/mod.rs b/src/systems/mod.rs new file mode 100644 index 0000000..9375d4f --- /dev/null +++ b/src/systems/mod.rs @@ -0,0 +1,13 @@ +use crate::prelude::*; + +mod entity_render; +mod map_render; +mod player_input; + +pub fn build_scheduler() -> Schedule { + Schedule::builder() + .add_system(player_input::player_input_system()) + .add_system(map_render::map_render_system()) + .add_system(entity_render::entity_render_system()) + .build() +} diff --git a/src/systems/player_input.rs b/src/systems/player_input.rs new file mode 100644 index 0000000..ef63ea8 --- /dev/null +++ b/src/systems/player_input.rs @@ -0,0 +1,31 @@ +use crate::prelude::*; + +#[system] +#[write_component(Point)] +#[read_component(Player)] +pub fn player_input( + ecs: &mut SubWorld, + #[resource] map: &Map, + #[resource] key: &Option, + #[resource] camera: &mut Camera, +) { + if let Some(key) = key { + let delta = match key { + VirtualKeyCode::A => Point::new(-1, 0), + VirtualKeyCode::D => Point::new(1, 0), + VirtualKeyCode::W => Point::new(0, -1), + VirtualKeyCode::S => Point::new(0, 1), + _ => Point::new(0, 0), + }; + if delta.x != 0 || delta.y != 0 { + let mut players = <&mut Point>::query().filter(component::()); + players.iter_mut(ecs).for_each(|pos| { + let destination = *pos + delta; + if map.can_enter_tile(destination) { + *pos = destination; + camera.on_player_move(destination); + } + }) + } + } +}