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
This commit is contained in:
Ludvig Böklin 2021-08-05 09:18:49 +02:00 committed by Treeco
parent b667ba86f9
commit ebf489984c
7 changed files with 86 additions and 36 deletions

View File

@ -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 - NPCs can now warn players before engaging in combat
- Custom error message when a supported graphics backend can not be found - Custom error message when a supported graphics backend can not be found
- Add server setting with PvE/PvP switch - Add server setting with PvE/PvP switch
- Can now tilt glider while only wielding it
### Changed ### 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 - 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 - Made server-cli admin add/remove command use positional arguments again
- Usage of "stamina" replaced with "energy" - Usage of "stamina" replaced with "energy"
- Glider dimensions now depend on character height
- Glider dimensions somewhat increased overall
### Removed ### Removed

View File

@ -194,6 +194,30 @@ impl Ori {
self.rotated(Quaternion::rotation_y(angle_radians)) 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) /// Returns a version without sideways tilt (roll)
/// ///
/// ``` /// ```

View File

@ -80,7 +80,7 @@ impl CharacterBehavior for Data {
if data.physics.on_ground.is_some() if data.physics.on_ground.is_some()
&& (data.vel.0 - data.physics.ground_vel).magnitude_squared() < 2_f32.powi(2) && (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() } else if data.physics.in_liquid().is_some()
|| data || data
.inventory .inventory

View File

@ -1,6 +1,6 @@
use super::utils::*; use super::utils::*;
use crate::{ use crate::{
comp::{slot::EquipSlot, CharacterState, InventoryAction, StateUpdate}, comp::{slot::EquipSlot, CharacterState, InventoryAction, Ori, StateUpdate},
states::{ states::{
behavior::{CharacterBehavior, JoinData}, behavior::{CharacterBehavior, JoinData},
glide, glide,
@ -10,13 +10,25 @@ use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data { pub struct Data {
// time since left ground, for preventing state transition pub ori: Ori,
// before block snapping kicks in span_length: f32,
timer: f32, chord_length: f32,
} }
impl Default for Data { impl From<&JoinData<'_>> for Data {
fn default() -> Self { Self { timer: 0.0 } } 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 { impl CharacterBehavior for Data {
@ -29,31 +41,37 @@ impl CharacterBehavior for Data {
handle_dodge_input(data, &mut update); handle_dodge_input(data, &mut update);
handle_wield(data, &mut update); handle_wield(data, &mut update);
// 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 // If not on the ground while wielding glider enter gliding state
if data.physics.on_ground.is_none() { update.character = if data.physics.on_ground.is_none() {
update.character = if self.timer > 0.3 { CharacterState::Glide(glide::Data::new(
CharacterState::Glide(glide::Data::new(10.0, 0.6, *data.ori)) self.span_length,
} else { self.chord_length,
CharacterState::GlideWield(Self { self.ori,
timer: self.timer + data.dt.0, ))
}) // make sure we have a glider and we're not (too deep) in water
}; } else if data
}
if data
.physics
.in_liquid()
.map(|depth| depth > 0.5)
.unwrap_or(false)
{
update.character = CharacterState::Idle;
}
if data
.inventory .inventory
.and_then(|inv| inv.equipped(EquipSlot::Glider)) .and_then(|inv| inv.equipped(EquipSlot::Glider))
.is_none() .is_some()
&& data.physics.in_liquid().map_or(true, |depth| depth < 0.5)
{ {
update.character = CharacterState::Idle CharacterState::GlideWield(Self {
// 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
}; };
}
update update
} }

View File

@ -638,7 +638,7 @@ pub fn attempt_glide_wield(data: &JoinData<'_>, update: &mut StateUpdate) {
.unwrap_or(false) .unwrap_or(false)
&& data.body.is_humanoid() && data.body.is_humanoid()
{ {
update.character = CharacterState::GlideWield(glide_wield::Data::default()); update.character = CharacterState::GlideWield(glide_wield::Data::from(data));
} }
} }

View File

@ -5,8 +5,9 @@ use super::{
pub struct GlideWieldAnimation; pub struct GlideWieldAnimation;
type GlideWieldAnimationDependency = (Quaternion<f32>, Quaternion<f32>);
impl Animation for GlideWieldAnimation { impl Animation for GlideWieldAnimation {
type Dependency<'a> = (); type Dependency<'a> = GlideWieldAnimationDependency;
type Skeleton = CharacterSkeleton; type Skeleton = CharacterSkeleton;
#[cfg(feature = "use-dyn-lib")] #[cfg(feature = "use-dyn-lib")]
@ -16,21 +17,25 @@ impl Animation for GlideWieldAnimation {
fn update_skeleton_inner<'a>( fn update_skeleton_inner<'a>(
skeleton: &Self::Skeleton, skeleton: &Self::Skeleton,
_: Self::Dependency<'a>, (orientation, glider_orientation): Self::Dependency<'a>,
_anim_time: f32, _anim_time: f32,
rate: &mut f32, rate: &mut f32,
s_a: &SkeletonAttr, s_a: &SkeletonAttr,
) -> Self::Skeleton { ) -> Self::Skeleton {
let mut next = (*skeleton).clone(); 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; *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_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.hand_r.orientation = Quaternion::rotation_x(3.35) * Quaternion::rotation_y(-0.2);
next.glider.scale = Vec3::one() * 1.0; 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); next.glider.position = Vec3::new(0.0, -5.0, 13.0);

View File

@ -1572,10 +1572,10 @@ impl FigureMgr {
skeleton_attr, skeleton_attr,
) )
}, },
CharacterState::GlideWield { .. } => { CharacterState::GlideWield(data) => {
anim::character::GlideWieldAnimation::update_skeleton( anim::character::GlideWieldAnimation::update_skeleton(
&target_base, &target_base,
(), (ori, data.ori.into()),
state.state_time, state.state_time,
&mut state_animation_rate, &mut state_animation_rate,
skeleton_attr, skeleton_attr,