From 68732bebdeacddbdb4a5c249e3380549deca44f0 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Tue, 21 Apr 2020 00:34:35 +0100 Subject: [PATCH] Fixed block snapping for bizarre block heights --- common/src/sys/phys.rs | 46 ++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/common/src/sys/phys.rs b/common/src/sys/phys.rs index 36cceb287f..8cfdb9ed48 100644 --- a/common/src/sys/phys.rs +++ b/common/src/sys/phys.rs @@ -152,7 +152,7 @@ impl<'a> System<'a> for Sys { // Function for determining whether the player at a specific position collides // with the ground - let collision_with = |pos: Vec3, hit: fn(&Block) -> bool, near_iter| { + let collision_with = |pos: Vec3, hit: &dyn Fn(&Block) -> bool, near_iter| { for (i, j, k) in near_iter { let block_pos = pos.map(|e| e.floor() as i32) + Vec3::new(i, j, k); @@ -192,7 +192,7 @@ impl<'a> System<'a> for Sys { const MAX_ATTEMPTS: usize = 16; // While the player is colliding with the terrain... - while collision_with(pos.0, |vox| vox.is_solid(), near_iter.clone()) + while collision_with(pos.0, &|block| block.is_solid(), near_iter.clone()) && attempts < MAX_ATTEMPTS { // Calculate the player's AABB @@ -203,7 +203,7 @@ impl<'a> System<'a> for Sys { // Determine the block that we are colliding with most (based on minimum // collision axis) - let (_block_pos, block_aabb) = near_iter + let (_block_pos, block_aabb, block_height) = near_iter .clone() // Calculate the block's position in world space .map(|(i, j, k)| pos.0.map(|e| e.floor() as i32) + Vec3::new(i, j, k)) @@ -221,15 +221,16 @@ impl<'a> System<'a> for Sys { min: block_pos.map(|e| e as f32), max: block_pos.map(|e| e as f32) + Vec3::new(1.0, 1.0, block.get_height()), }, + block.get_height(), )) } else { None } }) // Determine whether the block's AABB collides with the player's AABB - .filter(|(_, block_aabb)| block_aabb.collides_with_aabb(player_aabb)) + .filter(|(_, block_aabb, _)| block_aabb.collides_with_aabb(player_aabb)) // Find the maximum of the minimum collision axes (this bit is weird, trust me that it works) - .min_by_key(|(_, block_aabb)| { + .min_by_key(|(_, block_aabb, _)| { ((block_aabb.center() - player_aabb.center() - Vec3::unit_z() * 0.5) .map(|e| e.abs()) .sum() @@ -262,7 +263,7 @@ impl<'a> System<'a> for Sys { // When the resolution direction is non-vertical, we must be colliding 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()), |vox| vox.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... && resolve_dir.z == 0.0 // ...and the vertical resolution direction is sufficiently great... @@ -270,17 +271,17 @@ impl<'a> System<'a> for Sys { // ...and we're falling/standing OR there is a block *directly* beneath our current origin (note: not hitbox)... && (vel.0.z <= 0.0 || terrain .get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32)) - .map(|vox| vox.is_solid()) + .map(|block| block.is_solid()) .unwrap_or(false)) // ...and there is a collision with a block beneath our current hitbox... && collision_with( old_pos + resolve_dir - Vec3::unit_z() * 1.05, - |vox| vox.is_solid(), + &|block| block.is_solid(), near_iter.clone(), ) { // ...block-hop! - pos.0.z = (pos.0.z + 0.1).ceil(); + pos.0.z = (pos.0.z + 0.1).floor() + block_height; vel.0.z = 0.0; on_ground = true; break; @@ -309,20 +310,25 @@ impl<'a> System<'a> for Sys { // If the space below us is free, then "snap" to the ground } else if collision_with( pos.0 - Vec3::unit_z() * 1.05, - |vox| vox.is_solid(), + &|block| block.is_solid(), near_iter.clone(), ) && vel.0.z < 0.0 && vel.0.z > -1.5 && was_on_ground - && !terrain - .get( - Vec3::new(pos.0.x, pos.0.y, (pos.0.z - 0.05).floor()) - .map(|e| e.floor() as i32), - ) - .map(|vox| vox.is_solid()) - .unwrap_or(false) + && !collision_with( + pos.0 - Vec3::unit_z() * 0.05, + &|block| block.is_solid() && block.get_height() >= (pos.0.z - 0.05).rem_euclid(1.0), + near_iter.clone(), + ) { - pos.0.z = (pos.0.z - 0.05).floor(); + let snap_height = terrain + .get(Vec3::new(pos.0.x, pos.0.y, pos.0.z - 0.05) + .map(|e| e.floor() as i32)) + .ok() + .filter(|block| block.is_solid()) + .map(|block| block.get_height()) + .unwrap_or(0.0); + pos.0.z = (pos.0.z - 0.05).floor() + snap_height; physics_state.on_ground = true; } @@ -334,7 +340,7 @@ impl<'a> System<'a> for Sys { ]; if let (wall_dir, true) = dirs.iter().fold((Vec3::zero(), false), |(a, hit), dir| { - if collision_with(pos.0 + *dir * 0.01, |vox| vox.is_solid(), near_iter.clone()) { + if collision_with(pos.0 + *dir * 0.01, &|block| block.is_solid(), near_iter.clone()) { (a + dir, true) } else { (a, hit) @@ -346,7 +352,7 @@ impl<'a> System<'a> for Sys { } // Figure out if we're in water - physics_state.in_fluid = collision_with(pos.0, |vox| vox.is_fluid(), near_iter.clone()); + physics_state.in_fluid = collision_with(pos.0, &|block| block.is_fluid(), near_iter.clone()); let _ = physics_states.insert(entity, physics_state); }