From 173630edab6aa67bc0c47ed9e654364a5e66c335 Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Wed, 9 Jun 2021 14:13:09 -0500 Subject: [PATCH] Refactor visibility to use physics system. --- src/core.rs | 2 +- src/exploration.rs | 41 ++++-- src/lib.rs | 1 + src/map.rs | 60 +------- src/visibility.rs | 337 +++++++++++---------------------------------- 5 files changed, 111 insertions(+), 330 deletions(-) diff --git a/src/core.rs b/src/core.rs index eb86d87..139422f 100644 --- a/src/core.rs +++ b/src/core.rs @@ -21,7 +21,7 @@ impl Coordinates { )) } - fn to_transform(&self, config: &CoreConfig) -> Transform { + pub fn to_transform(&self, config: &CoreConfig) -> Transform { Transform::from_translation(Vec3::new( self.0 .0 * config.pixels_per_unit as f32, self.0 .1 * config.pixels_per_unit as f32, diff --git a/src/exploration.rs b/src/exploration.rs index 662e0d2..c73765d 100644 --- a/src/exploration.rs +++ b/src/exploration.rs @@ -2,6 +2,7 @@ use std::{error::Error, hash::Hash, marker::PhantomData}; use bevy::prelude::*; use bevy_input_actionmap::InputMap; +use bevy_rapier2d::prelude::*; use bevy_tts::Tts; use derive_more::{Deref, DerefMut}; use mapgen::TileType; @@ -328,13 +329,17 @@ fn exploration_changed_announcement( names: Query<&Name>, types: Query<&ExplorationType>, mappables: Query<&Mappable>, + query_pipeline: Res, + collider_query: QueryPipelineColliderComponentsQuery, ) -> Result<(), Box> { for (coordinates, exploring) in explorers.iter() { + let collider_set = QueryPipelineColliderComponentsSet(&collider_query); let coordinates = **coordinates; let coordinates = (coordinates.0.floor(), coordinates.1.floor()); for (map, revealed_tiles, visible_tiles) in map.iter() { let point = **exploring; let idx = point.to_index(map.width()); + let shape = Cuboid::new(Vec2::new(0.5, 0.5).into()); let known = revealed_tiles[idx]; let visible = visible_tiles[idx]; let fog_of_war = known && !visible; @@ -343,21 +348,31 @@ fn exploration_changed_announcement( for (entity, _) in focused.iter() { commands.entity(entity).remove::(); } - for entity in &map.entities[idx] { - commands - .entity(*entity) - .insert(ExplorationFocused::default()); - if visible || mappables.get(*entity).is_ok() { - if let Ok(name) = names.get(*entity) { - tokens.push(name.as_str()); - } - if tokens.is_empty() { - if let Ok(t) = types.get(*entity) { - tokens.push((*t).into()); + let shape_pos = (Vec2::new(exploring.x(), exploring.y()), 0.); + query_pipeline.intersections_with_shape( + &collider_set, + &shape_pos.into(), + &shape, + InteractionGroups::all(), + None, + |handle| { + let entity = handle.entity(); + commands + .entity(entity) + .insert(ExplorationFocused::default()); + if visible || mappables.get(entity).is_ok() { + if let Ok(name) = names.get(entity) { + tokens.push(name.as_str()); + } + if tokens.is_empty() { + if let Ok(t) = types.get(entity) { + tokens.push((*t).into()); + } } } - } - } + true + }, + ); if tokens.is_empty() { match map.base.tiles[idx] { TileType::Floor => "Floor".to_string(), diff --git a/src/lib.rs b/src/lib.rs index f20cefd..5e5f84e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(unused_imports)] #![allow(clippy::too_many_arguments)] #![allow(clippy::type_complexity)] diff --git a/src/map.rs b/src/map.rs index e226580..1fc5b0a 100644 --- a/src/map.rs +++ b/src/map.rs @@ -29,16 +29,12 @@ pub struct Portal; #[derive(Clone, Default)] pub struct Map { pub base: MapgenMap, - pub entities: Vec>, } impl Map { pub fn new(base: MapgenMap) -> Self { let count = (base.width * base.height) as usize; - Self { - base, - entities: vec![HashSet::new(); count], - } + Self { base } } pub fn width(&self) -> usize { @@ -358,38 +354,6 @@ fn area_description( } } -fn remove_coordinates(removed: RemovedComponents, mut map: Query<&mut Map>) { - for removed in removed.iter() { - for mut map in map.iter_mut() { - for entities in map.entities.iter_mut() { - entities.retain(|e| *e != removed); - } - } - } -} - -#[derive(Default, Deref, DerefMut)] -struct PreviousIndex(HashMap); - -fn entity_indexing( - mut map: Query<&mut Map>, - mut previous_index: ResMut, - query: Query<(Entity, &Coordinates), Changed>, -) { - for (entity, coordinates) in query.iter() { - for mut map in map.iter_mut() { - let idx = coordinates.to_index(map.width()); - if let Some(prev_idx) = previous_index.get(&entity) { - if idx != *prev_idx { - map.entities[*prev_idx].retain(|&e| e != entity); - } - } - map.entities[idx].insert(entity); - previous_index.insert(entity, idx); - } - } -} - fn add_areas(mut commands: Commands, query: Query<(Entity, &Map), (Added, Without)>) { for (entity, map) in query.iter() { let mut v = vec![]; @@ -403,8 +367,6 @@ fn add_areas(mut commands: Commands, query: Query<(Entity, &Map), (Added, W } } -pub const UPDATE_ENTITY_INDEX_LABEL: &str = "UPDATE_ENTITY_INDEX"; - pub struct MapPlugin; impl Plugin for MapPlugin { @@ -413,27 +375,9 @@ impl Plugin for MapPlugin { app.insert_resource(MapConfig::default()); } let config = app.world().get_resource::().unwrap().clone(); - const SPAWN_PORTALS: &str = "SPAWN_PORTALS"; app.register_type::() - .insert_resource(PreviousIndex::default()) .add_system(add_map_colliders.system()) - .add_system(entity_indexing.system().label(UPDATE_ENTITY_INDEX_LABEL)) - .add_system( - portal_spawner - .system() - .label(SPAWN_PORTALS) - .before(UPDATE_ENTITY_INDEX_LABEL), - ) - .add_system_to_stage( - CoreStage::PostUpdate, - remove_coordinates - .system() - .before(UPDATE_ENTITY_INDEX_LABEL), - ) - .add_system_to_stage( - CoreStage::PostUpdate, - entity_indexing.system().label(UPDATE_ENTITY_INDEX_LABEL), - ) + .add_system(portal_spawner.system()) .add_system(add_areas.system()) .add_system_to_stage(CoreStage::PostUpdate, add_areas.system()); if config.speak_area_descriptions { diff --git a/src/visibility.rs b/src/visibility.rs index f44d130..bb322fe 100644 --- a/src/visibility.rs +++ b/src/visibility.rs @@ -6,6 +6,7 @@ use derive_more::{Deref, DerefMut}; use shadowcast::{vision_distance, Context, InputGrid}; use crate::{ + bevy_rapier2d::prelude::*, core::{Angle, Coordinates, Player, PointLike}, log::Log, map::{ITileType, Map, MapConfig}, @@ -44,33 +45,16 @@ impl Viewshed { } } -#[derive(Clone, Debug, Default, Deref, DerefMut, Reflect)] -#[reflect(Component)] -pub struct VisibilityBlocked(pub Vec); - #[derive(Clone, Debug, Default, Deref, DerefMut, Reflect)] #[reflect(Component)] pub struct VisibleTiles(pub Vec); fn add_visibility_indices( mut commands: Commands, - query: Query< - (Entity, &Map), - ( - Added, - Without, - Without, - Without, - ), - >, + query: Query<(Entity, &Map), (Added, Without, Without)>, map_config: Res, ) { for (entity, map) in query.iter() { - let mut v = vec![]; - for tile in &map.base.tiles { - v.push(tile.blocks_visibility()); - } - commands.entity(entity).insert(VisibilityBlocked(v)); let count = map.count(); commands .entity(entity) @@ -81,85 +65,13 @@ fn add_visibility_indices( } } -pub(crate) fn set_visibility_blocked( - map: &Map, - index: usize, - visibility_blockers: &Query<&BlocksVisibility>, - visibility_blocked: &mut VisibilityBlocked, -) { - let mut new_visibility_blocked = map.base.tiles[index].blocks_visibility(); - if !new_visibility_blocked { - for e in &map.entities[index] { - if visibility_blockers.get(*e).is_ok() { - new_visibility_blocked = true; - break; - } - } - } - visibility_blocked[index] = new_visibility_blocked; -} +struct VisibilityGrid<'a, F>(&'a Map, F); -#[derive(Default, Deref, DerefMut)] -struct PrevIndex(HashMap); - -fn map_visibility_indexing( - mut map: Query<(&Map, &mut VisibilityBlocked)>, - mut prev_index: ResMut, - query: Query< - (Entity, &Coordinates), - ( - With, - Or<(Changed, Changed)>, - ), - >, - visibility_blockers: Query<&BlocksVisibility>, -) { - for (entity, coordinates) in query.iter() { - for (map, mut visibility_blocked) in map.iter_mut() { - let idx = coordinates.to_index(map.width()); - if let Some(prev_idx) = prev_index.get(&entity) { - if *prev_idx == idx { - continue; - } - set_visibility_blocked( - &map, - *prev_idx, - &visibility_blockers, - &mut visibility_blocked, - ); - } - visibility_blocked[idx] = true; - prev_index.insert(entity, idx); - } - } -} - -fn remove_blocks_visibility( - mut prev_index: ResMut, - mut map: Query<(&Map, &mut VisibilityBlocked)>, - removed: RemovedComponents, - coordinates: Query<&Coordinates>, - blocks_visibility: Query<&BlocksVisibility>, -) { - for entity in removed.iter() { - for (map, mut visibility_blocked) in map.iter_mut() { - let prev = prev_index.get(&entity).cloned(); - if let Some(prev) = prev { - prev_index.remove(&entity); - set_visibility_blocked(&map, prev, &blocks_visibility, &mut visibility_blocked); - } - if let Ok(coordinates) = coordinates.get_component::(entity) { - let idx = coordinates.to_index(map.width()); - set_visibility_blocked(&map, idx, &blocks_visibility, &mut visibility_blocked); - } - } - } -} - -struct VisibilityGrid(Map, VisibilityBlocked); - -impl InputGrid for VisibilityGrid { - type Grid = VisibilityGrid; +impl<'a, F> InputGrid for VisibilityGrid<'a, F> +where + F: Fn(Coord) -> u8, +{ + type Grid = VisibilityGrid<'a, F>; type Opacity = u8; @@ -167,115 +79,58 @@ impl InputGrid for VisibilityGrid { Size::new(grid.0.width() as u32, grid.0.height() as u32) } - fn get_opacity(&self, grid: &Self::Grid, coord: Coord) -> Self::Opacity { - let point = (coord.x, coord.y); - let index = point.to_index(grid.0.width()); - if grid.1 .0[index] { - 255 - } else { - 0 - } + fn get_opacity(&self, _grid: &Self::Grid, coord: Coord) -> Self::Opacity { + self.1(coord) } } -fn update_viewshed( - viewshed: &mut Viewshed, - start: &Coordinates, - map: &Map, - visibility_blocked: &VisibilityBlocked, -) { - let mut context: Context = Context::default(); - let vision_distance = vision_distance::Circle::new(viewshed.range); - let coord = Coord::new(start.x_i32(), start.y_i32()); - viewshed.visible.clear(); - let visibility_grid = VisibilityGrid(map.clone(), visibility_blocked.clone()); - context.for_each_visible( - coord, - &visibility_grid, - &visibility_grid, - vision_distance, - 255, - |coord, _directions, _visibility| { - viewshed.visible.insert((coord.x, coord.y)); - }, - ); -} - -fn update_viewshed_for_coordinates( +fn update_viewshed<'a>( mut viewers: Query<(&mut Viewshed, &Coordinates), Changed>, - map: Query<(&Map, &VisibilityBlocked)>, + map: Query<&Map>, + query_pipeline: Res, + collider_query: QueryPipelineColliderComponentsQuery, + blocks_visibility: Query<&BlocksVisibility>, ) { for (mut viewshed, start) in viewers.iter_mut() { - for (map, visibility_blocked) in map.iter() { - update_viewshed(&mut viewshed, start, &map, &visibility_blocked); + for map in map.iter() { + let mut context: Context = Context::default(); + let vision_distance = vision_distance::Circle::new(viewshed.range); + let coord = Coord::new(start.x_i32(), start.y_i32()); + viewshed.visible.clear(); + let collider_set = QueryPipelineColliderComponentsSet(&collider_query); + let shape = Cuboid::new(Vec2::new(0.5, 0.5).into()); + let visibility_grid = VisibilityGrid(map, |coord: Coord| { + let shape_pos = (Vec2::new(coord.x as f32, coord.y as f32), 0.); + let mut opacity = 0; + query_pipeline.intersections_with_shape( + &collider_set, + &shape_pos.into(), + &shape, + InteractionGroups::all(), + Some(&|v| blocks_visibility.get(v.entity()).is_ok()), + |_| { + opacity = 255; + false + }, + ); + opacity + }); + context.for_each_visible( + coord, + &visibility_grid, + &visibility_grid, + vision_distance, + 255, + |coord, _directions, _visibility| { + viewshed.visible.insert((coord.x, coord.y)); + }, + ); } } } -#[derive(Clone, Debug, Default, Deref, DerefMut)] -struct PrevVisibilityBlocked(Vec); - -fn visibility_blocked_added( - mut prev_visibility_blocked: ResMut, - added: Query<&VisibilityBlocked, Added>, -) { - for visibility_blocked in added.iter() { - **prev_visibility_blocked = visibility_blocked.to_vec(); - } -} - -fn visibility_blocked_removed( - mut prev_visibility_blocked: ResMut, - removed: RemovedComponents, -) { - for _ in removed.iter() { - prev_visibility_blocked.clear(); - } -} - -fn update_viewshed_for_visibility_blocked( - mut prev_visibility_blocked: ResMut, - map: Query<(&Map, &VisibilityBlocked), Changed>, - mut queryset: QuerySet<( - Query<(Entity, &Viewshed)>, - Query<(&mut Viewshed, &Coordinates)>, - )>, -) { - for (map, visibility_blocked) in map.iter() { - let mut to_check = vec![]; - for (index, new) in visibility_blocked.iter().enumerate() { - let changed = if let Some(prev) = prev_visibility_blocked.get(index) { - prev != new - } else { - true - }; - if changed { - let coords = (index % map.width(), index / map.width()); - for (entity, viewshed) in queryset.q0().iter() { - if viewshed.is_visible(&coords) { - to_check.push(entity); - } - } - } - } - to_check.dedup(); - for entity in to_check { - if let Ok((mut viewshed, coordinates)) = queryset.q1_mut().get_mut(entity) { - update_viewshed(&mut viewshed, coordinates, &map, &visibility_blocked); - } - } - **prev_visibility_blocked = visibility_blocked.to_vec(); - } -} - fn update_visible_and_revealed_tiles( - mut map: Query< - (&Map, &mut RevealedTiles, &mut VisibleTiles), - ( - With, - Or<(Changed, Changed)>, - ), - >, + mut map: Query<(&Map, &mut RevealedTiles, &mut VisibleTiles)>, viewers: Query<&Viewshed, With>, ) { for (map, mut revealed_tiles, mut visible_tiles) in map.iter_mut() { @@ -296,9 +151,10 @@ fn log_visible( time: Res