diff options
| author | Mel <einebeere@gmail.com> | 2024-01-25 11:25:29 +0100 |
|---|---|---|
| committer | Mel <einebeere@gmail.com> | 2024-01-25 11:25:29 +0100 |
| commit | 66e436d0f2cf3c33105d8a5bce43bf64d5e72255 (patch) | |
| tree | 3ee36001907453336cf96a57d8ec0154a9ae3135 /src/Entities | |
| parent | efd17623627607a26f33dac8f7ef1a1ddc931907 (diff) | |
| download | meowcraft-66e436d0f2cf3c33105d8a5bce43bf64d5e72255.tar.zst meowcraft-66e436d0f2cf3c33105d8a5bce43bf64d5e72255.zip | |
Mostly functioning world collisions
Diffstat (limited to 'src/Entities')
| -rw-r--r-- | src/Entities/Player.cpp | 71 | ||||
| -rw-r--r-- | src/Entities/Player.hpp | 12 |
2 files changed, 57 insertions, 26 deletions
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<Vec3> 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<AABB> 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<AABB> colliding_blocks; // TODO: Unbind from chunks and loop through all the @@ -71,7 +101,7 @@ std::vector<AABB> 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<AABB> 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<AABB> 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. -} - } diff --git a/src/Entities/Player.hpp b/src/Entities/Player.hpp index 3ae10ff..95e3d64 100644 --- a/src/Entities/Player.hpp +++ b/src/Entities/Player.hpp @@ -1,6 +1,5 @@ #pragma once -#include "../Common/FlexArray.hpp" #include "../Time.hpp" #include "../Transform.hpp" #include "../GFX/Camera.hpp" @@ -11,7 +10,6 @@ #include "../World/Position.hpp" namespace MC::Entities { - class Player { public: explicit Player(Position::World position) : m_transform(position) {} @@ -25,21 +23,19 @@ public: void rotate_to(Rotation to); AABB bounds() const; -private: - void update_camera_position(GFX::Camera& camera); - static constexpr UInt MaxCollidingTerrain = 34; +private: + static Position::World process_collisions(World::World& world, Position::World from, Position::World to); static std::vector<AABB> terrain_collision_domain(Position::World from, Position::World to, World::World& world); + void update_camera_position(GFX::Camera& camera); + // Creates a bounding box where `position` is at the center of the bottom face. static AABB bounding_box_for_position(Position::World position); // Returns position of the center of the bottom face of `box`. static Position::World position_for_bounding_box(AABB box); - static Position::World collision_reposition(Position::World from, Position::World to, AABB colliding); - Transform m_transform; - static inline AABB s_bounds{{0.35, 1.8, 0.35}}; }; |
