From 1e106d75a3cb39c4585ac4624e09edfc694174d2 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Tue, 28 Jul 2026 11:43:10 +0800 Subject: [PATCH] refactor(gameplay): extract physics and collision from ClientPlayer into PhysicalSystem Move per-axis collision detection and move distance calculation to new PhysicalSystem. Move SpeedSystem implementation from inline header to separate .cpp file. Add const accessors to Entity. Replace inline AABB helper with HitboxManager registration of player hitbox using new PLAYER_SIZE constant. Remove obsolete members and functions from ClientPlayer. --- include/Cubed/gameplay/client_player.hpp | 8 - include/Cubed/gameplay/entity.hpp | 9 + include/Cubed/gameplay/player.hpp | 4 +- .../gameplay/systems/physical_system.hpp | 16 ++ .../Cubed/gameplay/systems/speed_system.hpp | 53 +----- src/CMakeLists.txt | 2 + src/gameplay/client_player.cpp | 135 ++------------- src/gameplay/client_world.cpp | 4 +- src/gameplay/entity.cpp | 10 ++ src/gameplay/hitbox_manager.cpp | 10 +- src/gameplay/systems/physical_system.cpp | 154 ++++++++++++++++++ src/gameplay/systems/speed_system.cpp | 56 +++++++ 12 files changed, 276 insertions(+), 185 deletions(-) create mode 100644 include/Cubed/gameplay/systems/physical_system.hpp create mode 100644 src/gameplay/systems/physical_system.cpp create mode 100644 src/gameplay/systems/speed_system.cpp diff --git a/include/Cubed/gameplay/client_player.hpp b/include/Cubed/gameplay/client_player.hpp index d75fd27..5e755b5 100644 --- a/include/Cubed/gameplay/client_player.hpp +++ b/include/Cubed/gameplay/client_player.hpp @@ -1,5 +1,4 @@ #pragma once -#include "Cubed/AABB.hpp" #include "Cubed/constants.hpp" #include "Cubed/gameplay/block.hpp" #include "Cubed/gameplay/chunk_pos.hpp" @@ -39,7 +38,6 @@ public: const ChunkPosSet& get_chunk_pos_set() const; ChunkPosSet get_chunk_pos_set(); - static AABB get_aabb(const glm::vec3& pos); const glm::vec3& get_front() const; const std::optional& get_look_block_pos() const; @@ -102,13 +100,11 @@ private: bool m_sprinting = false; bool m_underwater = false; - glm::vec3 move_distance{0.0f, 0.0f, 0.0f}; // player is tow block tall, the pos is the lower pos ChunkPos m_last_chunk_pos{0, 0}; glm::vec3 m_front{0, 0, -1}; glm::vec3 m_right{0, 0, 0}; - static constexpr glm::vec3 M_SIZE{0.6f, 1.8f, 0.6f}; MouseState m_mouse_state{}; GameMode m_game_mode = CREATIVE; @@ -129,10 +125,6 @@ private: void update_move(float delta_time); - void update_x_move(glm::vec3& player_pos); - void update_y_move(glm::vec3& player_pos); - void update_z_move(glm::vec3& player_pos); - void update_player_chunk(); void play_walk_sound(float dt); diff --git a/include/Cubed/gameplay/entity.hpp b/include/Cubed/gameplay/entity.hpp index 199d318..e600bf2 100644 --- a/include/Cubed/gameplay/entity.hpp +++ b/include/Cubed/gameplay/entity.hpp @@ -92,6 +92,15 @@ public: MoveState& move_state(); Direction& direction(); + const Velocity& velocity() const; + const Position& pos() const; + const WalkPose& walk_pose() const; + const Orientation& angle() const; + const Movement& movement() const; + const Gravity& gravity() const; + const MoveState& move_state() const; + const Direction& direction() const; + protected: Position m_pos; WalkPose m_walk_pose; diff --git a/include/Cubed/gameplay/player.hpp b/include/Cubed/gameplay/player.hpp index 09deb0b..750786d 100644 --- a/include/Cubed/gameplay/player.hpp +++ b/include/Cubed/gameplay/player.hpp @@ -1,4 +1,6 @@ #pragma once +#include "glm/ext/vector_float3.hpp" + #include #include namespace Cubed { @@ -17,5 +19,5 @@ inline Gait get_gait_from_id(int id) { throw std::runtime_error("Unknown Gait"); } } - +static constexpr glm::vec3 PLAYER_SIZE{0.6f, 1.8f, 0.6f}; } // namespace Cubed diff --git a/include/Cubed/gameplay/systems/physical_system.hpp b/include/Cubed/gameplay/systems/physical_system.hpp new file mode 100644 index 0000000..f606621 --- /dev/null +++ b/include/Cubed/gameplay/systems/physical_system.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "Cubed/gameplay/entity.hpp" +#include "Cubed/gameplay/world.hpp" +namespace Cubed { +class PhysicalSystem { +public: + static glm::vec3 get_move_distance(float dt, const Entity& e); + static std::tuple update(float dt, Entity& e, + World& world); + static std::tuple + update(float dt, Entity& e, World& world, glm::vec3& moved_pos); + +private: +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/systems/speed_system.hpp b/include/Cubed/gameplay/systems/speed_system.hpp index 2a736cb..6537db6 100644 --- a/include/Cubed/gameplay/systems/speed_system.hpp +++ b/include/Cubed/gameplay/systems/speed_system.hpp @@ -5,58 +5,7 @@ namespace Cubed { class SpeedSystem { public: - static void update(float dt, Entity& e) { - auto& m_velocity = e.velocity(); - auto& m_move_state = e.move_state(); - auto& m_movement = e.movement(); - auto& direction = e.direction(); - auto& m_gravity = e.gravity(); - // calculate speed - if (m_move_state.forward || m_move_state.back || m_move_state.left || - m_move_state.right) { - direction.value = glm::vec3(0.0f, 0.0f, 0.0f); - m_velocity.value.x += m_movement.acceleration * dt; - m_velocity.value.z += m_movement.acceleration * dt; - if (m_velocity.value.x > m_velocity.max.x) { - m_velocity.value.x = m_velocity.max.x; - } - if (m_velocity.value.z > m_velocity.max.z) { - m_velocity.value.z = m_velocity.max.z; - } - } else { - m_velocity.value.x += -m_movement.deceleration * dt; - m_velocity.value.z += -m_movement.deceleration * dt; - if (m_velocity.value.z < 0.0f) { - m_velocity.value.z = 0.0f; - } - if (m_velocity.value.x < 0.0f) { - m_velocity.value.x = 0.0f; - } - if (m_velocity.value.z < 0.0f && m_velocity.value.x < 0.0f) { - direction.value = glm::vec3(0.0f, 0.0f, 0.0f); - } - } - if (m_move_state.is_fly) { - if (m_move_state.up) { - m_velocity.value.y = m_velocity.max.y; - } - - if (m_move_state.down) { - m_velocity.value.y = -m_velocity.max.y; - } - - if (!m_move_state.down && !m_move_state.up) { - m_velocity.value.y = 0.0f; - } - } else { - if (m_move_state.up && m_move_state.can_up) { - m_velocity.value.y = m_movement.jump_power; - m_move_state.can_up = false; - } - - m_velocity.value.y += -m_gravity.value * dt; - } - } + static void update(float dt, Entity& e); private: }; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c3f538d..e64ed11 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -98,4 +98,6 @@ target_sources(${PROJECT_NAME} gameplay/chunk.cpp gameplay/hitbox_manager.cpp gameplay/entity.cpp + gameplay/systems/physical_system.cpp + gameplay/systems/speed_system.cpp ) \ No newline at end of file diff --git a/src/gameplay/client_player.cpp b/src/gameplay/client_player.cpp index 5cd5a2d..b9fb33b 100644 --- a/src/gameplay/client_player.cpp +++ b/src/gameplay/client_player.cpp @@ -4,6 +4,8 @@ #include "Cubed/config.hpp" #include "Cubed/debug_collector.hpp" #include "Cubed/gameplay/client_world.hpp" +#include "Cubed/gameplay/hitbox_manager.hpp" +#include "Cubed/gameplay/systems/physical_system.hpp" #include "Cubed/gameplay/systems/speed_system.hpp" namespace {} // namespace @@ -11,13 +13,6 @@ namespace Cubed { ClientPlayer::ClientPlayer(ClientWorld& world) : m_world(world) {} ClientPlayer::~ClientPlayer() {} -AABB ClientPlayer::get_aabb(const glm::vec3& pos) { - glm::vec3 half = M_SIZE * 0.5f; - - glm::vec3 center{pos.x, pos.y + half.y, pos.z}; - - return AABB{center, half}; -} const glm::vec3& ClientPlayer::get_front() const { return m_front; } const std::optional& ClientPlayer::get_look_block_pos() const { @@ -318,7 +313,8 @@ void ClientPlayer::place_block(float dt) { glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal; if (!m_world.is_solid(near_pos)) { AABB block_box = ClientWorld::get_block_aabb(near_pos); - AABB player_box = get_aabb(get_player_pos()); + AABB player_box = HitboxManager::aabb("cubed:player"); + player_box.center += get_player_pos(); if (!player_box.intersects(block_box)) { m_world.report_block_change(near_pos, type); } @@ -368,11 +364,6 @@ void ClientPlayer::update_move(float delta_time) { update_direction(); - move_distance = {m_direction.value.x * m_velocity.value.x * delta_time, - 0.0f, - m_direction.value.z * m_velocity.value.z * delta_time}; - move_distance.y = m_velocity.value.y * delta_time; - // ensure the thread safe glm::vec3 player_pos; @@ -381,12 +372,15 @@ void ClientPlayer::update_move(float delta_time) { player_pos = m_pos.value; } - // y - update_y_move(player_pos); - // x - update_x_move(player_pos); - - update_z_move(player_pos); + if (m_game_mode == SPECTATOR) { + player_pos += PhysicalSystem::get_move_distance(delta_time, *this); + } else { + auto [x, y, z] = + PhysicalSystem::update(delta_time, *this, m_world, player_pos); + if (!x || !z) { + m_sprinting = false; + } + } if (player_pos.y < -15.0f) { Logger::warn("y is tow low"); @@ -413,109 +407,6 @@ void ClientPlayer::update_move(float delta_time) { } } -void ClientPlayer::update_x_move(glm::vec3& player_pos) { - player_pos.x += move_distance.x; - if (m_game_mode == SPECTATOR) { - return; - } - AABB player_box = get_aabb(player_pos); - glm::vec3 min = player_box.min(); - glm::vec3 max = player_box.max(); - - int minx = std::floor(min.x); - int maxx = std::floor(max.x); - int miny = std::floor(min.y); - int maxy = std::floor(max.y); - int minz = std::floor(min.z); - int maxz = std::floor(max.z); - - for (int x = minx; x <= maxx; ++x) { - for (int y = miny; y <= maxy; ++y) { - for (int z = minz; z <= maxz; ++z) { - glm::ivec3 block_pos{x, y, z}; - if (!m_world.can_pass_block(block_pos)) { - AABB block_box = ClientWorld::get_block_aabb(block_pos); - if (player_box.intersects(block_box)) { - m_sprinting = false; - player_pos.x -= move_distance.x; - return; - } - } - } - } - } -} - -void ClientPlayer::update_y_move(glm::vec3& player_pos) { - player_pos.y += move_distance.y; - if (m_game_mode == SPECTATOR) { - return; - } - AABB player_box = get_aabb(player_pos); - glm::vec3 min = player_box.min(); - glm::vec3 max = player_box.max(); - - int minx = std::floor(min.x); - int maxx = std::floor(max.x); - int miny = std::floor(min.y); - int maxy = std::floor(max.y); - int minz = std::floor(min.z); - int maxz = std::floor(max.z); - - for (int x = minx; x <= maxx; ++x) { - for (int y = miny; y <= maxy; ++y) { - for (int z = minz; z <= maxz; ++z) { - glm::ivec3 block_pos{x, y, z}; - if (!m_world.can_pass_block(block_pos)) { - AABB block_box = ClientWorld::get_block_aabb(block_pos); - if (player_box.intersects(block_box)) { - player_pos.y -= move_distance.y; - m_velocity.value.y = 0.0f; - if (move_distance.y < 0) { - m_move_state.can_up = true; - m_move_state.is_fly = false; - } - return; - } - } - } - } - } -} - -void ClientPlayer::update_z_move(glm::vec3& player_pos) { - player_pos.z += move_distance.z; - if (m_game_mode == SPECTATOR) { - return; - } - AABB player_box = get_aabb(player_pos); - glm::vec3 min = player_box.min(); - glm::vec3 max = player_box.max(); - - int minx = std::floor(min.x); - int maxx = std::floor(max.x); - int miny = std::floor(min.y); - int maxy = std::floor(max.y); - int minz = std::floor(min.z); - int maxz = std::floor(max.z); - - for (int x = minx; x <= maxx; ++x) { - for (int y = miny; y <= maxy; ++y) { - for (int z = minz; z <= maxz; ++z) { - glm::ivec3 block_pos{x, y, z}; - if (!m_world.can_pass_block(block_pos)) { - AABB block_box = ClientWorld::get_block_aabb(block_pos); - if (player_box.intersects(block_box)) { - m_sprinting = false; - player_pos.z -= move_distance.z; - return; - } - } - } - } - } -} - void ClientPlayer::update_player_chunk() { float x, z; { diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index f8c02cf..58ca240 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -4,6 +4,7 @@ #include "Cubed/gameplay/chunk_generator.hpp" #include "Cubed/gameplay/entity.hpp" #include "Cubed/gameplay/game_time.hpp" +#include "Cubed/gameplay/hitbox_manager.hpp" #include "Cubed/gameplay/packet.hpp" #include "Cubed/scene/world_scene.hpp" #include "Cubed/tools/math_tools.hpp" @@ -299,7 +300,8 @@ void ClientWorld::report_block_change(const glm::ivec3& pos, for (const auto& player : m_players_data) { - AABB box = ClientPlayer::get_aabb(player.pos.value); + AABB box = HitboxManager::aabb("cubed:player"); + box.center += player.pos.value; if (box.intersects(block_box)) { return; } diff --git a/src/gameplay/entity.cpp b/src/gameplay/entity.cpp index 1b9b550..7ea7030 100644 --- a/src/gameplay/entity.cpp +++ b/src/gameplay/entity.cpp @@ -20,4 +20,14 @@ Movement& Entity::movement() { return m_movement; } Gravity& Entity::gravity() { return m_gravity; } MoveState& Entity::move_state() { return m_move_state; } Direction& Entity::direction() { return m_direction; } + +const Velocity& Entity::velocity() const { return m_velocity; } +const Position& Entity::pos() const { return m_pos; } +const WalkPose& Entity::walk_pose() const { return m_walk_pose; } +const Orientation& Entity::angle() const { return m_angle; } +const Movement& Entity::movement() const { return m_movement; } +const Gravity& Entity::gravity() const { return m_gravity; } +const MoveState& Entity::move_state() const { return m_move_state; } +const Direction& Entity::direction() const { return m_direction; } + } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/hitbox_manager.cpp b/src/gameplay/hitbox_manager.cpp index 49c7b76..a340709 100644 --- a/src/gameplay/hitbox_manager.cpp +++ b/src/gameplay/hitbox_manager.cpp @@ -1,12 +1,20 @@ #include "Cubed/gameplay/hitbox_manager.hpp" +#include "Cubed/gameplay/player.hpp" #include "Cubed/tools/log.hpp" #include namespace fs = std::filesystem; using nlohmann::json; namespace Cubed { -HitboxManager::HitboxManager() {} +HitboxManager::HitboxManager() { + + glm::vec3 half = PLAYER_SIZE * 0.5f; + + glm::vec3 center{0.0f, half.y, 0.0f}; + m_hitboxes.emplace("cubed:player", AABB{center, half}); +} + HitboxManager::~HitboxManager() {} HitboxManager& HitboxManager::instance() { diff --git a/src/gameplay/systems/physical_system.cpp b/src/gameplay/systems/physical_system.cpp new file mode 100644 index 0000000..ed126d4 --- /dev/null +++ b/src/gameplay/systems/physical_system.cpp @@ -0,0 +1,154 @@ +#include "Cubed/gameplay/systems/physical_system.hpp" + +#include "Cubed/gameplay/hitbox_manager.hpp" +namespace Cubed { + +namespace { + +bool update_x(const glm::vec3& pos, const glm::vec3& distance, World& world, + const AABB& box) { + glm::vec3 p = pos; + p.x += distance.x; + AABB b = box; + b.center += p; + glm::vec3 min = b.min(); + glm::vec3 max = b.max(); + int minx = std::floor(min.x); + int maxx = std::floor(max.x); + int miny = std::floor(min.y); + int maxy = std::floor(max.y); + int minz = std::floor(min.z); + int maxz = std::floor(max.z); + + for (int x = minx; x <= maxx; ++x) { + for (int y = miny; y <= maxy; ++y) { + for (int z = minz; z <= maxz; ++z) { + glm::ivec3 block_pos{x, y, z}; + if (!world.can_pass_block(block_pos)) { + AABB block_box = World::get_block_aabb(block_pos); + if (b.intersects(block_box)) { + return false; + } + } + } + } + } + return true; +} + +bool update_y(const glm::vec3& pos, const glm::vec3& distance, World& world, + const AABB& box) { + glm::vec3 p = pos; + p.y += distance.y; + AABB b = box; + b.center += p; + glm::vec3 min = b.min(); + glm::vec3 max = b.max(); + int minx = std::floor(min.x); + int maxx = std::floor(max.x); + int miny = std::floor(min.y); + int maxy = std::floor(max.y); + int minz = std::floor(min.z); + int maxz = std::floor(max.z); + + for (int x = minx; x <= maxx; ++x) { + for (int y = miny; y <= maxy; ++y) { + for (int z = minz; z <= maxz; ++z) { + glm::ivec3 block_pos{x, y, z}; + if (!world.can_pass_block(block_pos)) { + AABB block_box = World::get_block_aabb(block_pos); + if (b.intersects(block_box)) { + return false; + } + } + } + } + } + return true; +} + +bool update_z(const glm::vec3& pos, const glm::vec3& distance, World& world, + const AABB& box) { + glm::vec3 p = pos; + p.z += distance.z; + AABB b = box; + b.center += p; + glm::vec3 min = b.min(); + glm::vec3 max = b.max(); + int minx = std::floor(min.x); + int maxx = std::floor(max.x); + int miny = std::floor(min.y); + int maxy = std::floor(max.y); + int minz = std::floor(min.z); + int maxz = std::floor(max.z); + + for (int x = minx; x <= maxx; ++x) { + for (int y = miny; y <= maxy; ++y) { + for (int z = minz; z <= maxz; ++z) { + glm::ivec3 block_pos{x, y, z}; + if (!world.can_pass_block(block_pos)) { + AABB block_box = World::get_block_aabb(block_pos); + if (b.intersects(block_box)) { + return false; + } + } + } + } + } + return true; +} + +} // namespace +std::tuple PhysicalSystem::update(float dt, Entity& e, + World& world) { + glm::vec3 pos = e.pos().value; + auto ans = update(dt, e, world, pos); + e.pos().value = pos; + return ans; +} + +std::tuple PhysicalSystem::update(float dt, Entity& e, + World& world, + glm::vec3& moved_pos) { + auto distance = get_move_distance(dt, e); + auto& m_velocity = e.velocity(); + auto& m_move_state = e.move_state(); + AABB box = HitboxManager::aabb("cubed:player"); + bool x = false; + bool y = false; + bool z = false; + if (update_x(moved_pos, distance, world, box)) { + moved_pos.x += distance.x; + x = true; + } else { + m_velocity.value.x = 0.0f; + } + + if (update_y(moved_pos, distance, world, box)) { + moved_pos.y += distance.y; + y = true; + } else { + m_velocity.value.y = 0.0f; + if (distance.y < 0) { + m_move_state.can_up = true; + m_move_state.is_fly = false; + } + } + + if (update_z(moved_pos, distance, world, box)) { + moved_pos.z += distance.z; + z = true; + } else { + m_velocity.value.z = 0.0f; + } + return {x, y, z}; +} + +glm::vec3 PhysicalSystem::get_move_distance(float dt, const Entity& e) { + auto& m_direction = e.direction(); + auto& m_velocity = e.velocity(); + return glm::vec3{m_direction.value.x * m_velocity.value.x * dt, + m_velocity.value.y * dt, + m_direction.value.z * m_velocity.value.z * dt}; +} +} // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/systems/speed_system.cpp b/src/gameplay/systems/speed_system.cpp new file mode 100644 index 0000000..03e09c9 --- /dev/null +++ b/src/gameplay/systems/speed_system.cpp @@ -0,0 +1,56 @@ +#include "Cubed/gameplay/systems/speed_system.hpp" + +namespace Cubed { +void SpeedSystem::update(float dt, Entity& e) { + auto& m_velocity = e.velocity(); + auto& m_move_state = e.move_state(); + auto& m_movement = e.movement(); + auto& direction = e.direction(); + auto& m_gravity = e.gravity(); + // calculate speed + if (m_move_state.forward || m_move_state.back || m_move_state.left || + m_move_state.right) { + direction.value = glm::vec3(0.0f, 0.0f, 0.0f); + m_velocity.value.x += m_movement.acceleration * dt; + m_velocity.value.z += m_movement.acceleration * dt; + if (m_velocity.value.x > m_velocity.max.x) { + m_velocity.value.x = m_velocity.max.x; + } + if (m_velocity.value.z > m_velocity.max.z) { + m_velocity.value.z = m_velocity.max.z; + } + } else { + m_velocity.value.x += -m_movement.deceleration * dt; + m_velocity.value.z += -m_movement.deceleration * dt; + if (m_velocity.value.z < 0.0f) { + m_velocity.value.z = 0.0f; + } + if (m_velocity.value.x < 0.0f) { + m_velocity.value.x = 0.0f; + } + if (m_velocity.value.z < 0.0f && m_velocity.value.x < 0.0f) { + direction.value = glm::vec3(0.0f, 0.0f, 0.0f); + } + } + if (m_move_state.is_fly) { + if (m_move_state.up) { + m_velocity.value.y = m_velocity.max.y; + } + + if (m_move_state.down) { + m_velocity.value.y = -m_velocity.max.y; + } + + if (!m_move_state.down && !m_move_state.up) { + m_velocity.value.y = 0.0f; + } + } else { + if (m_move_state.up && m_move_state.can_up) { + m_velocity.value.y = m_movement.jump_power; + m_move_state.can_up = false; + } + + m_velocity.value.y += -m_gravity.value * dt; + } +} +} // namespace Cubed \ No newline at end of file