From 1721dc98f85050ffbf5999c36f7d071c3a0751cc Mon Sep 17 00:00:00 2001 From: Nolan Darilek Date: Tue, 19 Jul 2022 11:24:45 -0500 Subject: [PATCH] Refactor helpers for direction and distance, adding some that take colliders into account. --- src/core.rs | 204 +++++++++++++++++++++++++++++++--------------- src/navigation.rs | 7 +- src/visibility.rs | 31 +++---- 3 files changed, 157 insertions(+), 85 deletions(-) diff --git a/src/core.rs b/src/core.rs index 33266b2..9a89119 100644 --- a/src/core.rs +++ b/src/core.rs @@ -8,7 +8,7 @@ use std::{ }; use bevy::{core::FloatOrd, ecs::query::WorldQuery, prelude::*}; -use bevy_rapier2d::prelude::*; +use bevy_rapier2d::{na, parry::query::ClosestPoints, prelude::*, rapier::math::Isometry}; use once_cell::sync::Lazy; use rand::prelude::*; use serde::{Deserialize, Serialize}; @@ -52,6 +52,74 @@ impl Angle { Radians(v) => *v, } } + + fn relative_desc(&self) -> String { + let mode = RELATIVE_DIRECTION_MODE.read().unwrap(); + match self.degrees() { + v if v <= 15. => "ahead", + v if v <= 45. => { + if *mode == RelativeDirectionMode::ClockFacing { + "11:00" + } else { + "ahead and left" + } + } + v if v <= 75. => { + if *mode == RelativeDirectionMode::ClockFacing { + "10:00" + } else { + "left and ahead" + } + } + v if v <= 105. => "left", + v if v <= 135. => { + if *mode == RelativeDirectionMode::ClockFacing { + "8:00" + } else { + "left and behind" + } + } + v if v <= 165. => { + if *mode == RelativeDirectionMode::ClockFacing { + "7:00" + } else { + "behind and left" + } + } + v if v <= 195. => "behind", + v if v <= 225. => { + if *mode == RelativeDirectionMode::ClockFacing { + "5:00" + } else { + "behind and right" + } + } + v if v <= 255. => { + if *mode == RelativeDirectionMode::ClockFacing { + "4:00" + } else { + "right and behind" + } + } + v if v <= 285. => "right", + v if v <= 315. => { + if *mode == RelativeDirectionMode::ClockFacing { + "2:00" + } else { + "right and ahead" + } + } + v if v <= 345. => { + if *mode == RelativeDirectionMode::ClockFacing { + "1:00" + } else { + "ahead and right" + } + } + _ => "ahead", + } + .into() + } } impl Sub for Angle { @@ -216,6 +284,8 @@ static RELATIVE_DIRECTION_MODE: Lazy> = Lazy::new( RwLock::new(v) }); +static PHYSICS_SCALE: Lazy> = Lazy::new(|| RwLock::new(1.)); + pub trait PointLike { fn x(&self) -> f32; @@ -283,73 +353,8 @@ pub trait PointLike { if distance > 0 { let tile_or_tiles = if distance == 1 { "tile" } else { "tiles" }; let direction: String = if let Some(yaw) = yaw { - let mode = RELATIVE_DIRECTION_MODE.read().unwrap(); let bearing = self.bearing(other); - let relative = (bearing - yaw).degrees(); - match relative { - v if v <= 15. => "ahead", - v if v <= 45. => { - if *mode == RelativeDirectionMode::ClockFacing { - "11:00" - } else { - "ahead and left" - } - } - v if v <= 75. => { - if *mode == RelativeDirectionMode::ClockFacing { - "10:00" - } else { - "left and ahead" - } - } - v if v <= 105. => "left", - v if v <= 135. => { - if *mode == RelativeDirectionMode::ClockFacing { - "8:00" - } else { - "left and behind" - } - } - v if v <= 165. => { - if *mode == RelativeDirectionMode::ClockFacing { - "7:00" - } else { - "behind and left" - } - } - v if v <= 195. => "behind", - v if v <= 225. => { - if *mode == RelativeDirectionMode::ClockFacing { - "5:00" - } else { - "behind and right" - } - } - v if v <= 255. => { - if *mode == RelativeDirectionMode::ClockFacing { - "4:00" - } else { - "right and behind" - } - } - v if v <= 285. => "right", - v if v <= 315. => { - if *mode == RelativeDirectionMode::ClockFacing { - "2:00" - } else { - "right and ahead" - } - } - v if v <= 345. => { - if *mode == RelativeDirectionMode::ClockFacing { - "1:00" - } else { - "ahead and right" - } - } - _ => "ahead", - } - .into() + (bearing - yaw).relative_desc() } else { self.direction(other).into() }; @@ -460,6 +465,69 @@ impl From<&dyn PointLike> for (i32, i32) { } } +pub trait GlobalTransformExt { + fn yaw(&self) -> Angle; + fn collider_direction_and_distance( + &self, + collider: &Collider, + other: &GlobalTransform, + other_collider: &Collider, + ) -> String; +} + +impl GlobalTransformExt for GlobalTransform { + fn yaw(&self) -> Angle { + let forward = self.local_x(); + Angle::Radians(forward.y.atan2(forward.x)) + } + + fn collider_direction_and_distance( + &self, + collider: &Collider, + other: &GlobalTransform, + other_collider: &Collider, + ) -> String { + use bevy::math::Vec3Swizzles; + let scale = PHYSICS_SCALE.read().unwrap(); + let pos1 = Isometry::new( + (self.translation / *scale).xy().into(), + self.rotation.to_scaled_axis().z, + ); + let pos2 = Isometry::new( + (other.translation / *scale).xy().into(), + other.rotation.to_scaled_axis().z, + ); + let closest = bevy_rapier2d::parry::query::closest_points( + &pos1, + &*collider.raw, + &pos2, + &*other_collider.raw, + f32::MAX, + ) + .unwrap(); + let distance: u32 = match closest { + ClosestPoints::Intersecting => 0, + ClosestPoints::WithinMargin(p1, p2) => na::distance(&p1, &p2) as u32, + ClosestPoints::Disjoint => 0, + }; + let tile_or_tiles = if distance == 1 { "tile" } else { "tiles" }; + if distance > 0 { + if let ClosestPoints::WithinMargin(p1, p2) = closest { + let p1 = (p1.x, p1.y); + let p2 = (p2.x, p2.y); + let bearing = p1.bearing(&p2); + let yaw = self.yaw(); + let direction = (bearing - yaw).relative_desc(); + format!("{direction} {distance} {tile_or_tiles}") + } else { + format!("{} {}", distance, tile_or_tiles) + } + } else { + format!("{} {}", distance, tile_or_tiles) + } + } +} + #[derive(Component, Clone, Copy, Debug, Default, Reflect)] #[reflect(Component)] pub struct Player; @@ -524,6 +592,8 @@ where } fn setup(core_config: Res) { + let mut scale = PHYSICS_SCALE.write().unwrap(); + *scale = core_config.pixels_per_unit as f32; let mut mode = RELATIVE_DIRECTION_MODE.write().unwrap(); *mode = core_config.relative_direction_mode; } diff --git a/src/navigation.rs b/src/navigation.rs index 2694757..af124d0 100644 --- a/src/navigation.rs +++ b/src/navigation.rs @@ -7,7 +7,7 @@ use bevy_tts::Tts; use crate::{ commands::RunIfExistsExt, - core::{Angle, CardinalDirection, Player}, + core::{Angle, CardinalDirection, Player, GlobalTransformExt}, error::error_handler, exploration::{ExplorationFocused, Exploring}, pathfinding::Destination, @@ -264,13 +264,12 @@ where fn update_direction( mut commands: Commands, mut query: Query< - (Entity, &Transform, Option<&mut CardinalDirection>), + (Entity, &GlobalTransform, Option<&mut CardinalDirection>), (With, Changed), >, ) { for (entity, transform, direction) in query.iter_mut() { - let forward = transform.local_x(); - let yaw = Angle::Radians(forward.y.atan2(forward.x)); + let yaw = transform.yaw(); let new_direction: CardinalDirection = yaw.into(); if let Some(mut direction) = direction { if *direction != new_direction { diff --git a/src/visibility.rs b/src/visibility.rs index 747afe3..3e2865a 100644 --- a/src/visibility.rs +++ b/src/visibility.rs @@ -12,7 +12,7 @@ use shadowcast::{vision_distance, Context, InputGrid}; use crate::{ bevy_rapier2d::prelude::*, commands::RunIfExistsExt, - core::{Angle, Player, PointLike}, + core::{GlobalTransformExt, Player, PointLike}, log::Log, map::{Map, MapConfig, MapObstruction}, }; @@ -380,8 +380,8 @@ fn log_visible( mut recently_lost: Local>, mut events: EventReader, mut log: Query<&mut Log>, - viewers: Query<(Entity, &Transform), (With, With)>, - visible: Query<(&Name, &Transform), Without>, + viewers: Query<(Entity, &GlobalTransform, Option<&Collider>), (With, With)>, + visible: Query<(&Name, &GlobalTransform, Option<&Collider>), Without>, ) { for timer in recently_lost.values_mut() { timer.tick(time.delta()); @@ -392,21 +392,24 @@ fn log_visible( VisibilityChanged::Gained { viewer, .. } => viewer, VisibilityChanged::Lost { viewer, .. } => viewer, }; - if let Ok((viewer_entity, viewer_transform)) = viewers.get(*viewer) { + if let Ok((viewer_entity, viewer_transform, viewer_collider)) = viewers.get(*viewer) { if let VisibilityChanged::Gained { viewed, .. } = event { if *viewed == viewer_entity || recently_lost.contains_key(viewed) { continue; } - if let Ok((name, transform)) = visible.get(*viewed) { - let viewed_coordinates = (transform.translation.x, transform.translation.y); - let forward = viewer_transform.local_x(); - let yaw = Angle::Radians(forward.y.atan2(forward.x)); - let viewer_coordinates = ( - viewer_transform.translation.x, - viewer_transform.translation.y, - ); - let location = - viewer_coordinates.direction_and_distance(&viewed_coordinates, Some(yaw)); + if let Ok((name, viewed_transform, viewed_collider)) = visible.get(*viewed) { + let location = if let (Some(viewer_collider), Some(viewed_collider)) = + (viewer_collider, viewed_collider) + { + viewer_transform.collider_direction_and_distance( + viewer_collider, + &viewed_transform, + viewed_collider, + ) + } else { + let yaw = viewer_transform.yaw(); + viewer_transform.direction_and_distance(viewed_transform, Some(yaw)) + }; if let Ok(mut log) = log.get_single_mut() { log.push(format!("{}: {location}", name)); }