Various mapping/collision/visibility tweaks.

This commit is contained in:
Nolan Darilek 2024-08-11 09:08:20 -05:00
parent 5a746660e1
commit afca116394
4 changed files with 110 additions and 109 deletions

View File

@ -185,22 +185,23 @@ fn spawn_colliders<D: 'static + Clone + Default + Send + Sync>(
) { ) {
for (map_entity, map, spawn_colliders) in &maps { for (map_entity, map, spawn_colliders) in &maps {
if **spawn_colliders { if **spawn_colliders {
commands commands.entity(map_entity).remove::<SpawnColliders>();
.entity(map_entity)
.remove::<SpawnColliders>()
.insert(RigidBody::Fixed);
for y in 0..map.height { for y in 0..map.height {
for x in 0..map.width { for x in 0..map.width {
if let Some(tile) = map.at(x, y) { if let Some(tile) = map.at(x, y) {
if tile.blocks_motion() { if tile.blocks_motion() {
let id = commands let id = commands
.spawn(( .spawn((
RigidBody::Fixed,
TransformBundle::from_transform(Transform::from_xyz( TransformBundle::from_transform(Transform::from_xyz(
x as f32 + 0.5, x as f32 + 0.5,
y as f32 + 0.5, y as f32 + 0.5,
0., 0.,
)), )),
Collider::cuboid(0.5, 0.5), Collider::cuboid(0.5, 0.5),
ActiveCollisionTypes::default()
| ActiveCollisionTypes::KINEMATIC_STATIC
| ActiveCollisionTypes::DYNAMIC_STATIC,
MapObstruction, MapObstruction,
)) ))
.id(); .id();

View File

@ -6,7 +6,7 @@ use bevy_tts::Tts;
use leafwing_input_manager::{axislike::DualAxisData, prelude::*}; use leafwing_input_manager::{axislike::DualAxisData, prelude::*};
use crate::{ use crate::{
core::{Angle, Area, CardinalDirection, GlobalTransformExt, Player}, core::{Angle, Area, CardinalDirection, GlobalTransformExt, Player, TransformExt},
error::error_handler, error::error_handler,
exploration::{ExplorationFocused, Exploring}, exploration::{ExplorationFocused, Exploring},
log::Log, log::Log,
@ -136,6 +136,7 @@ fn tick_snap_timers(time: Res<Time>, mut snap_timers: ResMut<SnapTimers>) {
fn controls( fn controls(
mut commands: Commands, mut commands: Commands,
rapier_context: Res<RapierContext>,
time: Res<Time>, time: Res<Time>,
snap_timers: Res<SnapTimers>, snap_timers: Res<SnapTimers>,
mut query: Query<( mut query: Query<(
@ -148,7 +149,7 @@ fn controls(
Option<&ForwardMovementFactor>, Option<&ForwardMovementFactor>,
Option<&StrafeMovementFactor>, Option<&StrafeMovementFactor>,
&mut Transform, &mut Transform,
Option<&mut KinematicCharacterController>, &Collider,
)>, )>,
exploration_focused: Query<Entity, With<ExplorationFocused>>, exploration_focused: Query<Entity, With<ExplorationFocused>>,
) { ) {
@ -162,7 +163,7 @@ fn controls(
forward_movement_factor, forward_movement_factor,
strafe_movement_factor, strafe_movement_factor,
mut transform, mut transform,
character_controller, collider,
) in &mut query ) in &mut query
{ {
let mut cleanup = false; let mut cleanup = false;
@ -176,22 +177,21 @@ fn controls(
backward_movement_factor.map(|v| v.0).unwrap_or_else(|| 1.); backward_movement_factor.map(|v| v.0).unwrap_or_else(|| 1.);
let strafe_movement_factor = let strafe_movement_factor =
strafe_movement_factor.map(|v| v.0).unwrap_or_else(|| 1.); strafe_movement_factor.map(|v| v.0).unwrap_or_else(|| 1.);
let forward_backward_movement_factor = if direction.y > 0. { let forward_backward_movement_factor = if direction.x > 0. {
forward_movement_factor forward_movement_factor
} else if direction.y < 0. { } else if direction.x < 0. {
backward_movement_factor backward_movement_factor
} else { } else {
1. 1.
}; };
let movement_factor = if direction.x != 0. && direction.y != 0. { let movement_factor = if direction.x != 0. && direction.y != 0. {
strafe_movement_factor.min(forward_backward_movement_factor) strafe_movement_factor.min(forward_backward_movement_factor)
} else if direction.x != 0. { } else if direction.y != 0. {
strafe_movement_factor strafe_movement_factor
} else { } else {
forward_backward_movement_factor forward_backward_movement_factor
}; };
trace!("{entity:?}: move: {direction:?}"); trace!("{entity:?}: move: {direction:?}");
direction = Vec2::new(direction.y, -direction.x);
direction = transform direction = transform
.compute_matrix() .compute_matrix()
.transform_vector3(direction.extend(0.)) .transform_vector3(direction.extend(0.))
@ -199,19 +199,11 @@ fn controls(
let mut speed = **speed; let mut speed = **speed;
speed *= movement_factor; speed *= movement_factor;
let velocity = direction * speed; let velocity = direction * speed;
if character_controller.is_some() { // println!("{entity:?}: SetLinearVelocity: {velocity:?}");
let translation = velocity * time.delta_seconds(); actions.press(&NavigationAction::SetLinearVelocity);
actions.press(&NavigationAction::Translate); actions
actions .action_data_mut_or_default(&NavigationAction::SetLinearVelocity)
.action_data_mut_or_default(&NavigationAction::Translate) .axis_pair = Some(DualAxisData::from_xy(velocity));
.axis_pair = Some(DualAxisData::from_xy(translation));
} else {
// println!("{entity:?}: SetLinearVelocity: {velocity:?}");
actions.press(&NavigationAction::SetLinearVelocity);
actions
.action_data_mut_or_default(&NavigationAction::SetLinearVelocity)
.axis_pair = Some(DualAxisData::from_xy(velocity));
}
} }
} else if actions.just_released(&NavigationAction::Move) { } else if actions.just_released(&NavigationAction::Move) {
trace!("{entity:?}: Stopped moving"); trace!("{entity:?}: Stopped moving");
@ -234,21 +226,32 @@ fn controls(
velocity.linvel = Vec2::ZERO; velocity.linvel = Vec2::ZERO;
actions actions
.action_data_mut_or_default(&NavigationAction::SetLinearVelocity) .action_data_mut_or_default(&NavigationAction::SetLinearVelocity)
.axis_pair = Some(DualAxisData::from_xy(Vec2::ZERO)); .axis_pair = None;
} }
if actions.pressed(&NavigationAction::Translate) { if actions.pressed(&NavigationAction::Translate) {
if let Some(pair) = actions.axis_pair(&NavigationAction::Translate) { if let Some(pair) = actions.axis_pair(&NavigationAction::Translate) {
if let Some(mut character_controller) = character_controller { let vel = pair.xy();
character_controller.translation = Some(pair.xy()); if rapier_context
.cast_shape(
transform.translation.truncate(),
transform.yaw().radians(),
vel,
collider,
1.,
true,
QueryFilter::new()
.exclude_sensors()
.exclude_collider(entity),
)
.is_none()
{
transform.translation += vel.extend(0.);
} }
} }
} else if actions.just_released(&NavigationAction::Translate) { } else if actions.just_released(&NavigationAction::Translate) {
if let Some(mut character_controller) = character_controller {
character_controller.translation = None;
}
actions actions
.action_data_mut_or_default(&NavigationAction::Translate) .action_data_mut_or_default(&NavigationAction::Translate)
.axis_pair = Some(DualAxisData::from_xy(Vec2::ZERO)); .axis_pair = None;
} }
if !snap_timers.contains_key(&entity) { if !snap_timers.contains_key(&entity) {
if let Some(rotation_speed) = rotation_speed { if let Some(rotation_speed) = rotation_speed {

View File

@ -15,7 +15,7 @@ use pathfinding::prelude::*;
use crate::{ use crate::{
core::{PointLike, TransformExt}, core::{PointLike, TransformExt},
map::{Map, MapObstruction}, map::{Map, MapObstruction},
navigation::{NavigationAction, RotationSpeed}, navigation::{NavigationAction, RotationSpeed, Speed},
}; };
#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug, Reflect)] #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug, Reflect)]
@ -105,24 +105,18 @@ fn find_path_for_shape(
((x - 1, y + 1), 1.5), ((x - 1, y + 1), 1.5),
((x + 1, y + 1), 1.5), ((x + 1, y + 1), 1.5),
]; ];
let pos = Vector2::new(x as f32, y as f32);
let shape_pos = Isometry2::new(pos, 0.);
for exit in &exits { for exit in &exits {
let mut should_push = true; let mut should_push = true;
let dest = Vector2::new(exit.0 .0 as f32, exit.0 .1 as f32); let dest = Isometry2::new(Vector2::new(exit.0 .0 as f32, exit.0 .1 as f32), 0.);
let shape_vel = dest - pos;
let max_toi = 1.;
if query_pipeline if query_pipeline
.cast_shape( .intersection_with_shape(
&rigid_body_set, &rigid_body_set,
&collider_set, &collider_set,
&shape_pos, &dest,
&shape_vel,
&*shape.raw, &*shape.raw,
max_toi,
true,
bevy_rapier2d::rapier::pipeline::QueryFilter::new() bevy_rapier2d::rapier::pipeline::QueryFilter::new()
.predicate(&|h, _c| h != initiator), .exclude_sensors()
.exclude_collider(initiator),
) )
.is_some() .is_some()
{ {
@ -177,7 +171,7 @@ fn calculate_path(
continue; continue;
} }
trace!( trace!(
"Calculating path from {:?}", "{entity:?}: Calculating path from {:?} to {destination:?}",
coordinates.translation.truncate().i32() coordinates.translation.truncate().i32()
); );
let coordinates_clone = *coordinates; let coordinates_clone = *coordinates;
@ -186,7 +180,7 @@ fn calculate_path(
let cost_map_clone = cost_map.cloned(); let cost_map_clone = cost_map.cloned();
let handle_clone = *handle; let handle_clone = *handle;
let mut collider_set = rapier_context.colliders.clone(); let mut collider_set = rapier_context.colliders.clone();
let mut to_remove = vec![]; let mut to_remove = vec![handle.0];
for handle in collider_set.iter() { for handle in collider_set.iter() {
if !obstructions.iter().map(|v| v.0).any(|x| x == handle.0) { if !obstructions.iter().map(|v| v.0).any(|x| x == handle.0) {
to_remove.push(handle.0); to_remove.push(handle.0);
@ -225,14 +219,21 @@ fn calculate_path(
} }
} }
fn poll_tasks(mut commands: Commands, mut query: Query<(Entity, &mut Calculating)>) { fn poll_tasks(
for (entity, mut calculating) in &mut query { mut commands: Commands,
mut query: Query<(Entity, &mut Calculating, &Transform, &Destination)>,
) {
for (entity, mut calculating, transform, destination) in &mut query {
if let Some(result) = future::block_on(future::poll_once(&mut **calculating)) { if let Some(result) = future::block_on(future::poll_once(&mut **calculating)) {
if let Some(path) = result { if let Some(path) = result {
trace!("{entity:?}: Path: {path:?}"); trace!("{entity:?}: Path: {path:?}");
commands.entity(entity).insert(path); commands.entity(entity).insert(path);
} else { } else {
trace!("{entity:?}: path: no path"); trace!(
"{entity:?}: path: no path from {:?} to {:?}",
transform.translation.truncate().i32(),
**destination
);
commands.entity(entity).insert(NoPath); commands.entity(entity).insert(NoPath);
} }
commands.entity(entity).remove::<Calculating>(); commands.entity(entity).remove::<Calculating>();
@ -254,77 +255,81 @@ fn remove_destination(
fn negotiate_path( fn negotiate_path(
mut commands: Commands, mut commands: Commands,
time: Res<Time>,
physics_config: Res<RapierConfiguration>,
mut query: Query<( mut query: Query<(
Entity, Entity,
&mut ActionState<NavigationAction>, &mut ActionState<NavigationAction>,
&mut Path, &mut Path,
&mut Transform, &mut Transform,
&Collider, &Collider,
&Speed,
Option<&RotationSpeed>, Option<&RotationSpeed>,
)>, )>,
rapier_context: Res<RapierContext>, rapier_context: Res<RapierContext>,
obstructions: Query<&MapObstruction>,
) { ) {
for (entity, mut actions, mut path, mut transform, collider, rotation_speed) in &mut query { if !physics_config.physics_pipeline_active || !physics_config.query_pipeline_active {
return;
}
for (entity, mut actions, mut path, mut transform, collider, speed, rotation_speed) in
&mut query
{
let start_i32 = transform.translation.truncate().i32(); let start_i32 = transform.translation.truncate().i32();
trace!( trace!(
"{entity:?}: start pathfinding from {start_i32:?} to {:?}: {:?}", "{entity:?}: start pathfinding from {start_i32:?} to {:?}: at {:?}",
path.last(), path.last(),
transform.translation.truncate() transform.translation.truncate().i32()
); );
if path.len() > 0 { if path.len() > 0 && path[0] == start_i32 {
if path.len() > 1 && path[0] != start_i32 && path[1] != start_i32 { trace!("At start, removing");
trace!("Off path"); path.remove(0);
} else if path.len() > 1 && path[1] == start_i32 {
trace!("Next in path is start of path, shouldn't happen");
}
if path[0] == start_i32 {
trace!("At start, removing");
path.remove(0);
}
} }
if let Some(next) = path.first() { if let Some(next) = path.first() {
trace!("{entity:?}: path: moving from {start_i32:?} to {next:?}"); trace!("{entity:?}: path: moving from {start_i32:?} to {next:?}");
let start = transform.translation.truncate(); let start = transform.translation.truncate();
let mut next = Vec2::new(next.0 as f32, next.1 as f32); let mut next = Vec2::new(next.0 as f32, next.1 as f32);
if next.x > 0. { if next.x >= 0. {
next.x += 0.5; next.x += 0.5;
} else { } else {
next -= 0.5; next.x -= 0.5;
} }
if next.y > 0. { if next.y >= 0. {
next.y += 0.5; next.y += 0.5;
} else { } else {
next.y -= 0.5; next.y -= 0.5;
} }
let mut direction = next - start; let mut direction = next - start;
direction = direction.normalize(); direction = direction.normalize();
direction *= **speed;
direction *= time.delta_seconds();
trace!( trace!(
"{entity:?}: Direction: {direction:?}, Distance: {}", "{entity:?}: Direction: {direction:?}, Distance: {}",
(next - start).length() (next - start).length()
); );
if let Some((_hit, _toi)) = rapier_context.cast_shape( if rapier_context
start, .cast_shape(
transform.yaw().radians(), start,
direction, transform.yaw().radians(),
collider, direction,
rapier_context.integration_parameters.dt, collider,
true, 1.,
QueryFilter::new() true,
.predicate(&|entity| obstructions.get(entity).is_ok()) QueryFilter::new()
.exclude_sensors() .exclude_sensors()
.exclude_collider(entity), .exclude_collider(entity),
) { )
// println!("{entity:?} is stuck, hit: {hit:?}, TOI: {toi:?}"); .is_some()
{
// trace!("{entity:?} is stuck, hit: {hit:?}, TOI: {toi:?}");
// TODO: Remove when we have an actual character controller. // TODO: Remove when we have an actual character controller.
transform.translation = next.extend(0.); transform.translation = next.extend(0.);
continue; continue;
} }
trace!("{entity:?}: path: direction: {direction:?}"); trace!("{entity:?}: path: direction: {direction:?}");
actions.press(&NavigationAction::Move); actions.press(&NavigationAction::Translate);
actions actions
.action_data_mut_or_default(&NavigationAction::Move) .action_data_mut_or_default(&NavigationAction::Translate)
.axis_pair = Some(DualAxisData::from_xy(Vec2::new(-direction.y, direction.x))); .axis_pair = Some(DualAxisData::from_xy(direction));
if rotation_speed.is_some() { if rotation_speed.is_some() {
let angle = direction.y.atan2(direction.x); let angle = direction.y.atan2(direction.x);
transform.rotation = Quat::from_rotation_z(angle); transform.rotation = Quat::from_rotation_z(angle);
@ -336,7 +341,6 @@ fn negotiate_path(
.remove::<Path>() .remove::<Path>()
.remove::<NoPath>() .remove::<NoPath>()
.remove::<Destination>(); .remove::<Destination>();
actions.release(&NavigationAction::Move);
trace!("{entity:?}: pathfinding: cleaned up"); trace!("{entity:?}: pathfinding: cleaned up");
} }
} }
@ -354,11 +358,12 @@ fn actions(
for (entity, mut actions, mut navigation_action, destination) in &mut query { for (entity, mut actions, mut navigation_action, destination) in &mut query {
if actions.pressed(&NegotiatePathAction) { if actions.pressed(&NegotiatePathAction) {
if let Some(pair) = actions.axis_pair(&NegotiatePathAction) { if let Some(pair) = actions.axis_pair(&NegotiatePathAction) {
trace!("Negotiating path to {pair:?}"); trace!("{entity:?}: Negotiating path to {pair:?}");
let dest = Destination((pair.x() as i32, pair.y() as i32)); let dest = Destination(pair.xy().i32());
if let Some(mut current_dest) = destination { if let Some(mut current_dest) = destination {
trace!("Got a destination");
if *current_dest != dest { if *current_dest != dest {
trace!("{entity:?}: New destination, zeroing velocity"); trace!("{entity:?}: New destination {dest:?} differs from {current_dest:?}, zeroing velocity");
navigation_action.press(&NavigationAction::SetLinearVelocity); navigation_action.press(&NavigationAction::SetLinearVelocity);
navigation_action navigation_action
.action_data_mut_or_default(&NavigationAction::SetLinearVelocity) .action_data_mut_or_default(&NavigationAction::SetLinearVelocity)
@ -368,16 +373,17 @@ fn actions(
} else { } else {
trace!("{entity:?}: Adding destination, zeroing velocity"); trace!("{entity:?}: Adding destination, zeroing velocity");
navigation_action.press(&NavigationAction::SetLinearVelocity); navigation_action.press(&NavigationAction::SetLinearVelocity);
navigation_action navigation_action
.action_data_mut_or_default(&NavigationAction::SetLinearVelocity) .action_data_mut_or_default(&NavigationAction::SetLinearVelocity)
.axis_pair = Some(DualAxisData::from_xy(Vec2::ZERO)); .axis_pair = Some(DualAxisData::from_xy(Vec2::ZERO));
commands.entity(entity).insert(dest); commands.entity(entity).insert(dest);
} }
} else if destination.is_some() { } else if destination.is_some() {
trace!("No value, resetting");
commands commands
.entity(entity) .entity(entity)
.remove::<Destination>() .remove::<Destination>()
.remove::<Path>()
.remove::<NoPath>(); .remove::<NoPath>();
} }
actions.release(&NegotiatePathAction); actions.release(&NegotiatePathAction);
@ -397,10 +403,13 @@ impl Plugin for PathfindingPlugin {
.register_type::<NoPath>() .register_type::<NoPath>()
.register_type::<Path>() .register_type::<Path>()
.register_type::<CostMap>() .register_type::<CostMap>()
.add_systems(PreUpdate, (poll_tasks, negotiate_path).chain())
.add_systems( .add_systems(
PreUpdate, PreUpdate,
(actions, calculate_path) (poll_tasks, apply_deferred, negotiate_path).chain(),
)
.add_systems(
PreUpdate,
(actions, apply_deferred, calculate_path)
.chain() .chain()
.after(InputManagerSystem::Tick), .after(InputManagerSystem::Tick),
) )

View File

@ -17,7 +17,7 @@ use crate::{
bevy_rapier2d::prelude::*, bevy_rapier2d::prelude::*,
core::{GlobalTransformExt, Player, PointLike}, core::{GlobalTransformExt, Player, PointLike},
log::Log, log::Log,
map::{Map, MapObstruction, MapPlugin}, map::{Map, MapPlugin},
}; };
#[derive(Component, Clone, Copy, Debug, Default, Reflect)] #[derive(Component, Clone, Copy, Debug, Default, Reflect)]
@ -55,7 +55,6 @@ impl Viewshed {
start: &GlobalTransform, start: &GlobalTransform,
rapier_context: &RapierContext, rapier_context: &RapierContext,
visible_query: &Query<(&Visible, &Collider, &GlobalTransform)>, visible_query: &Query<(&Visible, &Collider, &GlobalTransform)>,
obstructions_query: &Query<&MapObstruction>,
events: &mut EventWriter<VisibilityChanged>, events: &mut EventWriter<VisibilityChanged>,
cache: &mut HashMap<(i32, i32), (u8, HashSet<Entity>)>, cache: &mut HashMap<(i32, i32), (u8, HashSet<Entity>)>,
) { ) {
@ -66,7 +65,9 @@ impl Viewshed {
start.translation().truncate(), start.translation().truncate(),
0., 0.,
&shape, &shape,
QueryFilter::new().predicate(&|e| visible_query.get(e).is_ok()), QueryFilter::new()
.exclude_collider(*viewer_entity)
.predicate(&|e| visible_query.contains(e)),
|entity| { |entity| {
if let Ok((_, collider, transform)) = visible_query.get(entity) { if let Ok((_, collider, transform)) = visible_query.get(entity) {
let position = Isometry2::new( let position = Isometry2::new(
@ -122,19 +123,16 @@ impl Viewshed {
shape_pos, shape_pos,
0., 0.,
&shape, &shape,
QueryFilter::new().predicate(&|v| visible_query.get(v).is_ok()), QueryFilter::new()
.exclude_collider(*viewer_entity)
.predicate(&|v| visible_query.contains(v)),
|entity| { |entity| {
// println!("{:?}", entity); // println!("{:?}", entity);
let obstruction = obstructions_query.get(entity).is_ok();
if obstruction {
// println!("Obstruction");
coord_entities.clear();
}
coord_entities.insert(entity); coord_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);
} }
!obstruction true
}, },
); );
cache.insert((coord.x, coord.y), (opacity, coord_entities.clone())); cache.insert((coord.x, coord.y), (opacity, coord_entities.clone()));
@ -150,13 +148,7 @@ impl Viewshed {
for e in &coord_entities { for e in &coord_entities {
new_visible_entities.insert(*e); new_visible_entities.insert(*e);
} }
if coord_entities.contains(viewer_entity) { opacity
// println!("Self hit, 0");
0
} else {
// println!("{}", opacity);
opacity
}
} else { } else {
0 0
} }
@ -301,7 +293,6 @@ where
fn update_viewshed( fn update_viewshed(
config: Res<RapierConfiguration>, config: Res<RapierConfiguration>,
visible: Query<(&Visible, &Collider, &GlobalTransform)>, visible: Query<(&Visible, &Collider, &GlobalTransform)>,
obstructions: Query<&MapObstruction>,
mut viewers: Query<( mut viewers: Query<(
Entity, Entity,
&mut Viewshed, &mut Viewshed,
@ -322,7 +313,6 @@ fn update_viewshed(
viewer_transform, viewer_transform,
&rapier_context, &rapier_context,
&visible, &visible,
&obstructions,
&mut changed, &mut changed,
&mut cache, &mut cache,
); );
@ -339,7 +329,6 @@ fn remove_visible(
)>, )>,
rapier_context: Res<RapierContext>, rapier_context: Res<RapierContext>,
visible: Query<(&Visible, &Collider, &GlobalTransform)>, visible: Query<(&Visible, &Collider, &GlobalTransform)>,
obstructions: Query<&MapObstruction>,
mut changed: EventWriter<VisibilityChanged>, mut changed: EventWriter<VisibilityChanged>,
) { ) {
if !removed.is_empty() { if !removed.is_empty() {
@ -356,7 +345,6 @@ fn remove_visible(
start, start,
&rapier_context, &rapier_context,
&visible, &visible,
&obstructions,
&mut changed, &mut changed,
&mut cache, &mut cache,
); );