Merge branch 'better-physics' into 'master'

FPS independent physics

See merge request veloren/veloren!168
This commit is contained in:
Joshua Barretto 2019-06-05 19:51:18 +00:00
commit 910eccee82
2 changed files with 65 additions and 43 deletions

View File

@ -17,6 +17,16 @@ use crate::{
// Basic ECS AI agent system
pub struct Sys;
const HUMANOID_ACCEL: f32 = 100.0;
const HUMANOID_SPEED: f32 = 500.0;
const HUMANOID_AIR_ACCEL: f32 = 10.0;
const HUMANOID_AIR_SPEED: f32 = 100.0;
const HUMANOID_JUMP_ACCEL: f32 = 16.0;
const GLIDE_ACCEL: f32 = 15.0;
const GLIDE_SPEED: f32 = 45.0;
// Gravity is 9.81 * 4, so this makes gravity equal to .15
const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
@ -73,51 +83,39 @@ impl<'a> System<'a> for Sys {
continue;
}
// Handle held-down control
let on_ground = terrain
.get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
.map(|vox| !vox.is_empty())
.unwrap_or(false)
&& vel.0.z <= 0.0;
let (gliding, friction) = if on_ground {
// TODO: Don't hard-code this.
// Apply physics to the player: acceleration and non-linear deceleration.
vel.0 += Vec2::broadcast(dt.0) * control.move_dir * 200.0;
if jumps.get(entity).is_some() {
vel.0.z += 16.0;
jumps.remove(entity);
}
(false, 0.15)
let gliding = glides.get(entity).is_some() && vel.0.z < 0.0;
let move_dir = if control.move_dir.magnitude() > 1.0 {
control.move_dir.normalized()
} else {
// TODO: Don't hard-code this.
// Apply physics to the player: acceleration and non-linear deceleration.
vel.0 += Vec2::broadcast(dt.0) * control.move_dir * 10.0;
if glides.get(entity).is_some() && vel.0.z < 0.0 {
// TODO: Don't hard-code this.
let anti_grav = 9.81 * 3.95 + vel.0.z.powf(2.0) * 0.2;
vel.0.z +=
dt.0 * anti_grav * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
(true, 0.008)
} else {
(false, 0.015)
}
control.move_dir
};
// Friction
vel.0 -= Vec2::broadcast(dt.0)
* 50.0
* vel.0.map(|e| {
(e.abs() * friction * (vel.0.magnitude() * 0.1 + 0.5))
.min(e.abs() * dt.0 * 50.0)
.copysign(e)
})
* Vec3::new(1.0, 1.0, 0.0);
if on_ground {
// Move player according to move_dir
if vel.0.magnitude() < HUMANOID_SPEED {
vel.0 += Vec2::broadcast(dt.0) * move_dir * HUMANOID_ACCEL;
}
// Jump
if jumps.get(entity).is_some() && vel.0.z <= 0.0 {
vel.0.z = HUMANOID_JUMP_ACCEL;
jumps.remove(entity);
}
} else if gliding && vel.0.magnitude() < GLIDE_SPEED {
let anti_grav = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2;
vel.0.z += dt.0 * anti_grav * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
vel.0 += Vec2::broadcast(dt.0) * move_dir * GLIDE_ACCEL;
} else if vel.0.magnitude() < HUMANOID_AIR_SPEED {
vel.0 += Vec2::broadcast(dt.0) * move_dir * HUMANOID_AIR_ACCEL;
}
// Set direction based on velocity
if vel.0.magnitude_squared() != 0.0 {
ori.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0);
}

View File

@ -14,6 +14,29 @@ use vek::*;
pub struct Sys;
const GRAVITY: f32 = 9.81 * 4.0;
const FRIC_GROUND: f32 = 0.15;
const FRIC_AIR: f32 = 0.015;
// Integrates forces, calculates the new velocity based off of the old velocity
// dt = delta time
// lv = linear velocity
// damp = linear damping
// Friction is a type of damping.
fn integrate_forces(dt: f32, mut lv: Vec3<f32>, damp: f32) -> Vec3<f32> {
lv.z -= (GRAVITY * dt).max(-50.0);
let mut linear_damp = 1.0 - dt * damp;
if linear_damp < 0.0
// reached zero in the given time
{
linear_damp = 0.0;
}
lv *= linear_damp;
lv
}
impl<'a> System<'a> for Sys {
type SystemData = (
@ -31,18 +54,19 @@ impl<'a> System<'a> for Sys {
continue;
}
// Gravity
vel.0.z = (vel.0.z - GRAVITY * dt.0).max(-50.0);
let on_ground = terrain
.get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
.map(|vox| !vox.is_empty())
.unwrap_or(false)
&& vel.0.z <= 0.0;
// Movement
pos.0 += vel.0 * dt.0;
// Don't fall into the void.
// TODO: This shouldn't be needed when we have proper physics and chunk loading.
if pos.0.z < 0.0 {
pos.0.z = 0.0;
vel.0.z = 0.0;
}
// Integrate forces
// Friction is assumed to be a constant dependent on location
let friction = 50.0 * if on_ground { FRIC_GROUND } else { FRIC_AIR };
vel.0 = integrate_forces(dt.0, vel.0, friction);
// Basic collision with terrain
let mut i = 0.0;