No gliding bodies, only gliding glider

This commit is contained in:
Ludvig Böklin 2021-04-24 17:06:57 +02:00
parent 906bf798e7
commit 96168b5654
7 changed files with 97 additions and 156 deletions

View File

@ -594,21 +594,6 @@ impl Body {
} }
} }
pub fn wings(&self) -> Option<RigidWings> {
matches!(
self,
Body::BirdMedium(_)
| Body::BirdSmall(_)
| Body::Dragon(_)
| Body::FishMedium(_)
| Body::FishSmall(_)
)
.then_some({
let dim = self.dimensions().xy();
RigidWings::new(dim.x, dim.y * 0.2)
})
}
pub fn immune_to(&self, buff: BuffKind) -> bool { pub fn immune_to(&self, buff: BuffKind) -> bool {
match buff { match buff {
BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_) | Body::Ship(_)), BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_) | Body::Ship(_)),
@ -665,37 +650,3 @@ impl Body {
impl Component for Body { impl Component for Body {
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>; type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
} }
/// An elliptical fixed rigid wing. Plurally named simply because it's a shape
/// typically composed of two wings forming an elliptical lift distribution.
///
/// Animal wings are technically flexible, not rigid, (difference being that the
/// former's shape is affected by the flow) and usually has the ability to
/// assume complex shapes with properties like curved camber line, span-wise
/// twist, dihedral angle, sweep angle, and partitioned sections. However, we
/// can make do with this model for fully extended animal wings, enabling them
/// to glide.
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct RigidWings {
aspect_ratio: f32,
planform_area: f32,
// sweep_angle: Option<f32>,
}
impl RigidWings {
/// Wings from total span (wing-tip to wing-tip) and
/// chord length (leading edge to trailing edge)
pub fn new(span_length: f32, chord_length: f32) -> Self {
let planform_area = std::f32::consts::PI * chord_length * span_length * 0.25;
Self {
aspect_ratio: span_length.powi(2) / planform_area,
planform_area,
}
}
/// The aspect ratio is the ratio of the span squared to actual planform
/// area
pub fn aspect_ratio(&self) -> f32 { self.aspect_ratio }
pub fn planform_area(&self) -> f32 { self.planform_area }
}

View File

@ -1,5 +1,5 @@
use super::{ use super::{
body::{object, Body, RigidWings}, body::{object, Body},
Density, Ori, Vel, Density, Ori, Vel,
}; };
use crate::{ use crate::{
@ -105,7 +105,6 @@ impl Body {
0.5 * fluid_density 0.5 * fluid_density
* v_sq * v_sq
* wings * wings
.filter(|_| crate::lift_enabled())
.map(|wings| { .map(|wings| {
// Since we have wings, we proceed to calculate the lift and drag // Since we have wings, we proceed to calculate the lift and drag
@ -268,6 +267,40 @@ fn angle_of_attack(ori: &Ori, rel_flow_dir: &Dir) -> f32 {
PI / 2.0 - ori.up().angle_between(rel_flow_dir.to_vec()) PI / 2.0 - ori.up().angle_between(rel_flow_dir.to_vec())
} }
/// An elliptical fixed rigid wing. Plurally named simply because it's a shape
/// typically composed of two wings forming an elliptical lift distribution.
//
// Animal wings are technically flexible, not rigid, (difference being that the
// former's shape is affected by the flow) and usually has the ability to
// assume complex shapes with properties like curved camber line, span-wise
// twist, dihedral angle, sweep angle, and partitioned sections. However, we
// could make do with this model for fully extended animal wings, enabling them
// to glide.
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct RigidWings {
aspect_ratio: f32,
planform_area: f32,
// sweep_angle: Option<f32>,
}
impl RigidWings {
/// Wings from total span (wing-tip to wing-tip) and
/// chord length (leading edge to trailing edge)
pub fn new(span_length: f32, chord_length: f32) -> Self {
let planform_area = std::f32::consts::PI * chord_length * span_length * 0.25;
Self {
aspect_ratio: span_length.powi(2) / planform_area,
planform_area,
}
}
/// The aspect ratio is the ratio of the span squared to actual planform
/// area
pub fn aspect_ratio(&self) -> f32 { self.aspect_ratio }
pub fn planform_area(&self) -> f32 { self.planform_area }
}
impl RigidWings { impl RigidWings {
/// Total lift coefficient for a finite wing of symmetric aerofoil shape and /// Total lift coefficient for a finite wing of symmetric aerofoil shape and
/// elliptical pressure distribution. /// elliptical pressure distribution.

View File

@ -52,7 +52,7 @@ pub use self::{
body::{ body::{
biped_large, biped_small, bird_large, bird_medium, dragon, fish_medium, fish_small, golem, biped_large, biped_small, bird_large, bird_medium, dragon, fish_medium, fish_small, golem,
humanoid, object, quadruped_low, quadruped_medium, quadruped_small, ship, theropod, humanoid, object, quadruped_low, quadruped_medium, quadruped_small, ship, theropod,
AllBodies, Body, BodyData, RigidWings, AllBodies, Body, BodyData,
}, },
buff::{ buff::{
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs, Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs,
@ -68,7 +68,7 @@ pub use self::{
InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting, InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting,
}, },
energy::{Energy, EnergyChange, EnergySource}, energy::{Energy, EnergyChange, EnergySource},
fluid_dynamics::Fluid, fluid_dynamics::{Fluid, RigidWings},
group::Group, group::Group,
home_chunk::HomeChunk, home_chunk::HomeChunk,
inputs::CanBuild, inputs::CanBuild,

View File

@ -91,5 +91,3 @@ pub use comp::inventory::loadout_builder::LoadoutBuilder;
pub use explosion::{Explosion, RadiusEffect}; pub use explosion::{Explosion, RadiusEffect};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
pub use skillset_builder::SkillSetBuilder; pub use skillset_builder::SkillSetBuilder;
pub fn lift_enabled() -> bool { inline_tweak::tweak!(true) }

View File

@ -1,18 +1,11 @@
use super::utils::handle_climb; use super::utils::handle_climb;
use crate::{ use crate::{
comp::{inventory::slot::EquipSlot, CharacterState, Ori, RigidWings, StateUpdate}, comp::{inventory::slot::EquipSlot, CharacterState, Ori, RigidWings, StateUpdate},
states::{ states::behavior::{CharacterBehavior, JoinData},
behavior::{CharacterBehavior, JoinData},
utils::fly_move,
},
util::Dir, util::Dir,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use vek::Vec2; use vek::*;
const GLIDE_ANTIGRAV: f32 = crate::consts::GRAVITY * 0.90;
const GLIDE_ACCEL: f32 = 5.0;
const GLIDE_MAX_SPEED: f32 = 30.0;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data { pub struct Data {
@ -36,50 +29,69 @@ impl CharacterBehavior for Data {
// If player is on ground, end glide // If player is on ground, end glide
if data.physics.on_ground && data.vel.0.magnitude_squared() < 25.0 { if data.physics.on_ground && data.vel.0.magnitude_squared() < 25.0 {
update.character = CharacterState::GlideWield; update.character = CharacterState::GlideWield;
return update; update
} } else if data
if data
.physics .physics
.in_liquid() .in_liquid()
.map(|depth| depth > 0.5) .map(|depth| depth > 0.5)
.unwrap_or(false) .unwrap_or(false)
|| data.inventory.equipped(EquipSlot::Glider).is_none()
{ {
update.character = CharacterState::Idle; update.character = CharacterState::Idle;
} update
if data.inventory.equipped(EquipSlot::Glider).is_none() { } else if handle_climb(&data, &mut update) {
update.character = CharacterState::Idle update
};
if crate::lift_enabled() {
fly_move(data, &mut update, inline_tweak::tweak!(0.1));
} else { } else {
let horiz_vel = Vec2::<f32>::from(update.vel.0); let tgt_ori = Some(data.inputs.move_dir)
let horiz_speed_sq = horiz_vel.magnitude_squared(); .filter(|mv_dir| !mv_dir.is_approx_zero())
.map(|mv_dir| {
Vec3::new(
mv_dir.x,
mv_dir.y,
Lerp::lerp_unclamped(
0.0,
data.inputs.look_dir.z + inline_tweak::tweak!(0.3),
mv_dir.magnitude_squared() * inline_tweak::tweak!(2.0),
),
)
})
.and_then(Dir::from_unnormalized)
.and_then(|tgt_dir| {
Dir::from_unnormalized(data.vel.0)
.and_then(|moving_dir| moving_dir.to_horizontal())
.map(|moving_dir| {
Ori::from(tgt_dir).rolled_right(
(1.0 - moving_dir.dot(*tgt_dir).max(0.0))
* self.ori.right().dot(*tgt_dir).signum()
* std::f32::consts::PI
/ 3.0,
)
})
})
.or_else(|| self.ori.look_dir().to_horizontal().map(Ori::from))
.unwrap_or_default();
// Move player according to movement direction vector let rate = {
if horiz_speed_sq < GLIDE_MAX_SPEED.powi(2) { let angle = self.ori.look_dir().angle_between(*data.inputs.look_dir);
update.vel.0 += Vec2::broadcast(data.dt.0) * data.inputs.move_dir * GLIDE_ACCEL; 0.4 * std::f32::consts::PI / angle
}
// Determine orientation vector from movement direction vector
if let Some(dir) = Dir::from_unnormalized(update.vel.0) {
update.ori = update.ori.slerped_towards(Ori::from(dir), 2.0 * data.dt.0);
}; };
// Apply Glide antigrav lift let ori = self
if update.vel.0.z < 0.0 { .ori
let lift = (GLIDE_ANTIGRAV + update.vel.0.z.powi(2) * 0.15) .slerped_towards(tgt_ori, (data.dt.0 * rate).min(0.1));
* (horiz_speed_sq * f32::powf(0.075, 2.0)).clamp(0.2, 1.0); update.character = CharacterState::Glide(Self { ori, ..*self });
update.vel.0.z += lift * data.dt.0; if let Some(char_ori) = ori.to_horizontal() {
let rate = {
let angle = ori.look_dir().angle_between(*data.inputs.look_dir);
data.body.base_ori_rate() * std::f32::consts::PI / angle
};
update.ori = update
.ori
.slerped_towards(char_ori, (data.dt.0 * rate).min(0.1));
} }
update
} }
// If there is a wall in front of character and they are trying to climb go to
// climb
handle_climb(&data, &mut update);
update
} }
fn unwield(&self, data: &JoinData) -> StateUpdate { fn unwield(&self, data: &JoinData) -> StateUpdate {

View File

@ -6,7 +6,7 @@ use crate::{
quadruped_low, quadruped_medium, quadruped_small, ship, quadruped_low, quadruped_medium, quadruped_small, ship,
skills::{Skill, SwimSkill}, skills::{Skill, SwimSkill},
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind, theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
InventoryAction, Ori, StateUpdate, InventoryAction, StateUpdate,
}, },
consts::{FRIC_GROUND, GRAVITY}, consts::{FRIC_GROUND, GRAVITY},
event::{LocalEvent, ServerEvent}, event::{LocalEvent, ServerEvent},
@ -377,63 +377,14 @@ pub fn fly_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) -> b
.or_else(|| glider.is_some().then_some(0.0)) .or_else(|| glider.is_some().then_some(0.0))
{ {
let thrust = efficiency * force; let thrust = efficiency * force;
let accel = thrust / data.mass.0; let accel = thrust / data.mass.0;
// if lift is enabled we do some more advanced stuff with pitch and roll handle_orientation(data, update, efficiency);
if crate::lift_enabled() && !matches!(data.body, Body::Ship(_)) {
let mut ori = glider.map(|g| g.ori).unwrap_or(update.ori);
let fw_dir = ori.look_dir().to_horizontal();
let tgt_ori = Some(data.inputs.move_dir)
.filter(|mv_dir| !mv_dir.is_approx_zero())
.map(|mv_dir| {
Vec3::new(
mv_dir.x,
mv_dir.y,
Lerp::lerp_unclamped(
0.0,
data.inputs.look_dir.z + inline_tweak::tweak!(0.3),
mv_dir.magnitude_squared() * inline_tweak::tweak!(2.0),
),
)
})
.and_then(Dir::from_unnormalized)
.and_then(|tgt_dir| {
Dir::from_unnormalized(data.vel.0)
.and_then(|moving_dir| moving_dir.to_horizontal())
.map(|moving_dir| {
Ori::from(tgt_dir).rolled_right(
(1.0 - moving_dir.dot(*tgt_dir).max(0.0))
* ori.right().dot(*tgt_dir).signum()
* std::f32::consts::PI
/ 3.0,
)
})
})
.or_else(|| fw_dir.map(Ori::from))
.unwrap_or_default();
let rate = {
let angle = ori.look_dir().angle_between(*data.inputs.look_dir);
data.body.base_ori_rate() * efficiency * std::f32::consts::PI / angle
};
ori = ori.slerped_towards(tgt_ori, (data.dt.0 * rate).min(0.1));
if let Some(data) = glider {
update.character = CharacterState::Glide(glide::Data { ori, ..*data });
if let Some(char_ori) = ori.to_horizontal() {
update.ori = char_ori;
}
} else {
update.ori = ori;
}
} else {
handle_orientation(data, update, efficiency);
}
// Elevation control // Elevation control
match data.body { match data.body {
// flappy flappy // flappy flappy
Body::Dragon(_) | Body::BirdMedium(_) | Body::BirdLarge(_) => { Body::Dragon(_) | Body::BirdLarge(_) | Body::BirdMedium(_) => {
let anti_grav = GRAVITY * (1.0 + data.inputs.move_z.min(0.0)); let anti_grav = GRAVITY * (1.0 + data.inputs.move_z.min(0.0));
update.vel.0.z += data.dt.0 * (anti_grav + accel * data.inputs.move_z.max(0.0)); update.vel.0.z += data.dt.0 * (anti_grav + accel * data.inputs.move_z.max(0.0));
}, },
@ -530,7 +481,7 @@ pub fn attempt_sneak(data: &JoinData, update: &mut StateUpdate) {
} }
/// Checks that player can `Climb` and updates `CharacterState` if so /// Checks that player can `Climb` and updates `CharacterState` if so
pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) { pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) -> bool {
if data.inputs.climb.is_some() if data.inputs.climb.is_some()
&& data.physics.on_wall.is_some() && data.physics.on_wall.is_some()
&& !data.physics.on_ground && !data.physics.on_ground
@ -544,6 +495,9 @@ pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) {
&& update.energy.current() > 100 && update.energy.current() > 100
{ {
update.character = CharacterState::Climb(climb::Data::create_adjusted_by_skills(data)); update.character = CharacterState::Climb(climb::Data::create_adjusted_by_skills(data));
true
} else {
false
} }
} }

View File

@ -65,19 +65,12 @@ fn integrate_forces(
CharacterState::Glide(data) => Some(data), CharacterState::Glide(data) => Some(data),
_ => None, _ => None,
}); });
// let wings: Option<(RigidWings, Ori)> =
// body.wings().map(|w| (w, ori)).or(character_state.and_then(|cs| {
// match cs {
// CharacterState::Glide(states::glide::Data{wings, ori}) =>
// Some((*wings, *ori)), _ => None,
// }
// }));
let impulse = dt.0 let impulse = dt.0
* body.aerodynamic_forces( * body.aerodynamic_forces(
glider.map(|g| &g.ori).unwrap_or(ori), glider.map(|g| &g.ori).unwrap_or(ori),
&rel_flow, &rel_flow,
fluid_density.0, fluid_density.0,
glider.map(|g| g.wings).or_else(|| body.wings()).as_ref(), glider.map(|g| g.wings).as_ref(),
); );
debug_assert!(!impulse.map(|a| a.is_nan()).reduce_or()); debug_assert!(!impulse.map(|a| a.is_nan()).reduce_or());
if !impulse.is_approx_zero() { if !impulse.is_approx_zero() {