From e850a13f71808a4aa14831750c3fd5c107ba7e6a Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 14 Jan 2024 11:11:29 -0800 Subject: [PATCH] Improved ship unlanding --- crates/galactica/src/main.rs | 6 +- crates/render/src/gpustate/phys.rs | 14 +-- crates/render/src/ui/radar.rs | 18 +-- crates/system/src/data/ship/ship.rs | 103 +++++++----------- .../src/phys/controller/autopilot/landing.rs | 11 +- crates/system/src/phys/objects/ship.rs | 4 +- crates/system/src/phys/systemsim/mod.rs | 2 +- crates/system/src/phys/systemsim/step.rs | 60 ++++++++-- crates/system/src/phys/systemsim/steputil.rs | 31 ++++-- 9 files changed, 129 insertions(+), 120 deletions(-) diff --git a/crates/galactica/src/main.rs b/crates/galactica/src/main.rs index 493c73c..b9fab3e 100644 --- a/crates/galactica/src/main.rs +++ b/crates/galactica/src/main.rs @@ -86,6 +86,7 @@ fn main() -> Result<()> { if let Some(o) = o { match o.data.get_state() { ShipState::Landing { .. } + | ShipState::UnLanding { .. } | ShipState::Collapsing { .. } | ShipState::Flying { .. } => { let r = @@ -96,11 +97,6 @@ fn main() -> Result<()> { None } } - ShipState::UnLanding { .. } => { - let pos = - o.data.get_state().unlanding_position(&content).unwrap(); - Some(Vector2::new(pos.x, pos.y)) - } ShipState::Landed { target } => { let b = content.get_system_object(*target); diff --git a/crates/render/src/gpustate/phys.rs b/crates/render/src/gpustate/phys.rs index b5be8d7..68a1697 100644 --- a/crates/render/src/gpustate/phys.rs +++ b/crates/render/src/gpustate/phys.rs @@ -36,7 +36,7 @@ impl GPUState { ship_cnt = state.ct.get_ship(ship.data.get_content()); } - ShipState::Landing { current_z, .. } => { + ShipState::UnLanding { current_z, .. } | ShipState::Landing { current_z, .. } => { let r = state.systemsim.get_rigid_body(ship.rigid_body).unwrap(); let pos = *r.translation(); ship_pos = Point3::new(pos.x, pos.y, *current_z); @@ -44,13 +44,6 @@ impl GPUState { ship_ang = ship_rot.angle(); ship_cnt = state.ct.get_ship(ship.data.get_content()); } - - ShipState::UnLanding { .. } => { - ship_pos = ship.data.get_state().unlanding_position(state.ct).unwrap(); - //ship_ang = 0.0 + ((to_angle - 0.0) * 1f32.min(elapsed / 1.0)); - ship_ang = 0.0; - ship_cnt = state.ct.get_ship(ship.data.get_content()); - } } // Position adjusted for parallax @@ -112,8 +105,9 @@ impl GPUState { let flare = ship.data.get_outfits().get_flare_sprite(state.ct); if { let is_flying = match ship.data.get_state() { - ShipState::Flying { .. } => true, - ShipState::Landing { .. } => true, + ShipState::Flying { .. } + | ShipState::UnLanding { .. } + | ShipState::Landing { .. } => true, _ => false, }; ship.get_controls().thrust && flare.is_some() && is_flying diff --git a/crates/render/src/ui/radar.rs b/crates/render/src/ui/radar.rs index 17c0ca4..42b34b2 100644 --- a/crates/render/src/ui/radar.rs +++ b/crates/render/src/ui/radar.rs @@ -1,6 +1,6 @@ use galactica_system::data::ShipState; -use galactica_util::{constants::UI_SPRITE_INSTANCE_LIMIT, to_radians}; -use nalgebra::{Point2, Vector2}; +use galactica_util::{clockwise_angle, constants::UI_SPRITE_INSTANCE_LIMIT, to_radians}; +use nalgebra::{Point2, Rotation2, Vector2}; use crate::{ datastructs::RenderState, @@ -41,21 +41,15 @@ impl Radar { match player_ship.data.get_state() { ShipState::Dead => {} - ShipState::UnLanding { .. } => { - let pos = player_ship - .data - .get_state() - .unlanding_position(&input.ct) - .unwrap(); - self.last_player_position = Point2::new(pos.x, pos.y) - } - ShipState::Landed { target } => { let landed_body = input.ct.get_system_object(*target); self.last_player_position = Point2::new(landed_body.pos.x, landed_body.pos.y); } - ShipState::Landing { .. } | ShipState::Flying { .. } | ShipState::Collapsing { .. } => { + ShipState::UnLanding { .. } + | ShipState::Landing { .. } + | ShipState::Flying { .. } + | ShipState::Collapsing { .. } => { let player_body = input .systemsim .get_rigid_body(player_ship.rigid_body) diff --git a/crates/system/src/data/ship/ship.rs b/crates/system/src/data/ship/ship.rs index 006554d..7ba23bf 100644 --- a/crates/system/src/data/ship/ship.rs +++ b/crates/system/src/data/ship/ship.rs @@ -1,5 +1,5 @@ use galactica_content::{Content, FactionHandle, GunPoint, Outfit, ShipHandle, SystemObjectHandle}; -use nalgebra::{Point2, Point3}; +use nalgebra::Isometry2; use rand::{rngs::ThreadRng, Rng}; use rapier2d::math::Isometry; use std::{collections::HashMap, time::Instant}; @@ -65,17 +65,14 @@ pub enum ShipState { /// This ship is taking off from a planet /// (playing the animation) UnLanding { - /// The point, in world coordinates, to which we're going + /// The point to which we're going, in world coordinates to_position: Isometry, /// The planet we're taking off from from: SystemObjectHandle, - /// The total amount of time, in seconds, we will spend taking off - total: f32, - - /// The amount of time we've already spent playing this unlanding sequence - elapsed: f32, + /// Our current z-coordinate + current_z: f32, }, } @@ -99,42 +96,6 @@ impl ShipState { _ => None, } } - - /// Compute position of this ship's sprite during its unlanding sequence - pub fn unlanding_position(&self, ct: &Content) -> Option> { - match self { - Self::UnLanding { - to_position, - from, - total, - elapsed, - .. - } => Some({ - let from = ct.get_system_object(*from); - - let t = Point2::new(to_position.translation.x, to_position.translation.y); - let diff = t - Point2::new(from.pos.x, from.pos.y); - //let diff = diff - diff.normalize() * (target.size / 2.0) * 0.8; - - // TODO: improve animation - // TODO: fade - // TODO: atmosphere burn - // TODO: land at random point - // TODO: don't jump camera - // TODO: time by distance - // TODO: keep momentum - - let pos = Point2::new(from.pos.x, from.pos.y) + (diff * (elapsed / total)); - - Point3::new( - pos.x, - pos.y, - from.pos.z + ((1.0 - from.pos.z) * (elapsed / total)), - ) - }), - _ => None, - } - } } /// Represents all attributes of a single ship @@ -245,28 +206,57 @@ impl ShipData { self.state = ShipState::Landed { target }; } _ => { - unreachable!("Called `finish_land_on` on a ship that isn't flying!") + unreachable!("Called `finish_land_on` on a ship that isn't landing!") } }; } - /// Take off from `target` - pub fn unland(&mut self, to_position: Isometry) { + /// Land this ship on `target` + /// This does NO checks (speed, range, etc). + /// That is the simulation's responsiblity. + /// + /// Will panic if we're not flying. + pub fn start_unland_to(&mut self, ct: &Content, to_position: Isometry2) { match self.state { ShipState::Landed { target } => { + let obj = ct.get_system_object(target); self.state = ShipState::UnLanding { to_position, from: target, - total: 2.0, - elapsed: 0.0, + current_z: obj.pos.z, }; } _ => { - unreachable!("Called `unland` on a ship that isn't landed!") + unreachable!("Called `start_unland_to` on a ship that isn't landed!") } }; } + /// When unlanding, update z position. + /// Will panic if we're not unlanding + pub fn set_unlanding_z(&mut self, z: f32) { + match &mut self.state { + ShipState::UnLanding { + ref mut current_z, .. + } => *current_z = z, + _ => unreachable!("Called `set_unlanding_z` on a ship that isn't unlanding!"), + } + } + + /// Finish unlanding sequence + /// Will panic if we're not unlanding + pub fn finish_unland_to(&mut self) { + match self.state { + ShipState::UnLanding { .. } => { + self.state = ShipState::Flying { + autopilot: ShipAutoPilot::None, + } + } + _ => { + unreachable!("Called `finish_unland_to` on a ship that isn't unlanding!") + } + }; + } /// Add an outfit to this ship pub fn add_outfit(&mut self, o: &Outfit) -> super::OutfitAddResult { let r = self.outfits.add(o); @@ -331,20 +321,7 @@ impl ShipData { /// Update this ship's state by `t` seconds pub(crate) fn step(&mut self, t: f32) { match self.state { - ShipState::Landing { .. } => {} - - ShipState::UnLanding { - ref mut elapsed, - total, - .. - } => { - *elapsed += t; - if *elapsed >= total { - self.state = ShipState::Flying { - autopilot: ShipAutoPilot::None, - }; - } - } + ShipState::UnLanding { .. } | ShipState::Landing { .. } => {} ShipState::Landed { .. } => { // Cooldown guns diff --git a/crates/system/src/phys/controller/autopilot/landing.rs b/crates/system/src/phys/controller/autopilot/landing.rs index cefb7d7..17a062f 100644 --- a/crates/system/src/phys/controller/autopilot/landing.rs +++ b/crates/system/src/phys/controller/autopilot/landing.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; -use galactica_content::SystemObjectHandle; -use galactica_util::to_radians; +use galactica_util::{clockwise_angle, to_radians}; use nalgebra::Vector2; use rapier2d::{dynamics::RigidBodySet, geometry::ColliderHandle}; @@ -16,23 +15,21 @@ use crate::phys::{ /// Land this ship on the given object pub fn auto_landing( - res: &PhysStepResources, + _res: &PhysStepResources, rigid_bodies: &RigidBodySet, ships: &HashMap, this_ship: ColliderHandle, - target_handle: SystemObjectHandle, + target_pos: Vector2, ) -> Option { let rigid_body_handle = ships.get(&this_ship).unwrap().rigid_body; let rigid_body = rigid_bodies.get(rigid_body_handle).unwrap(); - let target_obj = res.ct.get_system_object(target_handle); - let target_pos = Vector2::new(target_obj.pos.x, target_obj.pos.y); let my_pos = *rigid_body.translation(); let my_rot = rigid_body.rotation() * Vector2::new(1.0, 0.0); let my_vel = rigid_body.linvel(); let my_angvel = rigid_body.angvel(); let v_t = target_pos - my_pos; // Vector to target let v_d = v_t - my_vel; // Desired thrust vector - let angle_delta = (my_rot.x * v_d.y - v_d.x * my_rot.y).atan2(my_rot.dot(&v_d)); + let angle_delta = clockwise_angle(&my_rot, &v_d); let mut controls = ShipControls::new(); if angle_delta < 0.0 && my_angvel > -0.3 { diff --git a/crates/system/src/phys/objects/ship.rs b/crates/system/src/phys/objects/ship.rs index e54907f..67099a2 100644 --- a/crates/system/src/phys/objects/ship.rs +++ b/crates/system/src/phys/objects/ship.rs @@ -100,11 +100,11 @@ impl PhysSimShip { self.step_effects(res, rigid_body, collider); } - ShipState::Landing { .. } => { + ShipState::UnLanding { .. } | ShipState::Landing { .. } => { self.step_physics(res, rigid_body, collider); } - ShipState::UnLanding { .. } | ShipState::Dead | ShipState::Landed { .. } => {} + ShipState::Dead | ShipState::Landed { .. } => {} } } diff --git a/crates/system/src/phys/systemsim/mod.rs b/crates/system/src/phys/systemsim/mod.rs index 136fbf2..0f22382 100644 --- a/crates/system/src/phys/systemsim/mod.rs +++ b/crates/system/src/phys/systemsim/mod.rs @@ -138,7 +138,7 @@ impl PhysSim { ShipState::Landed { .. } => { if player.input.pressed_land() { - self.unland_ship(ct, player.ship.unwrap()); + self.start_unland_ship(ct, player.ship.unwrap()); } } }; diff --git a/crates/system/src/phys/systemsim/step.rs b/crates/system/src/phys/systemsim/step.rs index e6ab620..88676eb 100644 --- a/crates/system/src/phys/systemsim/step.rs +++ b/crates/system/src/phys/systemsim/step.rs @@ -9,7 +9,11 @@ use rapier2d::{ use crate::{ data::{ShipAutoPilot, ShipState}, - phys::{controller::autopilot, objects::PhysProjectile, ParticleBuilder, PhysStepResources}, + phys::{ + controller::autopilot, + objects::{PhysProjectile, ShipControls}, + ParticleBuilder, PhysStepResources, + }, }; use super::PhysSim; @@ -36,9 +40,7 @@ impl PhysSim { to_remove.push(collider); } - ShipState::UnLanding { .. } - | ShipState::Landed { .. } - | ShipState::Collapsing { .. } => { + ShipState::Landed { .. } | ShipState::Collapsing { .. } => { let ship = self.ships.get_mut(&collider).unwrap(); ship.step( res, @@ -47,6 +49,48 @@ impl PhysSim { ); } + ShipState::UnLanding { + to_position, + current_z, + from, + } => { + let from_obj = res.ct.get_system_object(*from); + let controls = autopilot::auto_landing( + &res, + &self.rigid_body_set, + &self.ships, + ship.collider, + Vector2::new(to_position.translation.x, to_position.translation.y), + ); + let r = &mut self.rigid_body_set[ship.rigid_body]; + let max_d = (Vector2::new(from_obj.pos.x, from_obj.pos.y) + - Vector2::new(to_position.translation.x, to_position.translation.y)) + .magnitude(); + let now_d = (r.translation() + - Vector2::new(to_position.translation.x, to_position.translation.y)) + .magnitude(); + let f = now_d / max_d; + + let current_z = *current_z; + let ship = self.ships.get_mut(&collider).unwrap(); + let zdist = 1.0 - from_obj.pos.z; + + if current_z <= 1.0 { + self.finish_unland_ship(collider); + } else if current_z <= 1.5 { + ship.data + .set_unlanding_z(1f32.max(current_z - (0.5 * res.t) / 0.5)); + ship.controls = ShipControls::new(); + } else { + ship.data.set_unlanding_z(1.0 - zdist * f); + + if let Some(controls) = controls { + ship.controls = controls; + } + ship.step(res, r, &mut self.collider_set[ship.collider]) + }; + } + ShipState::Landing { target, current_z } => { let target_obj = res.ct.get_system_object(*target); let controls = autopilot::auto_landing( @@ -54,17 +98,16 @@ impl PhysSim { &self.rigid_body_set, &self.ships, ship.collider, - *target, + Vector2::new(target_obj.pos.x, target_obj.pos.y), ); let current_z = *current_z; - let target = *target; let ship = self.ships.get_mut(&collider).unwrap(); let r = &mut self.rigid_body_set[ship.rigid_body]; let zdist = target_obj.pos.z - 1.0; if current_z >= target_obj.pos.z { - self.finish_land_ship(res.ct, collider, target); + self.finish_land_ship(collider); } else { ship.data.set_landing_z(current_z + zdist * res.t / 2.0); @@ -82,12 +125,13 @@ impl PhysSim { ShipAutoPilot::Landing { target: target_handle, } => { + let target_obj = res.ct.get_system_object(*target_handle); let controls = autopilot::auto_landing( &res, &self.rigid_body_set, &self.ships, ship.collider, - *target_handle, + Vector2::new(target_obj.pos.x, target_obj.pos.y), ); let landed = self.try_land_ship(res.ct, collider, *target_handle); diff --git a/crates/system/src/phys/systemsim/steputil.rs b/crates/system/src/phys/systemsim/steputil.rs index 193012e..ec6cb6d 100644 --- a/crates/system/src/phys/systemsim/steputil.rs +++ b/crates/system/src/phys/systemsim/steputil.rs @@ -1,5 +1,6 @@ use galactica_content::{Content, Relationship, SystemObjectHandle}; -use nalgebra::{Isometry2, Point2, Vector2}; +use nalgebra::{Isometry2, Point2, Rotation2, Vector2}; +use rand::Rng; use rapier2d::{ dynamics::RigidBody, geometry::{ColliderHandle, Group, InteractionGroups}, @@ -70,7 +71,6 @@ impl PhysSim { } let collider = self.collider_set.get_mut(collider).unwrap(); - println!("{:?}", collider.collision_groups()); collider.set_collision_groups(InteractionGroups::new(Group::GROUP_1, Group::empty())); ship.data.start_land_on(target_handle); return true; @@ -78,12 +78,7 @@ impl PhysSim { /// Finish landing this ship on a planet. /// Called after the landing animation finishes. - pub(super) fn finish_land_ship( - &mut self, - _ct: &Content, - collider: ColliderHandle, - _target: SystemObjectHandle, - ) { + pub(super) fn finish_land_ship(&mut self, collider: ColliderHandle) { let ship = self.ships.get_mut(&collider).unwrap(); ship.data.finish_land_on(); let r = self.rigid_body_set.get_mut(ship.rigid_body).unwrap(); @@ -92,19 +87,31 @@ impl PhysSim { r.set_linvel(nalgebra::Vector2::new(0.0, 0.0), false); } - pub(super) fn unland_ship(&mut self, ct: &Content, collider: ColliderHandle) { + pub(super) fn start_unland_ship(&mut self, ct: &Content, collider: ColliderHandle) { let ship = self.ships.get_mut(&collider).unwrap(); let obj = ship.data.get_state().landed_on().unwrap(); let obj = ct.get_system_object(obj); - let target_pos = Isometry2::new(Vector2::new(obj.pos.x + 100.0, obj.pos.y + 100.0), 1.0); + let mut rng = rand::thread_rng(); + let radius = rng.gen_range(500.0..=1500.0); + let angle = rng.gen_range(0.0..std::f32::consts::TAU); + let target_offset = Rotation2::new(angle) * Vector2::new(radius, 0.0); + let target_trans = Vector2::new(obj.pos.x, obj.pos.y) + target_offset; + let target_pos = Isometry2::new(target_trans, angle); - ship.data.unland(target_pos); + ship.data.start_unland_to(ct, target_pos); let r = self.rigid_body_set.get_mut(ship.rigid_body).unwrap(); r.set_enabled(true); - r.set_position(target_pos, true); + r.set_position( + Isometry2::new(Vector2::new(obj.pos.x, obj.pos.y), angle), + true, + ); + } + pub(super) fn finish_unland_ship(&mut self, collider: ColliderHandle) { + let ship = self.ships.get_mut(&collider).unwrap(); + ship.data.finish_unland_to(); self.collider_set .get_mut(ship.collider) .unwrap()