From c5ce91e653c78d3c4f5e14f8203a04875a635bc6 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Fri, 31 Jul 2026 20:39:40 +0800 Subject: [PATCH] refactor(ecs): move entity physics to tick-based systems Update PhysicalSystem and SpeedSystem to operate on entt::registry instead of individual components. Add TickVelocity for server creatures so movement is calculated per tick without frame delta time. LocalPlayer now implements its own client-side physics with collision detection. --- include/Cubed/gameplay/ecs/movement.hpp | 10 +- include/Cubed/gameplay/ecs/server_entity.hpp | 2 +- include/Cubed/gameplay/local_player.hpp | 8 +- .../gameplay/systems/physical_system.hpp | 14 +- .../Cubed/gameplay/systems/speed_system.hpp | 9 +- src/gameplay/local_player.cpp | 214 ++++++++++++++++-- src/gameplay/systems/physical_system.cpp | 80 ++++--- src/gameplay/systems/speed_system.cpp | 60 ++--- 8 files changed, 276 insertions(+), 121 deletions(-) diff --git a/include/Cubed/gameplay/ecs/movement.hpp b/include/Cubed/gameplay/ecs/movement.hpp index 1307d73..52d605c 100644 --- a/include/Cubed/gameplay/ecs/movement.hpp +++ b/include/Cubed/gameplay/ecs/movement.hpp @@ -3,10 +3,17 @@ #include namespace Cubed { +struct TickVelocity { + + glm::vec3 value{0.0f}; + // blocks/tick!!! -1 for in + glm::vec3 max{1.0f, -1.0f, 1.0f}; +}; + struct Velocity { glm::vec3 value{0.0f}; - + // blocks/second!!! glm::vec3 max{4.5f, 7.5f, 7.5f}; }; @@ -23,4 +30,5 @@ struct Gravity { float value = DEFAULT_G; }; + } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/ecs/server_entity.hpp b/include/Cubed/gameplay/ecs/server_entity.hpp index eb6f7e8..23f064d 100644 --- a/include/Cubed/gameplay/ecs/server_entity.hpp +++ b/include/Cubed/gameplay/ecs/server_entity.hpp @@ -8,7 +8,7 @@ namespace Cubed { struct BaseServerCreature { Transform transform{}; - Velocity velocity{}; + TickVelocity velocity{}; Movement movement{}; diff --git a/include/Cubed/gameplay/local_player.hpp b/include/Cubed/gameplay/local_player.hpp index 4f3c535..fa76c31 100644 --- a/include/Cubed/gameplay/local_player.hpp +++ b/include/Cubed/gameplay/local_player.hpp @@ -149,12 +149,14 @@ private: void update_direction(); void update_lookup_block(); - - void update_move(float delta_time); - + void update_move(float dt); void update_player_chunk(); void play_walk_sound(float dt); Gait compute_gait() const; + + void update_speed(float dt); + std::tuple update_physical(float dt); + glm::vec3 get_move_distance(float dt); }; } // namespace Cubed diff --git a/include/Cubed/gameplay/systems/physical_system.hpp b/include/Cubed/gameplay/systems/physical_system.hpp index 0ba1442..7c121c7 100644 --- a/include/Cubed/gameplay/systems/physical_system.hpp +++ b/include/Cubed/gameplay/systems/physical_system.hpp @@ -1,18 +1,16 @@ #pragma once #include "Cubed/gameplay/ecs/movement.hpp" -#include "Cubed/gameplay/ecs/state.hpp" #include "Cubed/gameplay/ecs/transform.hpp" -#include "Cubed/gameplay/world.hpp" -namespace Cubed { +#include +namespace Cubed { +class ServerWorld; class PhysicalSystem { public: - static glm::vec3 get_move_distance(float dt, const Direction& d, - const Velocity& v); + static glm::vec3 get_move_distance(const Direction& d, + const TickVelocity& v); - static std::tuple - update(float dt, World& world, glm::vec3& moved_pos, Velocity& v, - Direction& direction, MoveState& move_state, HitboxID hitbox); + static void update(ServerWorld& world, entt::registry& registry); private: }; diff --git a/include/Cubed/gameplay/systems/speed_system.hpp b/include/Cubed/gameplay/systems/speed_system.hpp index e6a141f..6ec5e02 100644 --- a/include/Cubed/gameplay/systems/speed_system.hpp +++ b/include/Cubed/gameplay/systems/speed_system.hpp @@ -1,15 +1,10 @@ #pragma once -#include "Cubed/gameplay/ecs/movement.hpp" -#include "Cubed/gameplay/ecs/state.hpp" -#include "Cubed/gameplay/ecs/transform.hpp" - +#include namespace Cubed { class SpeedSystem { public: - static void update(float dt, Velocity& v, MoveState& move_state, - Movement& movement, Direction& direction, - const Gravity& g); + static void update(float dt, entt::registry& registry); private: }; diff --git a/src/gameplay/local_player.cpp b/src/gameplay/local_player.cpp index 3d70e0b..7208094 100644 --- a/src/gameplay/local_player.cpp +++ b/src/gameplay/local_player.cpp @@ -5,12 +5,110 @@ #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 namespace Cubed { -LocalPlayer::LocalPlayer(ClientWorld& world) : m_world(world) {} + +namespace { + +bool update_x(const glm::vec3& pos, const glm::vec3& distance, + ClientWorld& world, const Hitbox& box) { + glm::vec3 p = pos; + p.x += distance.x; + Hitbox 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)) { + Hitbox 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, + ClientWorld& world, const Hitbox& box) { + glm::vec3 p = pos; + p.y += distance.y; + Hitbox 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)) { + Hitbox 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, + ClientWorld& world, const Hitbox& box) { + glm::vec3 p = pos; + p.z += distance.z; + Hitbox 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)) { + Hitbox block_box = World::get_block_aabb(block_pos); + if (b.intersects(block_box)) { + return false; + } + } + } + } + } + return true; +} + +} // namespace + +LocalPlayer::LocalPlayer(ClientWorld& world) : m_world(world) { + + m_hitbox = HitboxManager::instance().get_hitbox_id("cubed:player"); +} LocalPlayer::~LocalPlayer() {} const glm::vec3& LocalPlayer::get_front() const { return m_front; } @@ -333,9 +431,9 @@ LocalPlayer::get_hotbar() const { return m_hotbar; } -void LocalPlayer::update_move(float delta_time) { +void LocalPlayer::update_move(float dt) { // if frame rate less than 1 frame per second, don't update - if (delta_time > 1.0f) { + if (dt > 1.0f) { return; } @@ -343,7 +441,7 @@ void LocalPlayer::update_move(float delta_time) { m_sprinting = false; } if (space_on) { - space_on_time += delta_time; + space_on_time += dt; if (space_on_time >= MAX_SPACE_ON_TIME) { space_on = false; space_on_time = 0.0f; @@ -360,8 +458,7 @@ void LocalPlayer::update_move(float delta_time) { glm::vec3{m_max_run_speed, m_max_y_speed, m_max_run_speed}; } - SpeedSystem::update(delta_time, m_velocity, m_move_state, m_movement, - m_direction, m_gravity); + update_speed(dt); update_direction(); @@ -374,12 +471,9 @@ void LocalPlayer::update_move(float delta_time) { } if (m_game_mode == SPECTATOR) { - player_pos += PhysicalSystem::get_move_distance(delta_time, m_direction, - m_velocity); + player_pos += get_move_distance(dt); } else { - auto [x, y, z] = - PhysicalSystem::update(delta_time, m_world, player_pos, m_velocity, - m_direction, m_move_state, m_hitbox); + auto [x, y, z] = update_physical(dt); if (!x || !z) { m_sprinting = false; } @@ -406,7 +500,7 @@ void LocalPlayer::update_move(float delta_time) { } for (auto& [key, timer] : m_timers) { - timer.update(delta_time); + timer.update(dt); } } @@ -593,6 +687,96 @@ void LocalPlayer::init(std::string_view name) { } } +void LocalPlayer::update_speed(float dt) { + // calculate speed + auto& v = m_velocity; + if (m_move_state.forward || m_move_state.back || m_move_state.left || + m_move_state.right) { + m_direction.value = glm::vec3(0.0f, 0.0f, 0.0f); + v.value.x += m_movement.acceleration * dt; + v.value.z += m_movement.acceleration * dt; + if (v.value.x > v.max.x) { + v.value.x = v.max.x; + } + if (v.value.z > v.max.z) { + v.value.z = v.max.z; + } + } else { + v.value.x += -m_movement.deceleration * dt; + v.value.z += -m_movement.deceleration * dt; + if (v.value.z < 0.0f) { + v.value.z = 0.0f; + } + if (v.value.x < 0.0f) { + v.value.x = 0.0f; + } + if (v.value.z < 0.0f && v.value.x < 0.0f) { + m_direction.value = glm::vec3(0.0f, 0.0f, 0.0f); + } + } + if (m_move_state.is_fly) { + if (m_move_state.up) { + v.value.y = v.max.y; + } + + if (m_move_state.down) { + v.value.y = -v.max.y; + } + + if (!m_move_state.down && !m_move_state.up) { + v.value.y = 0.0f; + } + } else { + if (m_move_state.up && m_move_state.can_up) { + v.value.y = m_movement.jump_power; + m_move_state.can_up = false; + } + + v.value.y += -m_gravity.value * dt; + } +} + +std::tuple LocalPlayer::update_physical(float dt) { + + auto distance = get_move_distance(dt); + auto box = HitboxManager::hitbox(m_hitbox); + bool x = false; + bool y = false; + bool z = false; + if (update_x(m_pos.value, distance, m_world, box.box)) { + m_pos.value.x += distance.x; + x = true; + } else { + m_velocity.value.x = 0.0f; + } + + if (update_y(m_pos.value, distance, m_world, box.box)) { + m_pos.value.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(m_pos.value, distance, m_world, box.box)) { + m_pos.value.z += distance.z; + z = true; + } else { + m_velocity.value.z = 0.0f; + } + return {x, y, z}; +} + +glm::vec3 LocalPlayer::get_move_distance(float dt) { + const auto& d = m_direction; + const auto& v = m_velocity; + return glm::vec3{d.value.x * v.value.x * dt, v.value.y * dt, + d.value.z * v.value.z * dt}; +} + bool LocalPlayer::is_underwater() const { return m_underwater; } void LocalPlayer::set_underwater(bool u) { m_underwater = u; } diff --git a/src/gameplay/systems/physical_system.cpp b/src/gameplay/systems/physical_system.cpp index 8e41cd7..8d35e02 100644 --- a/src/gameplay/systems/physical_system.cpp +++ b/src/gameplay/systems/physical_system.cpp @@ -1,12 +1,14 @@ #include "Cubed/gameplay/systems/physical_system.hpp" +#include "Cubed/gameplay/ecs/server_entity.hpp" #include "Cubed/gameplay/hitbox_manager.hpp" +#include "Cubed/gameplay/server_world.hpp" namespace Cubed { namespace { -bool update_x(const glm::vec3& pos, const glm::vec3& distance, World& world, - const Hitbox& box) { +bool update_x(const glm::vec3& pos, const glm::vec3& distance, + ServerWorld& world, const Hitbox& box) { glm::vec3 p = pos; p.x += distance.x; Hitbox b = box; @@ -36,8 +38,8 @@ bool update_x(const glm::vec3& pos, const glm::vec3& distance, World& world, return true; } -bool update_y(const glm::vec3& pos, const glm::vec3& distance, World& world, - const Hitbox& box) { +bool update_y(const glm::vec3& pos, const glm::vec3& distance, + ServerWorld& world, const Hitbox& box) { glm::vec3 p = pos; p.y += distance.y; Hitbox b = box; @@ -67,8 +69,8 @@ bool update_y(const glm::vec3& pos, const glm::vec3& distance, World& world, return true; } -bool update_z(const glm::vec3& pos, const glm::vec3& distance, World& world, - const Hitbox& box) { +bool update_z(const glm::vec3& pos, const glm::vec3& distance, + ServerWorld& world, const Hitbox& box) { glm::vec3 p = pos; p.z += distance.z; Hitbox b = box; @@ -100,45 +102,41 @@ bool update_z(const glm::vec3& pos, const glm::vec3& distance, World& world, } // namespace -std::tuple -PhysicalSystem::update(float dt, World& world, glm::vec3& moved_pos, - Velocity& v, Direction& direction, MoveState& move_state, - HitboxID hitbox) { - auto distance = get_move_distance(dt, direction, v); - auto box = HitboxManager::hitbox(hitbox); - bool x = false; - bool y = false; - bool z = false; - if (update_x(moved_pos, distance, world, box.box)) { - moved_pos.x += distance.x; - x = true; - } else { - v.value.x = 0.0f; - } +void PhysicalSystem::update(ServerWorld& world, entt::registry& registry) { - if (update_y(moved_pos, distance, world, box.box)) { - moved_pos.y += distance.y; - y = true; - } else { - v.value.y = 0.0f; - if (distance.y < 0) { - move_state.can_up = true; - move_state.is_fly = false; + auto view = registry.view(); + for (auto e : view) { + auto& creature = view.get(e); + auto distance = + get_move_distance(creature.direction, creature.velocity); + auto box = HitboxManager::hitbox(creature.hitbox); + auto& pos = creature.transform.position.value; + auto& v = creature.velocity; + if (update_x(pos, distance, world, box.box)) { + pos.x += distance.x; + + } else { + v.value.x = 0.0f; + } + + if (update_y(pos, distance, world, box.box)) { + pos.y += distance.y; + + } else { + v.value.y = 0.0f; + } + + if (update_z(pos, distance, world, box.box)) { + pos.z += distance.z; + + } else { + v.value.z = 0.0f; } } - - if (update_z(moved_pos, distance, world, box.box)) { - moved_pos.z += distance.z; - z = true; - } else { - v.value.z = 0.0f; - } - return {x, y, z}; } -glm::vec3 PhysicalSystem::get_move_distance(float dt, const Direction& d, - const Velocity& v) { - return glm::vec3{d.value.x * v.value.x * dt, v.value.y * dt, - d.value.z * v.value.z * dt}; +glm::vec3 PhysicalSystem::get_move_distance(const Direction& d, + const TickVelocity& v) { + return glm::vec3{d.value.x * v.value.x, v.value.y, d.value.z * v.value.z}; } } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/systems/speed_system.cpp b/src/gameplay/systems/speed_system.cpp index 5d4763b..5f94e7f 100644 --- a/src/gameplay/systems/speed_system.cpp +++ b/src/gameplay/systems/speed_system.cpp @@ -1,54 +1,24 @@ #include "Cubed/gameplay/systems/speed_system.hpp" +#include "Cubed/gameplay/ecs/ai_struct.hpp" +#include "Cubed/gameplay/ecs/server_entity.hpp" + namespace Cubed { -void SpeedSystem::update(float dt, Velocity& v, MoveState& move_state, - Movement& movement, Direction& direction, - const Gravity& g) { - // calculate speed - if (move_state.forward || move_state.back || move_state.left || - move_state.right) { - direction.value = glm::vec3(0.0f, 0.0f, 0.0f); - v.value.x += movement.acceleration * dt; - v.value.z += movement.acceleration * dt; - if (v.value.x > v.max.x) { - v.value.x = v.max.x; - } - if (v.value.z > v.max.z) { - v.value.z = v.max.z; - } - } else { - v.value.x += -movement.deceleration * dt; - v.value.z += -movement.deceleration * dt; - if (v.value.z < 0.0f) { - v.value.z = 0.0f; - } - if (v.value.x < 0.0f) { - v.value.x = 0.0f; - } - if (v.value.z < 0.0f && v.value.x < 0.0f) { - direction.value = glm::vec3(0.0f, 0.0f, 0.0f); - } - } - if (move_state.is_fly) { - if (move_state.up) { - v.value.y = v.max.y; - } +void SpeedSystem::update(float dt, entt::registry& registry) { + auto view = registry.view(); - if (move_state.down) { - v.value.y = -v.max.y; + for (auto e : view) { + auto [creature, moveboost] = view.get(e); + if (moveboost.count <= moveboost.duration) { + ++moveboost.count; + creature.velocity.value += + creature.direction.value * creature.movement.acceleration; + } else { + creature.velocity.value -= + creature.direction.value * creature.movement.deceleration; } - - if (!move_state.down && !move_state.up) { - v.value.y = 0.0f; - } - } else { - if (move_state.up && move_state.can_up) { - v.value.y = movement.jump_power; - move_state.can_up = false; - } - - v.value.y += -g.value * dt; + creature.velocity.value.y = creature.gravity.value * dt; } }