summaryrefslogtreecommitdiff
path: root/src/Entities
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/Entities
parent7b107078a2a3b9f62a076f91e873f5ad0597d167 (diff)
downloadmeowcraft-7b061aee2b4c15d242bb9f18101d6b9ea776c5cd.tar.zst
meowcraft-7b061aee2b4c15d242bb9f18101d6b9ea776c5cd.zip
Walking movement
Diffstat (limited to 'src/Entities')
-rw-r--r--src/Entities/Player.cpp116
-rw-r--r--src/Entities/Player.hpp21
2 files changed, 116 insertions, 21 deletions
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}};
};