summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2024-02-01 23:56:57 +0100
committerMel <einebeere@gmail.com>2024-02-01 23:56:57 +0100
commit7b061aee2b4c15d242bb9f18101d6b9ea776c5cd (patch)
tree66ef0166efb36146a58075e2e21e8c1c36fbd927 /src
parent7b107078a2a3b9f62a076f91e873f5ad0597d167 (diff)
downloadmeowcraft-7b061aee2b4c15d242bb9f18101d6b9ea776c5cd.tar.zst
meowcraft-7b061aee2b4c15d242bb9f18101d6b9ea776c5cd.zip
Walking movement
Diffstat (limited to 'src')
-rw-r--r--src/Common/Casts.hpp3
-rw-r--r--src/Entities/Player.cpp116
-rw-r--r--src/Entities/Player.hpp21
-rw-r--r--src/Math/AABB.cpp1
-rw-r--r--src/Math/AABB.hpp2
5 files changed, 122 insertions, 21 deletions
diff --git a/src/Common/Casts.hpp b/src/Common/Casts.hpp
new file mode 100644
index 0000000..c2c7088
--- /dev/null
+++ b/src/Common/Casts.hpp
@@ -0,0 +1,3 @@
+#pragma once
+
+#define TO(type, value) (static_cast<type>(value))
\ No newline at end of file
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index f87f2f9..66f08c6 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -1,30 +1,26 @@
 #include "Player.hpp"
 
+#include "../Common/Casts.hpp"
 #include <unordered_set>
 
 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); };
+    auto const input_direction = directional_input(window);
 
-    Real x = key(GLFW_KEY_D) - key(GLFW_KEY_A);
-    Real y = key(GLFW_KEY_SPACE) - key(GLFW_KEY_LEFT_SHIFT);
-    Real z = key(GLFW_KEY_S) - key(GLFW_KEY_W);
-    Real boost = key(GLFW_KEY_LEFT_CONTROL) * 75.0f;
+    auto destination = movement(window, time, input_direction);
+    auto const [position, blocked_axes] = process_collisions(world, m_transform.position(), destination);
 
-    auto move_speed = (10.0f + boost) * time.delta();
-    auto rotation_speed = 0.1f;
+    destination = position;
+    m_on_ground = blocked_axes.y().negative;
 
-    auto direction = m_transform.right() * x + Vec3(0, y, 0) + m_transform.forward() * z;
-    auto destination = origin + direction * move_speed;
-
-    destination = process_collisions(world, origin, destination);
+    for (UInt axis = 0; axis < 3; axis++) {
+        if (blocked_axes[axis].positive) m_velocity[axis] = std::max(m_velocity[axis], 0.0);
+        if (blocked_axes[axis].negative) m_velocity[axis] = std::min(m_velocity[axis], 0.0);
+    }
 
     move_to(destination);
-    rotate({r.y() * rotation_speed, r.x() * rotation_speed, 0.0f});
+    rotate(rotational_input(window));
 
     update_camera_position(camera);
 }
@@ -50,10 +46,57 @@ AABB Player::bounds() const {
     return bounding_box_for_position(m_transform.position());
 }
 
+Position::World Player::movement(GFX::Window& window, const Time& time, Vec3 input_direction) {
+    m_velocity = m_flying
+        ? flying_velocity(window, time, input_direction)
+        : walking_velocity(window, time, input_direction);
+
+    return m_transform.position() + m_velocity;
+}
+
+Vec3 Player::walking_velocity(GFX::Window& window, const Time& time, Vec3 input_direction) {
+    constexpr auto base_move_speed = 8.0;
+    constexpr auto gravity = 0.6;
+
+    auto walking_direction = m_transform.right() * input_direction.x() + m_transform.forward() * input_direction.z();
+    walking_direction.y() = 0;
+    if (!walking_direction.is_zero()) walking_direction = walking_direction.normalize();
+
+    auto const walking_velocity = walking_direction * base_move_speed * time.delta();
+
+    auto vertical_velocity = 0.0;
+    if (m_on_ground) {
+        if (window.key(GLFW_KEY_SPACE, GLFW_PRESS)) {
+            vertical_velocity = 0.16;
+            m_on_ground = false;
+        }
+    } else {
+        // TODO: This integration depends on frame delta.
+        vertical_velocity = m_velocity.y() - gravity * time.delta();
+    }
+
+    return {
+        walking_velocity.x(),
+        vertical_velocity,
+        walking_velocity.z(),
+    };
+}
+
+Vec3 Player::flying_velocity(GFX::Window& window, const Time& time, Vec3 input_direction) {
+    constexpr auto base_move_speed = 10.0;
+
+    Real const boost = TO(Real, window.key(GLFW_KEY_LEFT_CONTROL, GLFW_PRESS)) * 75.0;
+
+    auto const [x, y, z] = input_direction.elements;
+    auto const direction = m_transform.right() * x + Vec3(0, y, 0) + m_transform.forward() * z;
+
+    return direction * (base_move_speed + boost) * time.delta();
+}
+
 #define STUCK_THRESHOLD 100
 
