mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
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:
parent
b667ba86f9
commit
ebf489984c
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -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
|
||||||
|
@ -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 not on the ground while wielding glider enter gliding state
|
// If still in this state, do the things
|
||||||
if data.physics.on_ground.is_none() {
|
if matches!(update.character, CharacterState::GlideWield(_)) {
|
||||||
update.character = if self.timer > 0.3 {
|
// If not on the ground while wielding glider enter gliding state
|
||||||
CharacterState::Glide(glide::Data::new(10.0, 0.6, *data.ori))
|
update.character = if data.physics.on_ground.is_none() {
|
||||||
} else {
|
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 {
|
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
|
update
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user