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 {
match buff {
BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_) | Body::Ship(_)),
@ -665,37 +650,3 @@ impl Body {
impl Component for Body {
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::{
body::{object, Body, RigidWings},
body::{object, Body},
Density, Ori, Vel,
};
use crate::{
@ -105,7 +105,6 @@ impl Body {
0.5 * fluid_density
* v_sq
* wings
.filter(|_| crate::lift_enabled())
.map(|wings| {
// 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())
}
/// 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 {
/// Total lift coefficient for a finite wing of symmetric aerofoil shape and
/// elliptical pressure distribution.

View File

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

View File

@ -91,5 +91,3 @@ pub use comp::inventory::loadout_builder::LoadoutBuilder;
pub use explosion::{Explosion, RadiusEffect};
#[cfg(not(target_arch = "wasm32"))]
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 crate::{
comp::{inventory::slot::EquipSlot, CharacterState, Ori, RigidWings, StateUpdate},
states::{
behavior::{CharacterBehavior, JoinData},
utils::fly_move,
},
states::behavior::{CharacterBehavior, JoinData},
util::Dir,
};
use serde::{Deserialize, Serialize};
use vek::Vec2;
const GLIDE_ANTIGRAV: f32 = crate::consts::GRAVITY * 0.90;
const GLIDE_ACCEL: f32 = 5.0;
const GLIDE_MAX_SPEED: f32 = 30.0;
use vek::*;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data {
@ -36,50 +29,69 @@ impl CharacterBehavior for Data {
// If player is on ground, end glide
if data.physics.on_ground && data.vel.0.magnitude_squared() < 25.0 {
update.character = CharacterState::GlideWield;
return update;
}
if data
update
} else if data
.physics
.in_liquid()
.map(|depth| depth > 0.5)
.unwrap_or(false)
|| data.inventory.equipped(EquipSlot::Glider).is_none()
{
update.character = CharacterState::Idle;
}
if data.inventory.equipped(EquipSlot::Glider).is_none() {
update.character = CharacterState::Idle
};
if crate::lift_enabled() {
fly_move(data, &mut update, inline_tweak::tweak!(0.1));
update
} else if handle_climb(&data, &mut update) {
update
} else {
let horiz_vel = Vec2::<f32>::from(update.vel.0);
let horiz_speed_sq = horiz_vel.magnitude_squared();
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))
* 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
if horiz_speed_sq < GLIDE_MAX_SPEED.powi(2) {
update.vel.0 += Vec2::broadcast(data.dt.0) * data.inputs.move_dir * GLIDE_ACCEL;
}
// 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);
let rate = {
let angle = self.ori.look_dir().angle_between(*data.inputs.look_dir);
0.4 * std::f32::consts::PI / angle
};
// Apply Glide antigrav lift
if update.vel.0.z < 0.0 {
let lift = (GLIDE_ANTIGRAV + update.vel.0.z.powi(2) * 0.15)
* (horiz_speed_sq * f32::powf(0.075, 2.0)).clamp(0.2, 1.0);
let ori = self
.ori
.slerped_towards(tgt_ori, (data.dt.0 * rate).min(0.1));
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 {

View File

@ -6,7 +6,7 @@ use crate::{
quadruped_low, quadruped_medium, quadruped_small, ship,
skills::{Skill, SwimSkill},
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
InventoryAction, Ori, StateUpdate,
InventoryAction, StateUpdate,
},
consts::{FRIC_GROUND, GRAVITY},
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))
{
let thrust = efficiency * force;
let accel = thrust / data.mass.0;
// if lift is enabled we do some more advanced stuff with pitch and roll
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);
}
handle_orientation(data, update, efficiency);
// Elevation control
match data.body {
// 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));
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
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()
&& data.physics.on_wall.is_some()
&& !data.physics.on_ground
@ -544,6 +495,9 @@ pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) {
&& update.energy.current() > 100
{
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),
_ => 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
* body.aerodynamic_forces(
glider.map(|g| &g.ori).unwrap_or(ori),
&rel_flow,
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());
if !impulse.is_approx_zero() {