From ebf489984c65e5267517b6527f7440b3aea907c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20B=C3=B6klin?= Date: Thu, 5 Aug 2021 09:18:49 +0200 Subject: [PATCH] Improve gliding - make glider dimensions a factor of body height - increase glider dimensions across the board - remove delay from transition into glide - enable glider control while wielding glider - improve glide wield animation --- CHANGELOG.md | 3 + common/src/comp/ori.rs | 24 ++++++++ common/src/states/glide.rs | 2 +- common/src/states/glide_wield.rs | 72 +++++++++++++++--------- common/src/states/utils.rs | 2 +- voxygen/anim/src/character/glidewield.rs | 15 +++-- voxygen/src/scene/figure/mod.rs | 4 +- 7 files changed, 86 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 930e68ed01..29efb0ee00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - NPCs can now warn players before engaging in combat - Custom error message when a supported graphics backend can not be found - Add server setting with PvE/PvP switch +- Can now tilt glider while only wielding it ### Changed @@ -47,6 +48,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The types of animals that can be tamed as pets are now limited to certain species, pending further balancing of pets - Made server-cli admin add/remove command use positional arguments again - Usage of "stamina" replaced with "energy" +- Glider dimensions now depend on character height +- Glider dimensions somewhat increased overall ### Removed diff --git a/common/src/comp/ori.rs b/common/src/comp/ori.rs index f6cc9b75c6..5531c94829 100644 --- a/common/src/comp/ori.rs +++ b/common/src/comp/ori.rs @@ -194,6 +194,30 @@ impl Ori { self.rotated(Quaternion::rotation_y(angle_radians)) } + /// Returns a version which is rolled such that its up points towards `dir` + /// as much as possible without pitching or yawing + pub fn rolled_towards(self, dir: Dir) -> Self { + dir.projected(&Plane::from(self.look_dir())) + .map_or(self, |dir| self.prerotated(self.up().rotation_between(dir))) + } + + /// Returns a version which has been pitched towards `dir` as much as + /// possible without yawing or rolling + pub fn pitched_towards(self, dir: Dir) -> Self { + dir.projected(&Plane::from(self.right())) + .map_or(self, |dir_| { + self.prerotated(self.look_dir().rotation_between(dir_)) + }) + } + + /// Returns a version which has been yawed towards `dir` as much as possible + /// without pitching or rolling + pub fn yawed_towards(self, dir: Dir) -> Self { + dir.projected(&Plane::from(self.up())).map_or(self, |dir_| { + self.prerotated(self.look_dir().rotation_between(dir_)) + }) + } + /// Returns a version without sideways tilt (roll) /// /// ``` diff --git a/common/src/states/glide.rs b/common/src/states/glide.rs index 2542cfa211..399110dc3c 100644 --- a/common/src/states/glide.rs +++ b/common/src/states/glide.rs @@ -80,7 +80,7 @@ impl CharacterBehavior for Data { if data.physics.on_ground.is_some() && (data.vel.0 - data.physics.ground_vel).magnitude_squared() < 2_f32.powi(2) { - update.character = CharacterState::GlideWield(glide_wield::Data::default()); + update.character = CharacterState::GlideWield(glide_wield::Data::from(data)); } else if data.physics.in_liquid().is_some() || data .inventory diff --git a/common/src/states/glide_wield.rs b/common/src/states/glide_wield.rs index de8af4ea12..60e832fbbc 100644 --- a/common/src/states/glide_wield.rs +++ b/common/src/states/glide_wield.rs @@ -1,6 +1,6 @@ use super::utils::*; use crate::{ - comp::{slot::EquipSlot, CharacterState, InventoryAction, StateUpdate}, + comp::{slot::EquipSlot, CharacterState, InventoryAction, Ori, StateUpdate}, states::{ behavior::{CharacterBehavior, JoinData}, glide, @@ -10,13 +10,25 @@ use serde::{Deserialize, Serialize}; #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Data { - // time since left ground, for preventing state transition - // before block snapping kicks in - timer: f32, + pub ori: Ori, + span_length: f32, + chord_length: f32, } -impl Default for Data { - fn default() -> Self { Self { timer: 0.0 } } +impl From<&JoinData<'_>> for Data { + fn from(data: &JoinData) -> Self { + let scale = data.body.dimensions().z; + Self { + // Aspect ratio is what really matters for lift/drag ratio + // and the aerodynamics model works for ARs up to 25. + // The inflated dimensions are hopefully only a temporary + // bandaid for the poor glide ratio experienced under 2.5G. + // A span/chord ratio of 4.5 gives an AR of ~5.73. + span_length: scale * 3.0, + chord_length: scale / 1.5, + ori: *data.ori, + } + } } impl CharacterBehavior for Data { @@ -29,31 +41,37 @@ impl CharacterBehavior for Data { handle_dodge_input(data, &mut update); handle_wield(data, &mut update); - // If not on the ground while wielding glider enter gliding state - if data.physics.on_ground.is_none() { - update.character = if self.timer > 0.3 { - CharacterState::Glide(glide::Data::new(10.0, 0.6, *data.ori)) - } else { + // If still in this state, do the things + if matches!(update.character, CharacterState::GlideWield(_)) { + // If not on the ground while wielding glider enter gliding state + update.character = if data.physics.on_ground.is_none() { + CharacterState::Glide(glide::Data::new( + self.span_length, + self.chord_length, + self.ori, + )) + // make sure we have a glider and we're not (too deep) in water + } else if data + .inventory + .and_then(|inv| inv.equipped(EquipSlot::Glider)) + .is_some() + && data.physics.in_liquid().map_or(true, |depth| depth < 0.5) + { CharacterState::GlideWield(Self { - timer: self.timer + data.dt.0, + // Glider tilt follows look dir + ori: self.ori.slerped_towards( + data.ori.slerped_towards( + Ori::from(data.inputs.look_dir).pitched_up(0.6), + (1.0 + data.inputs.look_dir.dot(*data.ori.look_dir()).max(0.0)) / 3.0, + ), + 5.0 * data.dt.0, + ), + ..*self }) + } else { + CharacterState::Idle }; } - if data - .physics - .in_liquid() - .map(|depth| depth > 0.5) - .unwrap_or(false) - { - update.character = CharacterState::Idle; - } - if data - .inventory - .and_then(|inv| inv.equipped(EquipSlot::Glider)) - .is_none() - { - update.character = CharacterState::Idle - }; update } diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index e3d8c2ca2d..8611f5cfb0 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -638,7 +638,7 @@ pub fn attempt_glide_wield(data: &JoinData<'_>, update: &mut StateUpdate) { .unwrap_or(false) && data.body.is_humanoid() { - update.character = CharacterState::GlideWield(glide_wield::Data::default()); + update.character = CharacterState::GlideWield(glide_wield::Data::from(data)); } } diff --git a/voxygen/anim/src/character/glidewield.rs b/voxygen/anim/src/character/glidewield.rs index 8118fb117b..e142946b12 100644 --- a/voxygen/anim/src/character/glidewield.rs +++ b/voxygen/anim/src/character/glidewield.rs @@ -5,8 +5,9 @@ use super::{ pub struct GlideWieldAnimation; +type GlideWieldAnimationDependency = (Quaternion, Quaternion); impl Animation for GlideWieldAnimation { - type Dependency<'a> = (); + type Dependency<'a> = GlideWieldAnimationDependency; type Skeleton = CharacterSkeleton; #[cfg(feature = "use-dyn-lib")] @@ -16,21 +17,25 @@ impl Animation for GlideWieldAnimation { fn update_skeleton_inner<'a>( skeleton: &Self::Skeleton, - _: Self::Dependency<'a>, + (orientation, glider_orientation): Self::Dependency<'a>, _anim_time: f32, rate: &mut f32, s_a: &SkeletonAttr, ) -> Self::Skeleton { let mut next = (*skeleton).clone(); + let glider_ori = orientation.inverse() * glider_orientation; + let glider_pos = Vec3::new(0.0, -5.0, 13.0); *rate = 1.0; - next.hand_l.position = Vec3::new(-2.0 - s_a.hand.0, s_a.hand.1, s_a.hand.2 + 15.0); + next.hand_l.position = + glider_pos + glider_ori * Vec3::new(-s_a.hand.0 + -2.0, s_a.hand.1 + 8.0, s_a.hand.2); next.hand_l.orientation = Quaternion::rotation_x(3.35) * Quaternion::rotation_y(0.2); - next.hand_r.position = Vec3::new(2.0 + s_a.hand.0, s_a.hand.1, s_a.hand.2 + 15.0); + next.hand_r.position = + glider_pos + glider_ori * Vec3::new(s_a.hand.0 + 2.0, s_a.hand.1 + 8.0, s_a.hand.2); next.hand_r.orientation = Quaternion::rotation_x(3.35) * Quaternion::rotation_y(-0.2); next.glider.scale = Vec3::one() * 1.0; - next.glider.orientation = Quaternion::rotation_x(0.35); + next.glider.orientation = glider_ori; next.glider.position = Vec3::new(0.0, -5.0, 13.0); diff --git a/voxygen/src/scene/figure/mod.rs b/voxygen/src/scene/figure/mod.rs index 6ec10087d4..a27b677d6a 100644 --- a/voxygen/src/scene/figure/mod.rs +++ b/voxygen/src/scene/figure/mod.rs @@ -1572,10 +1572,10 @@ impl FigureMgr { skeleton_attr, ) }, - CharacterState::GlideWield { .. } => { + CharacterState::GlideWield(data) => { anim::character::GlideWieldAnimation::update_skeleton( &target_base, - (), + (ori, data.ori.into()), state.state_time, &mut state_animation_rate, skeleton_attr,