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 {
let x = x as f32;
let y = y as f32;
let x = x as f32 + 0.5;
let y = y as f32 + 0.5;
if !portals.contains(&(x, y)) {
portals.push((x, y));
}
}
}
}
for portal in portals {
let x = portal.0 as f32 + 0.5;
let y = portal.1 as f32 + 0.5;
for (x, y) in portals {
let portal = commands
.spawn_bundle(PortalBundle {
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();
app.register_type::<Portal>()
.add_system(spawn_colliders::<D>)
.add_system(spawn_portals::<D>)
.add_system(spawn_portal_colliders::<D>);
.add_system_to_stage(
CoreStage::PreUpdate,
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 {
app.add_system_to_stage(CoreStage::PostUpdate, area_description);
}

View File

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