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
- 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

View File

@ -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)
///
/// ```

View File

@ -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

View File

@ -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
}

View File

@ -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));
}
}

View File

@ -5,8 +5,9 @@ use super::{
pub struct GlideWieldAnimation;
type GlideWieldAnimationDependency = (Quaternion<f32>, Quaternion<f32>);
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);

View File

@ -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,