From 66e436d0f2cf3c33105d8a5bce43bf64d5e72255 Mon Sep 17 00:00:00 2001 From: Mel Date: Thu, 25 Jan 2024 11:25:29 +0100 Subject: Mostly functioning world collisions --- src/Entities/Player.cpp | 71 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 18 deletions(-) (limited to 'src/Entities/Player.cpp') diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index fb88222..fd22247 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -3,6 +3,7 @@ namespace MC::Entities { void Player::update(const Time& time, GFX::Window& window, GFX::Camera& camera, World::World& world) { + auto origin = m_transform.position(); auto r = window.mouse_delta(); auto key = [&](Int k) -> Real { return window.key(k, GLFW_PRESS); }; @@ -16,12 +17,9 @@ void Player::update(const Time& time, GFX::Window& window, GFX::Camera& camera, auto rotation_speed = 0.1f; auto direction = m_transform.right() * x + Vec3(0, y, 0) + m_transform.forward() * z; - auto destination = m_transform.position() + direction * move_speed; + auto destination = origin + direction * move_speed; - auto collision_domain = terrain_collision_domain(m_transform.position(), destination, world); - for (auto collision : collision_domain) { - destination = collision_reposition(m_transform.position(), destination, collision); - } + destination = process_collisions(world, origin, destination); move_to(destination); rotate({r.y() * rotation_speed, r.x() * rotation_speed, 0.0f}); @@ -50,19 +48,51 @@ AABB Player::bounds() const { return bounding_box_for_position(m_transform.position()); } -void Player::update_camera_position(GFX::Camera& camera) { - auto camera_position = m_transform.position(); - camera_position.y() += 1.5; +#define STUCK_THRESHOLD 100 - camera.set_position(camera_position); - camera.set_angles(m_transform.rotation()); +Position::World Player::process_collisions(World::World& world, Position::World from, Position::World to) { + auto current_box = bounding_box_for_position(from); + + // All the blocks we could theoretically collide with. + auto collision_domain = terrain_collision_domain(from, to, world); + + // Sort the pushouts by magnitude, and apply the biggest one. + // If we apply a pushout, we need to check again for collisions, + // since we might have slid into another block. (up to STUCK_THRESHOLD times) + // If there are no pushouts, we have no collisions, + // and the player can move freely to the proposed position. + + std::vector pushouts; + for (UInt stuck = 0; stuck < STUCK_THRESHOLD; stuck++) { + // Find the pushout vector for each block we're colliding with. + for (auto possible_collision : collision_domain) { + auto pushout = current_box.pushout(to - from, possible_collision); + if (!pushout.is_zero()) pushouts.push_back(pushout); + } + + if (pushouts.empty()) return to; + + std::sort(pushouts.begin(), pushouts.end(), [=](const Vec3& a, const Vec3& b) -> bool { + return a.magnitude_squared() > b.magnitude_squared(); + }); + + to += pushouts[0]; + pushouts.clear(); + } + + // We got stuck, don't move. + return from; } std::vector Player::terrain_collision_domain( Position::World from, Position::World to, World::World& world ) { - auto domain_box = bounding_box_for_position(from).unite(bounding_box_for_position(to)); + // Make the box a bit bigger so we don't clip through blocks. + auto domain_box = bounding_box_for_position(from) + .unite(bounding_box_for_position(to)) + .unite(AABB{{1, 1, 1}}); + std::vector colliding_blocks; // TODO: Unbind from chunks and loop through all the @@ -71,7 +101,7 @@ std::vector Player::terrain_collision_domain( // since right now if you're fast enough you can clip // through whole chunks. auto add_colliding = [&](Position::World chunk_pos, Position::BlockLocal pos, World::Chunk::BlockData& b) { - if (b.type == World::BlockType::Air) return; + if (!b.type.is_solid()) return; auto block_bounds = World::Chunk::block_bounds(pos).offset(chunk_pos); if (domain_box.collides(block_bounds)) colliding_blocks.push_back(block_bounds); }; @@ -82,6 +112,9 @@ std::vector Player::terrain_collision_domain( chunk_from.chunk->for_each([&](auto p, auto b) { add_colliding(position, p, b); }); } + if (World::ChunkIndex::from_position(to) == World::ChunkIndex::from_position(from)) + return colliding_blocks; + auto chunk_to = world.chunks().find(to); if (chunk_to.chunk.has_value()) { auto position = chunk_to.chunk.value().position(); @@ -91,6 +124,14 @@ std::vector Player::terrain_collision_domain( return colliding_blocks; } +void Player::update_camera_position(GFX::Camera& camera) { + auto camera_position = m_transform.position(); + camera_position.y() += 1.5; + + camera.set_position(camera_position); + camera.set_angles(m_transform.rotation()); +} + AABB Player::bounding_box_for_position(Position::World position) { Vec3 box_start = { position.x() - s_bounds.max.x() / 2, @@ -106,10 +147,4 @@ Position::World Player::position_for_bounding_box(AABB box) { return {center.x(), box.min.y(), center.z()}; } -Position::World Player::collision_reposition(Position::World from, Position::World to, AABB colliding) { - if (from == to) return from; - auto resulting_bounding_box = bounding_box_for_position(from).collision_response(to - from, colliding); - return position_for_bounding_box(resulting_bounding_box); // Ugly, we convert to AABB and back. -} - } -- cgit 1.4.1