Added on_ceiling check for sticky entities

This commit is contained in:
Joshua Barretto
2020-04-26 15:44:03 +01:00
parent eb23b0b2bd
commit 8e0f40c71e
4 changed files with 62 additions and 48 deletions

View File

@ -35,7 +35,7 @@ pub use inventory::{
}; };
pub use last::Last; pub use last::Last;
pub use location::{Waypoint, WaypointArea}; pub use location::{Waypoint, WaypointArea};
pub use phys::{ForceUpdate, Gravity, Mass, Collider, Ori, PhysicsState, Pos, Scale, Sticky, Vel}; pub use phys::{Collider, ForceUpdate, Gravity, Mass, Ori, PhysicsState, Pos, Scale, Sticky, Vel};
pub use player::Player; pub use player::Player;
pub use projectile::Projectile; pub use projectile::Projectile;
pub use stats::{Exp, HealthChange, HealthSource, Level, Stats}; pub use stats::{Exp, HealthChange, HealthSource, Level, Stats};

View File

@ -46,11 +46,7 @@ impl Component for Mass {
// Mass // Mass
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Collider { pub enum Collider {
Box { Box { radius: f32, z_min: f32, z_max: f32 },
radius: f32,
z_min: f32,
z_max: f32,
},
Point, Point,
} }

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
comp::{Gravity, Mass, Collider, Mounting, Ori, PhysicsState, Pos, Scale, Sticky, Vel}, comp::{Collider, Gravity, Mass, Mounting, Ori, PhysicsState, Pos, Scale, Sticky, Vel},
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
state::DeltaTime, state::DeltaTime,
sync::Uid, sync::Uid,
@ -93,7 +93,7 @@ impl<'a> System<'a> for Sys {
{ {
let mut physics_state = physics_states.get(entity).cloned().unwrap_or_default(); let mut physics_state = physics_states.get(entity).cloned().unwrap_or_default();
if sticky.is_some() && (physics_state.on_ground || physics_state.on_wall.is_some()) { if sticky.is_some() && (physics_state.on_ground || physics_state.on_ceiling || physics_state.on_wall.is_some()) {
continue; continue;
} }
@ -133,7 +133,11 @@ impl<'a> System<'a> for Sys {
}; };
match collider { match collider {
Collider::Box { radius, z_min, z_max } => { Collider::Box {
radius,
z_min,
z_max,
} => {
// Scale collider // Scale collider
let radius = *radius * scale; let radius = *radius * scale;
let z_min = *z_min * scale; let z_min = *z_min * scale;
@ -145,7 +149,9 @@ impl<'a> System<'a> for Sys {
let near_iter = (-hdist..hdist + 1) let near_iter = (-hdist..hdist + 1)
.map(move |i| { .map(move |i| {
(-hdist..hdist + 1).map(move |j| { (-hdist..hdist + 1).map(move |j| {
(1 - BlockKind::MAX_HEIGHT.ceil() as i32 + z_min.floor() as i32..z_max.ceil() as i32 + 1).map(move |k| (i, j, k)) (1 - BlockKind::MAX_HEIGHT.ceil() as i32 + z_min.floor() as i32
..z_max.ceil() as i32 + 1)
.map(move |k| (i, j, k))
}) })
}) })
.flatten() .flatten()
@ -153,7 +159,9 @@ impl<'a> System<'a> for Sys {
// Function for determining whether the player at a specific position collides // Function for determining whether the player at a specific position collides
// with the ground // with the ground
let collision_with = |pos: Vec3<f32>, hit: &dyn Fn(&Block) -> bool, near_iter| { let collision_with = |pos: Vec3<f32>,
hit: &dyn Fn(&Block) -> bool,
near_iter| {
for (i, j, k) in near_iter { for (i, j, k) in near_iter {
let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k); let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k);
@ -243,8 +251,8 @@ impl<'a> System<'a> for Sys {
// Find the intrusion vector of the collision // Find the intrusion vector of the collision
let dir = player_aabb.collision_vector_with_aabb(block_aabb); let dir = player_aabb.collision_vector_with_aabb(block_aabb);
// Determine an appropriate resolution vector (i.e: the minimum distance needed // Determine an appropriate resolution vector (i.e: the minimum distance
// to push out of the block) // needed to push out of the block)
let max_axis = dir.map(|e| e.abs()).reduce_partial_min(); let max_axis = dir.map(|e| e.abs()).reduce_partial_min();
let resolve_dir = -dir.map(|e| { let resolve_dir = -dir.map(|e| {
if e.abs().to_bits() == max_axis.to_bits() { if e.abs().to_bits() == max_axis.to_bits() {
@ -254,19 +262,21 @@ impl<'a> System<'a> for Sys {
} }
}); });
// When the resolution direction is pointing upwards, we must be on the ground // When the resolution direction is pointing upwards, we must be on the
// ground
if resolve_dir.z > 0.0 && vel.0.z <= 0.0 { if resolve_dir.z > 0.0 && vel.0.z <= 0.0 {
on_ground = true; on_ground = true;
if !was_on_ground { if !was_on_ground {
event_emitter.emit(ServerEvent::LandOnGround { entity, vel: vel.0 }); event_emitter
.emit(ServerEvent::LandOnGround { entity, vel: vel.0 });
} }
} else if resolve_dir.z < 0.0 && vel.0.z >= 0.0 { } else if resolve_dir.z < 0.0 && vel.0.z >= 0.0 {
on_ceiling = true; on_ceiling = true;
} }
// When the resolution direction is non-vertical, we must be colliding with a // When the resolution direction is non-vertical, we must be colliding
// wall If the space above is free... // with a wall If the space above is free...
if !collision_with(Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()), &|block| block.is_solid(), near_iter.clone()) if !collision_with(Vec3::new(pos.0.x, pos.0.y, (pos.0.z + 0.1).ceil()), &|block| block.is_solid(), near_iter.clone())
// ...and we're being pushed out horizontally... // ...and we're being pushed out horizontally...
&& resolve_dir.z == 0.0 && resolve_dir.z == 0.0
@ -291,10 +301,9 @@ impl<'a> System<'a> for Sys {
break; break;
} else { } else {
// Correct the velocity // Correct the velocity
vel.0 = vel.0.map2( vel.0 = vel.0.map2(resolve_dir, |e, d| {
resolve_dir, if d * e.signum() < 0.0 { 0.0 } else { e }
|e, d| if d * e.signum() < 0.0 { 0.0 } else { e }, });
);
} }
// Resolve the collision normally // Resolve the collision normally
@ -326,13 +335,17 @@ impl<'a> System<'a> for Sys {
&& !collision_with( && !collision_with(
pos.0 - Vec3::unit_z() * 0.05, pos.0 - Vec3::unit_z() * 0.05,
&|block| { &|block| {
block.is_solid() && block.get_height() >= (pos.0.z - 0.05).rem_euclid(1.0) block.is_solid()
&& block.get_height() >= (pos.0.z - 0.05).rem_euclid(1.0)
}, },
near_iter.clone(), near_iter.clone(),
) )
{ {
let snap_height = terrain let snap_height = terrain
.get(Vec3::new(pos.0.x, pos.0.y, pos.0.z - 0.05).map(|e| e.floor() as i32)) .get(
Vec3::new(pos.0.x, pos.0.y, pos.0.z - 0.05)
.map(|e| e.floor() as i32),
)
.ok() .ok()
.filter(|block| block.is_solid()) .filter(|block| block.is_solid())
.map(|block| block.get_height()) .map(|block| block.get_height())
@ -348,17 +361,19 @@ impl<'a> System<'a> for Sys {
-Vec3::unit_y(), -Vec3::unit_y(),
]; ];
if let (wall_dir, true) = dirs.iter().fold((Vec3::zero(), false), |(a, hit), dir| { if let (wall_dir, true) =
if collision_with( dirs.iter().fold((Vec3::zero(), false), |(a, hit), dir| {
pos.0 + *dir * 0.01, if collision_with(
&|block| block.is_solid(), pos.0 + *dir * 0.01,
near_iter.clone(), &|block| block.is_solid(),
) { near_iter.clone(),
(a + dir, true) ) {
} else { (a + dir, true)
(a, hit) } else {
} (a, hit)
}) { }
})
{
physics_state.on_wall = Some(wall_dir); physics_state.on_wall = Some(wall_dir);
} else { } else {
physics_state.on_wall = None; physics_state.on_wall = None;
@ -369,33 +384,36 @@ impl<'a> System<'a> for Sys {
collision_with(pos.0, &|block| block.is_fluid(), near_iter.clone()); collision_with(pos.0, &|block| block.is_fluid(), near_iter.clone());
}, },
Collider::Point => { Collider::Point => {
let (dist, block) = terrain let (dist, block) = terrain.ray(pos.0, pos.0 + pos_delta).ignore_error().cast();
.ray(pos.0, pos.0 + pos_delta)
.ignore_error()
.cast();
pos.0 += pos_delta.try_normalized().unwrap_or(Vec3::zero()) * dist; pos.0 += pos_delta.try_normalized().unwrap_or(Vec3::zero()) * dist;
if block.unwrap().is_some() { // Can't fail if block.unwrap().is_some() {
// Can't fail
if sticky.is_some() { if sticky.is_some() {
vel.0 = Vec3::zero(); vel.0 = Vec3::zero();
let block_center = pos.0.map(|e| e.floor()) + 0.5; let block_center = pos.0.map(|e| e.floor()) + 0.5;
let block_rpos = (pos.0 - block_center).try_normalized().unwrap_or(Vec3::zero()); let block_rpos = (pos.0 - block_center)
.try_normalized()
.unwrap_or(Vec3::zero());
// See whether we're on the top/bottom of a block, or the side // See whether we're on the top/bottom of a block, or the side
if block_rpos.z.abs() > block_rpos.xy().map(|e| e.abs()).reduce_partial_max() { if block_rpos.z.abs()
> block_rpos.xy().map(|e| e.abs()).reduce_partial_max()
{
if block_rpos.z > 0.0 { if block_rpos.z > 0.0 {
physics_state.on_ground = true; physics_state.on_ground = true;
} else { } else {
physics_state.on_ceiling = true; physics_state.on_ceiling = true;
} }
} else { } else {
physics_state.on_wall = Some(if block_rpos.x.abs() > block_rpos.y.abs() { physics_state.on_wall =
Vec3::unit_x() * -block_rpos.x.signum() Some(if block_rpos.x.abs() > block_rpos.y.abs() {
} else { Vec3::unit_x() * -block_rpos.x.signum()
Vec3::unit_y() * -block_rpos.y.signum() } else {
}); Vec3::unit_y() * -block_rpos.y.signum()
});
} }
} }
} }

View File

@ -1,8 +1,8 @@
use super::SysTimer; use super::SysTimer;
use common::{ use common::{
comp::{ comp::{
Body, CanBuild, CharacterState, Energy, Gravity, Item, LightEmitter, Loadout, Mass, Collider, Body, CanBuild, CharacterState, Collider, Energy, Gravity, Item, LightEmitter, Loadout,
MountState, Mounting, Ori, Player, Pos, Scale, Stats, Sticky, Vel, Mass, MountState, Mounting, Ori, Player, Pos, Scale, Stats, Sticky, Vel,
}, },
msg::EcsCompPacket, msg::EcsCompPacket,
sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, Uid, UpdateTracker, WorldSyncExt}, sync::{CompSyncPackage, EntityPackage, EntitySyncPackage, Uid, UpdateTracker, WorldSyncExt},