More collider-based visibility work.

This commit is contained in:
Nolan Darilek 2021-09-21 12:58:57 -05:00
parent 42912e59a8
commit 4e9fcef178

View File

@ -86,6 +86,12 @@ pub enum VisibilityChanged {
Lost { viewer: Entity, viewed: Entity },
}
#[derive(Clone, Debug, Default, Deref, DerefMut)]
struct VisibilityColliderToViewshed(HashMap<Entity, Entity>);
#[derive(Clone, Debug, Default, Deref, DerefMut)]
struct ViewshedToVisibilityCollider(HashMap<Entity, Entity>);
impl PointLike for Coord {
fn x(&self) -> f32 {
self.x as f32
@ -112,49 +118,59 @@ fn add_visibility_indices(
}
}
fn viewshed_added(mut commands: Commands, query: Query<Entity, Added<Viewshed>>) {
fn viewshed_added(
mut commands: Commands,
mut collider_to_viewshed: ResMut<VisibilityColliderToViewshed>,
mut viewshed_to_collider: ResMut<ViewshedToVisibilityCollider>,
query: Query<Entity, Added<Viewshed>>,
) {
for entity in query.iter() {
let id = commands.spawn().insert(VisibilityCollider).id();
commands.entity(entity).push_children(&[id]);
let id = commands
.spawn_bundle(ColliderBundle {
collider_type: ColliderType::Sensor,
flags: ColliderFlags {
active_collision_types: ActiveCollisionTypes::all(),
active_events: ActiveEvents::INTERSECTION_EVENTS,
..Default::default()
},
..Default::default()
})
.insert(VisibilityCollider)
.id();
viewshed_to_collider.insert(entity, id);
collider_to_viewshed.insert(id, entity);
}
}
fn viewshed_changed(
mut commands: Commands,
viewshed_to_collider: Res<ViewshedToVisibilityCollider>,
query: Query<(Entity, &Viewshed, &RigidBodyPosition), Changed<Viewshed>>,
mut collider_data: Query<
(&Parent, Entity, Option<&mut ColliderShape>),
(Option<&mut ColliderShape>, Option<&mut ColliderPosition>),
With<VisibilityCollider>,
>,
) {
for (entity, viewshed, position) in query.iter() {
for (parent, child_entity, collider_shape) in collider_data.iter_mut() {
if **parent != entity {
continue;
}
if viewshed.visible_points.len() < 2 {
commands.entity(child_entity).remove::<ColliderShape>();
} else {
let position = position.position;
let mut points = vec![];
for p in &viewshed.visible_points {
points.push(point!(
position.translation.x - p.x(),
position.translation.y - p.y()
));
}
if let Some(shape) = ColliderShape::convex_hull(&points) {
//println!("Shape for {:?}", points);
if let Some(mut collider_shape) = collider_shape {
*collider_shape = shape;
} else {
//println!("Creating collider at {:?}", coordinates.i32());
commands.entity(child_entity).insert_bundle(ColliderBundle {
collider_type: ColliderType::Sensor,
shape,
flags: ActiveEvents::INTERSECTION_EVENTS.into(),
..Default::default()
});
if let Some(collider_entity) = viewshed_to_collider.get(&entity) {
if let Ok((collider_shape, collider_position)) = collider_data.get_mut(*collider_entity)
{
if viewshed.visible_points.len() < 2 {
println!("Too few points, removing");
commands.entity(*collider_entity).remove::<ColliderShape>();
} else {
let translation = position.position.translation;
let mut points = vec![];
for p in &viewshed.visible_points {
points.push(point!(p.x() - translation.x, p.y() - translation.y));
}
if let Some(shape) = ColliderShape::convex_hull(&points) {
if let (Some(mut collider_shape), Some(mut collider_position)) =
(collider_shape, collider_position)
{
*collider_shape = shape;
*collider_position = translation.into();
}
}
}
}
@ -162,37 +178,16 @@ fn viewshed_changed(
}
}
fn lock_rotation(
mut query: Query<(&Parent, &mut ColliderPosition), With<VisibilityCollider>>,
positions: Query<&RigidBodyPosition>,
) {
for (parent, mut position) in query.iter_mut() {
if let Ok(parent_position) = positions.get(**parent) {
let delta = parent_position
.position
.rotation
.angle_to(&UnitComplex::new(0.));
println!("Syncing: {:?}", delta);
position.rotation = UnitComplex::new(delta);
}
}
}
fn viewshed_removed(
mut commands: Commands,
mut collider_to_viewshed: ResMut<VisibilityColliderToViewshed>,
mut viewshed_to_collider: ResMut<ViewshedToVisibilityCollider>,
query: RemovedComponents<Viewshed>,
children: Query<&Children>,
collider_shapes: Query<Entity, With<VisibilityCollider>>,
) {
for entity in query.iter() {
if let Ok(children) = children.get(entity) {
for child in children.iter() {
if collider_shapes.get(*child).is_ok() {
commands.entity(*child).despawn_recursive();
break;
}
}
if let Some(collider) = viewshed_to_collider.get(&entity) {
collider_to_viewshed.remove(&collider);
}
viewshed_to_collider.remove(&entity);
}
}
@ -245,10 +240,9 @@ fn update_viewshed(
|handle| {
if let Ok(blocks_visibility) = blocks_visibility.get(handle.entity()) {
opacity = **blocks_visibility;
false
} else {
true
}
true
},
);
opacity
@ -373,38 +367,35 @@ fn update_visible_and_revealed_tiles(
fn intersection(
mut events: EventReader<IntersectionEvent>,
colliders: Query<&Parent, With<VisibilityCollider>>,
collider_to_viewshed: Res<VisibilityColliderToViewshed>,
colliders: Query<Entity, With<VisibilityCollider>>,
mut viewers: Query<&mut VisibleEntities>,
names: Query<&Name>,
parents: Query<&Parent>,
mut visibility_changed: EventWriter<VisibilityChanged>,
) {
for event in events.iter() {
println!("{:?}", event);
if let Some((visibility_collider, other)) =
target_and_other(event.collider1.entity(), event.collider2.entity(), |v| {
colliders.get(v).is_ok()
})
{
if let Ok(parent) = colliders.get(visibility_collider) {
if let Ok(mut visible_entities) = viewers.get_mut(**parent) {
if let Some(viewshed_entity) = collider_to_viewshed.get(&visibility_collider) {
if let Ok(mut visible_entities) = viewers.get_mut(*viewshed_entity) {
if event.intersecting {
println!("{:?} is visible: {:?}", other, names.get(other));
if let Ok(p) = parents.get(other) {
println!("{:?}", names.get(**p));
}
visibility_changed.send(VisibilityChanged::Gained {
viewer: **parent,
viewer: *viewshed_entity,
viewed: other,
});
visible_entities.insert(other);
} else {
println!("{:?} is not visible", other);
visibility_changed.send(VisibilityChanged::Lost {
viewer: **parent,
viewer: *viewshed_entity,
viewed: other,
});
visible_entities.remove(&other);
if visible_entities.contains(&other) {
visible_entities.remove(&other);
} else {
println!("Making invisible something that was never visible");
}
}
}
}
@ -413,65 +404,42 @@ fn intersection(
}
fn log_visible(
time: Res<Time>,
mut recently_lost: Local<HashMap<Entity, Timer>>,
mut events: EventReader<IntersectionEvent>,
// time: Res<Time>,
// mut recently_lost: Local<HashMap<Entity, Timer>>,
mut events: EventReader<VisibilityChanged>,
mut log: Query<&mut Log>,
viewer_colliders: Query<&Parent, With<VisibilityCollider>>,
viewers: Query<(Entity, &Coordinates, &Transform), (With<Player>, With<Viewshed>)>,
viewed: Query<
(
Entity,
&Name,
Option<&RigidBodyPosition>,
Option<&ColliderPosition>,
),
vieweds: Query<
(&Name, Option<&RigidBodyPosition>, Option<&ColliderPosition>),
Without<DontLogWhenVisible>,
>,
) {
for timer in recently_lost.values_mut() {
/*for timer in recently_lost.values_mut() {
timer.tick(time.delta());
}
recently_lost.retain(|_entity, timer| !timer.finished());
}*/
//recently_lost.retain(|_entity, timer| !timer.finished());
for event in events.iter() {
if let Some((viewer_collider, other)) =
target_and_other(event.collider1.entity(), event.collider2.entity(), |v| {
viewer_colliders.get(v).is_ok()
})
{
if let Ok((viewed_entity, name, rigid_body_position, collider_position)) =
viewed.get(other)
if let VisibilityChanged::Gained { viewer, viewed } = event {
if let Ok((viewer_entity, viewer_coordinates, viewer_transform)) = viewers.get(*viewer)
{
if event.intersecting {
if recently_lost.contains_key(&viewed_entity) {
recently_lost.remove(&viewed_entity);
} else {
if let Ok(parent) = viewer_colliders.get(viewer_collider) {
if let Ok((viewer_entity, coordinates, transform)) =
viewers.get(**parent)
{
if viewer_entity == viewed_entity {
continue;
}
if let Ok(mut log) = log.single_mut() {
let viewed_coordinates = if let Some(p) = rigid_body_position {
(p.position.translation.x, p.position.translation.y)
} else if let Some(p) = collider_position {
(p.translation.x, p.translation.y)
} else {
(0., 0.)
};
let forward = transform.local_x();
let yaw = Angle::Radians(forward.y.atan2(forward.x));
let location = coordinates
.direction_and_distance(&viewed_coordinates, Some(yaw));
log.push(format!("{}: {}", **name, location));
}
}
}
if *viewed == viewer_entity {
continue;
}
if let Ok((name, body_position, collider_position)) = vieweds.get(*viewed) {
if let Ok(mut log) = log.single_mut() {
let viewed_coordinates = if let Some(p) = body_position {
(p.position.translation.x, p.position.translation.y)
} else if let Some(p) = collider_position {
(p.translation.x, p.translation.y)
} else {
(0., 0.)
};
let forward = viewer_transform.local_x();
let yaw = Angle::Radians(forward.y.atan2(forward.x));
let location = viewer_coordinates
.direction_and_distance(&viewed_coordinates, Some(yaw));
log.push(format!("{}: {}", **name, location));
}
} else {
recently_lost.insert(viewed_entity, Timer::from_seconds(2., false));
}
}
}
@ -484,11 +452,12 @@ pub struct VisibilityPlugin;
impl Plugin for VisibilityPlugin {
fn build(&self, app: &mut AppBuilder) {
app.add_event::<VisibilityChanged>()
app.init_resource::<VisibilityColliderToViewshed>()
.init_resource::<ViewshedToVisibilityCollider>()
.add_event::<VisibilityChanged>()
.add_system(add_visibility_indices.system())
.add_system(viewshed_added.system())
.add_system(viewshed_changed.system())
.add_system(lock_rotation.system())
.add_system_to_stage(CoreStage::PostUpdate, viewshed_removed.system())
.add_system(update_viewshed_for_coordinates.system())
.add_system(update_viewshed_for_start.system())