-Position::World Player::process_collisions(World::World& world, Position::World from, Position::World to) {
-    if (from.mostly_equal(to)) return to;
+Player::ProcessCollisionsResult Player::process_collisions(World::World& world, Position::World from, Position::World to) {
+    if (from.mostly_equal(to)) return {to, {}};
 
     auto current_box = bounding_box_for_position(from);
 
@@ -70,6 +113,8 @@ Position::World Player::process_collisions(World::World& world, Position::World
     // If there are no more responses, we have no collisions,
     // and the player can move freely to the proposed position.
 
+    Vector<3, BlockedAxis> blocked_axes;
+
     struct Response {
         AABB::CollisionResponse response;
         Real distance_from_entity_squared;
@@ -91,7 +136,7 @@ Position::World Player::process_collisions(World::World& world, Position::World
             }
         }
 
-        if (responses.empty()) return to;
+        if (responses.empty()) return {to, blocked_axes};
 
         std::sort(responses.begin(), responses.end(), [=](const Response& a, const Response& b) -> bool {
             return std::tie(a.magnitude_squared, a.distance_from_entity_squared) < std::tie(b.magnitude_squared, b.distance_from_entity_squared);
@@ -100,12 +145,26 @@ Position::World Player::process_collisions(World::World& world, Position::World
         // TODO: This applies the entire response, even though ideally we'd apply it in two parts,
         // since technically the total velocity is a diagonal of the two components.
         // This should only be a marginal issue, though.
-        to = from + responses[0].response.v_to_collision + responses[0].response.v_slide;
+        auto response = responses[0].response;
+        to = from + response.v_to_collision + response.v_slide;
+
+
+        auto check_axis = [&](Real axis_normal, BlockedAxis& axis) -> Bool {
+            if (axis_normal < -0.5) { axis.negative = true; return true; }
+            if (axis_normal > 0.5) { axis.positive = true; return true; }
+            return false;
+        };
+
+        check_axis(response.normal.x(), blocked_axes.x()) ||
+            check_axis(response.normal.y(), blocked_axes.y()) ||
+            check_axis(response.normal.z(), blocked_axes.z());
+
         responses.clear();
     }
 
     // We got stuck, don't move.
-    return from;
+    // Also, if we're stuck, we're also presumably touching the ground on any axis.
+    return {from, {BlockedAxis{true, true}, BlockedAxis{true, true}, BlockedAxis{true, true}}};
 }
 
 std::vector<AABB> Player::terrain_collision_domain(
@@ -152,6 +211,23 @@ void Player::update_camera_position(GFX::Camera& camera) {
     camera.set_angles(m_transform.rotation());
 }
 
+Vec3 Player::directional_input(GFX::Window& window) {
+    auto key = [&](Int k) -> Real { return window.key(k, GLFW_PRESS); };
+
+    Real x = key(GLFW_KEY_D) - key(GLFW_KEY_A);
+    Real y = key(GLFW_KEY_SPACE) - key(GLFW_KEY_LEFT_SHIFT);
+    Real z = key(GLFW_KEY_S) - key(GLFW_KEY_W);
+
+    return {x, y, z};
+}
+
+Rotation Player::rotational_input(GFX::Window& window) {
+    constexpr auto base_rotation_speed = 0.1f;
+
+    auto r = window.mouse_delta();
+    return {r.y() * base_rotation_speed, r.x() * base_rotation_speed, 0.0f};
+}
+
 AABB Player::bounding_box_for_position(Position::World position) {
     Vec3 box_start = {
         position.x() - s_bounds.max.x() / 2,
diff --git a/src/Entities/Player.hpp b/src/Entities/Player.hpp
index 95e3d64..5d70559 100644
--- a/src/Entities/Player.hpp
+++ b/src/Entities/Player.hpp
@@ -25,16 +25,35 @@ public:
     AABB bounds() const;
 
 private:
-    static Position::World process_collisions(World::World& world, Position::World from, Position::World to);
+    struct BlockedAxis {
+        Bool positive, negative;
+    };
+
+    struct ProcessCollisionsResult {
+        Position::World position;
+        Vector<3, BlockedAxis> blocked_axes;
+    };
+    static ProcessCollisionsResult 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);
 
+    Position::World movement(GFX::Window& window, const Time& time, Vec3 input_direction);
+    Vec3 walking_velocity(GFX::Window& window, const Time& time, Vec3 input_direction);
+    Vec3 flying_velocity(GFX::Window& window, const Time& time, Vec3 input_direction);
+
     void update_camera_position(GFX::Camera& camera);
 
+    static Vec3 directional_input(GFX::Window& window);
+    static Rotation rotational_input(GFX::Window& window);
+
     // 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);
 
+    Bool m_on_ground = false;
+    Bool m_flying = false;
+
+    Vec3 m_velocity{};
     Transform m_transform;
     static inline AABB s_bounds{{0.35, 1.8, 0.35}};
 };
diff --git a/src/Math/AABB.cpp b/src/Math/AABB.cpp
index b16960e..7f2c05d 100644
--- a/src/Math/AABB.cpp
+++ b/src/Math/AABB.cpp
@@ -29,5 +29,6 @@ AABB::CollisionResponse AABB::collision_response(Vector<3> v, AABB against) cons
     return {
         .v_to_collision = raycast.point - origin,
         .v_slide = projected_velocity,
+        .normal = raycast.normal,
     };
 }
diff --git a/src/Math/AABB.hpp b/src/Math/AABB.hpp
index b50ba56..b298126 100644
--- a/src/Math/AABB.hpp
+++ b/src/Math/AABB.hpp
@@ -83,6 +83,8 @@ struct AABB {
         // Velocity sliding along the collision plane.
         // Projection of the fraction of the original velocity that was blocked by collision, onto the collision plane.
         Vec3 v_slide;
+        // Normal of the collision plane.
+        Vec3 normal;
     };
     CollisionResponse collision_response(Vector<3> v, AABB against) const;