Make visibility colliders the single source of truth for calculations.
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Nolan Darilek 2022-07-11 10:26:03 -05:00
parent 6ff5eb15a9
commit 1ff98c9132
2 changed files with 40 additions and 37 deletions

View File

@ -294,17 +294,15 @@ fn spawn_portals<D: 'static + Clone + Default + Send + Sync>(
} }
} }
if spawn_portal { if spawn_portal {
let x = x as f32; let x = x as f32 + 0.5;
let y = y as f32; let y = y as f32 + 0.5;
if !portals.contains(&(x, y)) { if !portals.contains(&(x, y)) {
portals.push((x, y)); portals.push((x, y));
} }
} }
} }
} }
for portal in portals { for (x, y) in portals {
let x = portal.0 as f32 + 0.5;
let y = portal.1 as f32 + 0.5;
let portal = commands let portal = commands
.spawn_bundle(PortalBundle { .spawn_bundle(PortalBundle {
transform: Transform::from_translation(Vec3::new(x, y, 0.)), transform: Transform::from_translation(Vec3::new(x, y, 0.)),
@ -387,9 +385,12 @@ impl<D: 'static + Clone + Default + Send + Sync> Plugin for MapPlugin<D> {
} }
let config = app.world.get_resource::<MapConfig>().unwrap().clone(); let config = app.world.get_resource::<MapConfig>().unwrap().clone();
app.register_type::<Portal>() app.register_type::<Portal>()
.add_system(spawn_colliders::<D>) .add_system_to_stage(
.add_system(spawn_portals::<D>) CoreStage::PreUpdate,
.add_system(spawn_portal_colliders::<D>); spawn_colliders::<D>.before(crate::visibility::UPDATE_VIEWSHED_LABEL),
)
.add_system_to_stage(CoreStage::PreUpdate, spawn_portals::<D>)
.add_system_to_stage(CoreStage::PreUpdate, spawn_portal_colliders::<D>);
if config.speak_area_descriptions { if config.speak_area_descriptions {
app.add_system_to_stage(CoreStage::PostUpdate, area_description); app.add_system_to_stage(CoreStage::PostUpdate, area_description);
} }

View File

@ -5,7 +5,7 @@ use std::{
}; };
use bevy::{core::FixedTimestep, prelude::*}; use bevy::{core::FixedTimestep, prelude::*};
use bevy_rapier2d::na::{self, Point2};
use coord_2d::{Coord, Size}; use coord_2d::{Coord, Size};
use shadowcast::{vision_distance, Context, InputGrid}; use shadowcast::{vision_distance, Context, InputGrid};
@ -14,7 +14,7 @@ use crate::{
commands::RunIfExistsExt, commands::RunIfExistsExt,
core::{Angle, Player, PointLike}, core::{Angle, Player, PointLike},
log::Log, log::Log,
map::{ITileType, Map, MapConfig}, map::{Map, MapConfig, MapObstruction},
}; };
#[derive(Component, Clone, Copy, Debug, Default, Reflect)] #[derive(Component, Clone, Copy, Debug, Default, Reflect)]
@ -57,19 +57,20 @@ impl Viewshed {
rapier_context: &RapierContext, rapier_context: &RapierContext,
map: &Map<D>, map: &Map<D>,
visible_query: &Query<&Visible>, visible_query: &Query<&Visible>,
obstructions_query: &Query<&MapObstruction>,
events: &mut EventWriter<VisibilityChanged>, events: &mut EventWriter<VisibilityChanged>,
cache: &mut HashMap<Coord, (u8, HashSet<Entity>)>, cache: &mut HashMap<Coord, (u8, HashSet<Entity>)>,
) { ) {
let mut context: Context<u8> = Context::default(); let mut context: Context<u8> = Context::default();
let vision_distance = vision_distance::Circle::new(self.range); let vision_distance = vision_distance::Circle::new(self.range);
let shape = Collider::cuboid(0.5 - f32::EPSILON, 0.5 - f32::EPSILON); let shape = Collider::cuboid(0.5 - f32::EPSILON, 0.5 - f32::EPSILON);
let origin = Point2::new(start.x().trunc(), start.y().trunc());
let mut new_visible_entities = HashSet::new(); let mut new_visible_entities = HashSet::new();
let size = (map.width as u32, map.height as u32);
let visibility_grid = VisibilityGrid( let visibility_grid = VisibilityGrid(
map, size,
RefCell::new(Box::new(|coord: Coord| { RefCell::new(Box::new(|coord: Coord| {
let dest = Point2::new(coord.x as f32, coord.y as f32); let shape_pos = Vec2::new(coord.x as f32 + 0.5, coord.y as f32 + 0.5);
if na::distance(&origin, &dest) >= self.range as f32 { if start.distance(&shape_pos) > self.range as f32 + 1. {
return u8::MAX; return u8::MAX;
} }
if let Some((opacity, entities)) = cache.get(&coord) { if let Some((opacity, entities)) = cache.get(&coord) {
@ -82,14 +83,8 @@ impl Viewshed {
return *opacity; return *opacity;
} }
} }
let tile = map.at(coord.x as usize, coord.y as usize);
if tile.blocks_visibility() {
cache.insert(coord, (u8::MAX, HashSet::new()));
return u8::MAX;
}
let mut opacity = 0; let mut opacity = 0;
let mut entities = HashSet::new(); let mut entities = HashSet::new();
let shape_pos = Vec2::new(dest.x + 0.5, dest.y + 0.5);
rapier_context.intersections_with_shape( rapier_context.intersections_with_shape(
shape_pos, shape_pos,
0., 0.,
@ -97,20 +92,24 @@ impl Viewshed {
InteractionGroups::all(), InteractionGroups::all(),
Some(&|v| visible_query.get(v).is_ok()), Some(&|v| visible_query.get(v).is_ok()),
|entity| { |entity| {
let obstruction = obstructions_query.get(entity).is_ok();
if obstruction {
entities.clear();
}
entities.insert(entity); entities.insert(entity);
if let Ok(visible) = visible_query.get(entity) { if let Ok(visible) = visible_query.get(entity) {
opacity = opacity.max(**visible); opacity = opacity.max(**visible);
} }
true !obstruction
}, },
); );
for e in &entities { for e in &entities {
new_visible_entities.insert(*e); new_visible_entities.insert(*e);
} }
cache.insert(coord, (opacity, entities.clone()));
if entities.contains(viewer_entity) { if entities.contains(viewer_entity) {
0 0
} else { } else {
cache.insert(coord, (opacity, entities));
opacity opacity
} }
})), })),
@ -119,9 +118,9 @@ impl Viewshed {
context.for_each_visible( context.for_each_visible(
Coord::new(start.x_i32(), start.y_i32()), Coord::new(start.x_i32(), start.y_i32()),
&visibility_grid, &visibility_grid,
&visibility_grid, &size,
vision_distance, vision_distance,
255, u8::MAX,
|coord, _directions, _visibility| { |coord, _directions, _visibility| {
new_visible.insert((coord.x, coord.y)); new_visible.insert((coord.x, coord.y));
}, },
@ -230,22 +229,18 @@ fn viewshed_removed(
} }
} }
pub struct VisibilityGrid<'a, D: 'static + Clone + Default + Send + Sync, F>( pub struct VisibilityGrid<F>(pub (u32, u32), pub RefCell<Box<F>>);
pub &'a Map<D>,
pub RefCell<Box<F>>,
);
impl<'a, D, F> InputGrid for VisibilityGrid<'a, D, F> impl<F> InputGrid for VisibilityGrid<F>
where where
D: 'static + Clone + Default + Send + Sync,
F: FnMut(Coord) -> u8, F: FnMut(Coord) -> u8,
{ {
type Grid = VisibilityGrid<'a, D, F>; type Grid = (u32, u32);
type Opacity = u8; type Opacity = u8;
fn size(&self, grid: &Self::Grid) -> Size { fn size(&self, grid: &Self::Grid) -> Size {
Size::new(grid.0.width as u32, grid.0.height as u32) Size::new(grid.0, grid.1)
} }
fn get_opacity(&self, _grid: &Self::Grid, coord: Coord) -> Self::Opacity { fn get_opacity(&self, _grid: &Self::Grid, coord: Coord) -> Self::Opacity {
@ -261,6 +256,7 @@ fn update_viewshed<D: 'static + Clone + Default + Send + Sync>(
Or<(Changed<Transform>, Changed<Visible>)>, Or<(Changed<Transform>, Changed<Visible>)>,
>, >,
visible: Query<&Visible>, visible: Query<&Visible>,
obstructions: Query<&MapObstruction>,
mut viewers: Query<(Entity, &mut Viewshed, &mut VisibleEntities, &Transform)>, mut viewers: Query<(Entity, &mut Viewshed, &mut VisibleEntities, &Transform)>,
map: Query<&Map<D>>, map: Query<&Map<D>>,
rapier_context: Res<RapierContext>, rapier_context: Res<RapierContext>,
@ -321,6 +317,7 @@ fn update_viewshed<D: 'static + Clone + Default + Send + Sync>(
&*rapier_context, &*rapier_context,
map, map,
&visible, &visible,
&obstructions,
&mut changed, &mut changed,
&mut cache, &mut cache,
); );
@ -335,6 +332,7 @@ fn remove_visible<D: 'static + Clone + Default + Send + Sync>(
map: Query<&Map<D>>, map: Query<&Map<D>>,
rapier_context: Res<RapierContext>, rapier_context: Res<RapierContext>,
visible: Query<&Visible>, visible: Query<&Visible>,
obstructions: Query<&MapObstruction>,
mut changed: EventWriter<VisibilityChanged>, mut changed: EventWriter<VisibilityChanged>,
) { ) {
for removed in removed.iter() { for removed in removed.iter() {
@ -352,6 +350,7 @@ fn remove_visible<D: 'static + Clone + Default + Send + Sync>(
&*rapier_context, &*rapier_context,
map, map,
&visible, &visible,
&obstructions,
&mut changed, &mut changed,
&mut cache, &mut cache,
); );
@ -430,19 +429,22 @@ impl<D: 'static + Clone + Default + Send + Sync> Default for VisibilityPlugin<D>
} }
} }
pub const UPDATE_VIEWSHED_LABEL: &str = "UPDATE_VIEWSHED";
impl<D: 'static + Clone + Default + Send + Sync> Plugin for VisibilityPlugin<D> { impl<D: 'static + Clone + Default + Send + Sync> Plugin for VisibilityPlugin<D> {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_event::<VisibilityChanged>() app.add_event::<VisibilityChanged>()
.add_system(add_visibility_indices::<D>) .add_system_to_stage(CoreStage::PreUpdate, add_visibility_indices::<D>)
.add_system_set_to_stage( .add_system_set_to_stage(
CoreStage::PostUpdate, CoreStage::PreUpdate,
SystemSet::new() SystemSet::new()
.with_run_criteria(FixedTimestep::step(0.05)) .with_run_criteria(FixedTimestep::step(0.05))
.with_system(update_viewshed::<D>), .with_system(update_viewshed::<D>)
.label(UPDATE_VIEWSHED_LABEL),
) )
.add_system_to_stage(CoreStage::PostUpdate, viewshed_removed) .add_system_to_stage(CoreStage::PostUpdate, viewshed_removed)
.add_system_to_stage(CoreStage::PostUpdate, remove_visible::<D>) .add_system_to_stage(CoreStage::PostUpdate, remove_visible::<D>)
.add_system_to_stage(CoreStage::PostUpdate, update_revealed_tiles::<D>) .add_system_to_stage(CoreStage::PreUpdate, update_revealed_tiles::<D>)
.add_system_to_stage(CoreStage::PostUpdate, log_visible); .add_system_to_stage(CoreStage::PreUpdate, log_visible);
} }
} }