summaryrefslogtreecommitdiff
path: root/src/Entities
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2024-01-25 11:25:29 +0100
committerMel <einebeere@gmail.com>2024-01-25 11:25:29 +0100
commit66e436d0f2cf3c33105d8a5bce43bf64d5e72255 (patch)
tree3ee36001907453336cf96a57d8ec0154a9ae3135 /src/Entities
parentefd17623627607a26f33dac8f7ef1a1ddc931907 (diff)
downloadmeowcraft-66e436d0f2cf3c33105d8a5bce43bf64d5e72255.tar.zst
meowcraft-66e436d0f2cf3c33105d8a5bce43bf64d5e72255.zip
Mostly functioning world collisions
Diffstat (limited to 'src/Entities')
-rw-r--r--src/Entities/Player.cpp71
-rw-r--r--src/Entities/Player.hpp12
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}};
};