From df3ac5b5db4155395308fc2544006d4f009db8dd Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Sat, 25 Jul 2026 14:53:19 +0800 Subject: [PATCH] refactor(collision): convert AABB to center-half representation and migrate player data to ECS --- assets/model/creature/collision.json | 16 ++ include/Cubed/AABB.hpp | 18 +- include/Cubed/gameplay/client_world.hpp | 34 ++-- include/Cubed/gameplay/entity.hpp | 21 +- src/gameplay/client_player.cpp | 54 +++--- src/gameplay/client_world.cpp | 244 +++++++++++++----------- 6 files changed, 222 insertions(+), 165 deletions(-) create mode 100644 assets/model/creature/collision.json diff --git a/assets/model/creature/collision.json b/assets/model/creature/collision.json new file mode 100644 index 0000000..69607ea --- /dev/null +++ b/assets/model/creature/collision.json @@ -0,0 +1,16 @@ +{ + "boxes": [ + { + "center": [ + 0, + 0.78, + 0 + ], + "half": [ + 0.53, + 0.78, + 0.406 + ] + } + ] +} \ No newline at end of file diff --git a/include/Cubed/AABB.hpp b/include/Cubed/AABB.hpp index ec6ad52..fd32468 100644 --- a/include/Cubed/AABB.hpp +++ b/include/Cubed/AABB.hpp @@ -4,16 +4,20 @@ namespace Cubed { struct AABB { - glm::vec3 min{0.0f, 0.0f, 0.0f}; - glm::vec3 max{0.0f, 0.0f, 0.0f}; + glm::vec3 center{0.0f}; + glm::vec3 half{0.0f}; - AABB(glm::vec3 min_point, glm::vec3 max_point) - : min(min_point), max(max_point) {} + AABB(glm::vec3 center_point, glm::vec3 half_size) + : center(center_point), half(half_size) {} + + glm::vec3 min() const { return center - half; } + + glm::vec3 max() const { return center + half; } bool intersects(const AABB& other) const { - return (min.x <= other.max.x && max.x >= other.min.x) && - (min.y <= other.max.y && max.y >= other.min.y) && - (min.z <= other.max.z && max.z >= other.min.z); + return (glm::abs(center.x - other.center.x) <= half.x + other.half.x) && + (glm::abs(center.y - other.center.y) <= half.y + other.half.y) && + (glm::abs(center.z - other.center.z) <= half.z + other.half.z); } }; diff --git a/include/Cubed/gameplay/client_world.hpp b/include/Cubed/gameplay/client_world.hpp index c849043..1b6f4f3 100644 --- a/include/Cubed/gameplay/client_world.hpp +++ b/include/Cubed/gameplay/client_world.hpp @@ -20,21 +20,6 @@ #include namespace Cubed { -struct PlayerInfo { - std::string name; - std::string uuid; - glm::vec3 render_pos; - glm::vec3 target_pos; - float render_yaw; - float yaw; - float render_pitch; - float pitch; - Gait gait; - float angle = 0.0f; - float walk_time = 0.0f; - float moving_time = 0.0f; -}; - struct PlayerRenderData { std::string name; std::string uuid; @@ -55,7 +40,8 @@ public: ~ClientWorld(); void init(std::string_view player_name, std::shared_ptr client); - void update(float delta_time); + void update(float dt); + void update_players(float dt); bool handle_event(const Event& e); const std::optional& get_look_block_pos() const; ClientPlayer& get_player(); @@ -123,6 +109,17 @@ public: std::forward_as_tuple(threshold, std::forward(f))); } + template + entt::entity create_entity(Components&&... components) { + auto e = m_registry.create(); + + (m_registry.emplace>( + e, std::forward(components)), + ...); + + return e; + } + private: struct VoiceMessage { std::string data; @@ -141,7 +138,6 @@ private: ChunkPos::TBBHash>; using ChunkPosSet = absl::flat_hash_set; using ChunkPosVector = std::vector; - using OtherPlayerHashMap = std::unordered_map; using chunk_acc = ChunkHashMap::accessor; using chunk_cacc = ChunkHashMap::const_accessor; @@ -156,7 +152,7 @@ private: entt::registry m_registry; ClientPlayer m_player; - OtherPlayerHashMap m_player_info; + std::unordered_map m_player_entities; ChunkHashMap m_chunks; AudioEngine& m_audio; Config& m_config; @@ -164,7 +160,7 @@ private: std::vector m_planes; std::jthread m_client_thread; - mutable std::shared_mutex m_player_info_mutex; + mutable std::shared_mutex m_registry_mutex; tbb::concurrent_queue> m_pending_upload_queue; tbb::concurrent_queue m_dirty_chunk_queue; diff --git a/include/Cubed/gameplay/entity.hpp b/include/Cubed/gameplay/entity.hpp index 056b38e..fd27395 100644 --- a/include/Cubed/gameplay/entity.hpp +++ b/include/Cubed/gameplay/entity.hpp @@ -1,9 +1,20 @@ #pragma once +#include "Cubed/gameplay/player.hpp" + #include #include namespace Cubed { struct Transform { - glm::vec3 pos; + glm::vec3 pos{0, 0, 0}; + glm::vec3 render_pos{0, 0, 0}; + Gait gait = Gait::STOP; + float walk_time = 0.0f; + float moving_time = 0.0f; +}; + +struct EntityInfo { + std::string name; + std::string uuid; }; struct Model { @@ -15,4 +26,12 @@ struct Health { float max_hp = 20; }; +struct ViewAngles { + float render_yaw = 0.0f; + float yaw = 0.0f; + float render_pitch = 0.0f; + float pitch = 0.0f; + float angle = 0.0f; +}; + } // namespace Cubed \ No newline at end of file diff --git a/src/gameplay/client_player.cpp b/src/gameplay/client_player.cpp index 03ae263..fb875da 100644 --- a/src/gameplay/client_player.cpp +++ b/src/gameplay/client_player.cpp @@ -12,14 +12,11 @@ ClientPlayer::ClientPlayer(ClientWorld& world) : m_world(world) {} ClientPlayer::~ClientPlayer() {} AABB ClientPlayer::get_aabb(const glm::vec3& pos) { - float half_width = M_SIZE.x / 2.0f; - float half_depth = M_SIZE.z / 2.0f; + glm::vec3 half = M_SIZE * 0.5f; - glm::vec3 min{pos.x - half_width, pos.y, pos.z - half_depth}; + glm::vec3 center{pos.x, pos.y + half.y, pos.z}; - glm::vec3 max{pos.x + half_width, pos.y + M_SIZE.y, pos.z + half_depth}; - - return AABB{min, max}; + return AABB{center, half}; } const glm::vec3& ClientPlayer::get_front() const { return m_front; } @@ -442,12 +439,15 @@ void ClientPlayer::update_x_move(glm::vec3& player_pos) { return; } AABB player_box = get_aabb(player_pos); - int minx = std::floor(player_box.min.x); - int maxx = std::floor(player_box.max.x); - int miny = std::floor(player_box.min.y); - int maxy = std::floor(player_box.max.y); - int minz = std::floor(player_box.min.z); - int maxz = std::floor(player_box.max.z); + 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) { @@ -472,12 +472,15 @@ void ClientPlayer::update_y_move(glm::vec3& player_pos) { return; } AABB player_box = get_aabb(player_pos); - int minx = std::floor(player_box.min.x); - int maxx = std::floor(player_box.max.x); - int miny = std::floor(player_box.min.y); - int maxy = std::floor(player_box.max.y); - int minz = std::floor(player_box.min.z); - int maxz = std::floor(player_box.max.z); + 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) { @@ -506,12 +509,15 @@ void ClientPlayer::update_z_move(glm::vec3& player_pos) { return; } AABB player_box = get_aabb(player_pos); - int minx = std::floor(player_box.min.x); - int maxx = std::floor(player_box.max.x); - int miny = std::floor(player_box.min.y); - int maxy = std::floor(player_box.max.y); - int minz = std::floor(player_box.min.z); - int maxz = std::floor(player_box.max.z); + 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) { diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index c85e172..ca2ecd1 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -296,10 +296,13 @@ void ClientWorld::report_block_change(const glm::ivec3& pos, unsigned id) const { if (id != 0) { AABB block_box = get_block_aabb(pos); - std::shared_lock lock(m_player_info_mutex); + std::shared_lock lock(m_registry_mutex); - for (auto& [uuid, player] : m_player_info) { - AABB box = ClientPlayer::get_aabb(player.target_pos); + for (auto& [key, player] : m_player_entities) { + auto [transform, entity_info] = + m_registry.get(player); + + AABB box = ClientPlayer::get_aabb(transform.pos); if (box.intersects(block_box)) { return; } @@ -331,20 +334,22 @@ void ClientWorld::receive_remote_player(const PlayerInfoRsp& rsp) { auto pitch = rsp.pitch(); auto yaw = rsp.yaw(); { - std::lock_guard lock(m_player_info_mutex); + std::lock_guard lock(m_registry_mutex); glm::vec3 pos{rsp.pos().x(), rsp.pos().y(), rsp.pos().z()}; - auto it = m_player_info.find(rsp.uuid()); - if (it == m_player_info.end()) { - m_player_info.emplace( - std::piecewise_construct, std::forward_as_tuple(rsp.uuid()), - std::forward_as_tuple(rsp.name(), rsp.uuid(), pos, pos, yaw, - yaw, pitch, pitch, - get_gait_from_id(rsp.gait()))); + auto it = m_player_entities.find(rsp.uuid()); + if (it == m_player_entities.end()) { + auto entity = + create_entity(EntityInfo{rsp.name(), rsp.uuid()}, + Transform{pos, pos, get_gait_from_id(rsp.gait())}, + ViewAngles{yaw, yaw, pitch, pitch}); + m_player_entities.try_emplace(rsp.uuid(), entity); } else { - it->second.target_pos = pos; - it->second.yaw = yaw; - it->second.pitch = pitch; - it->second.gait = get_gait_from_id(rsp.gait()); + auto& transform = m_registry.get(it->second); + transform.pos = pos; + transform.gait = get_gait_from_id(rsp.gait()); + auto& view_angles = m_registry.get(it->second); + view_angles.yaw = yaw; + view_angles.pitch = pitch; } // Logger::info("Player {} pos Update", rsp.name()); } @@ -360,11 +365,13 @@ void ClientWorld::receive_player_logout(const LogoutRsp& rsp) { return; } { - std::lock_guard lock(m_player_info_mutex); - int sum = m_player_info.erase(rsp.uuid()); - if (sum == 0) { + std::lock_guard lock(m_registry_mutex); + auto it = m_player_entities.find(rsp.uuid()); + if (it == m_player_entities.end()) { Logger::warn("Player {} not find", rsp.uuid()); } else { + m_registry.destroy(it->second); + m_player_entities.erase(rsp.uuid()); Logger::info("Player {} erase", rsp.uuid()); } } @@ -474,9 +481,8 @@ void ClientWorld::init(std::string_view player_name, Logger::info("Send Login Request"); m_client->send(make_packet(req), 0); m_audio.play_bgm(); - auto pig = m_registry.create(); - m_registry.emplace(pig, glm::vec3{0.0f, 100.0f, 0.0f}); - m_registry.emplace(pig, "model/creature/pig.glb"); + create_entity(Transform{glm::vec3{0.0f, 100.0f, 0.0f}}, + Model{"model/creature/pig.glb"}); } void ClientWorld::receive_login_rsp(LoginRsp& rsp) { @@ -726,15 +732,11 @@ bool ClientWorld::is_receive_exit() { return m_receive_exit; } int ClientWorld::chunk_size() const { return m_chunks.size(); } AABB ClientWorld::get_block_aabb(const glm::ivec3& pos) { - auto x = pos.x; - auto y = pos.y; - auto z = pos.z; - return {glm::vec3{static_cast(x), static_cast(y), - static_cast(z)}, - glm::vec3{static_cast(x + 1), static_cast(y + 1), - static_cast(z + 1)}}; + return {glm::vec3{static_cast(pos.x) + 0.5f, + static_cast(pos.y) + 0.5f, + static_cast(pos.z) + 0.5f}, + glm::vec3{0.5f, 0.5f, 0.5f}}; } - AudioEngine& ClientWorld::get_audio() { return m_audio; } const AudioEngine& ClientWorld::get_audio() const { return m_audio; } Config& ClientWorld::get_config() { return m_config; } @@ -857,91 +859,7 @@ void ClientWorld::update(float delta_time) { } m_render_player_data.clear(); - { - std::lock_guard lock(m_player_info_mutex); - for (auto& [uuid, player] : m_player_info) { - player.render_pos = - glm::mix(player.render_pos, player.target_pos, 0.15f); - if (Math::distance2(player.render_pos, m_player.get_player_pos()) > - m_rendering_distance * CHUNK_SIZE * m_rendering_distance * - CHUNK_SIZE) { - continue; - } - player.render_yaw = glm::mix(player.render_yaw, player.yaw, 0.15); - player.render_pitch = - glm::mix(player.render_pitch, player.pitch, 0.15); - - if (player.gait == Gait::WALK || player.gait == Gait::RUN) { - - player.walk_time += delta_time; - - float speed = player.gait == Gait::RUN ? 14.0f : 8.0f; - float amp = player.gait == Gait::RUN ? 50.0f : 35.0f; - // float amp = 90.0f; - player.angle = - glm::sin(player.walk_time * speed) * glm::radians(amp); - } else if (player.gait == Gait::STOP) { - float t = glm::clamp(delta_time * 10.0f, 0.0f, 1.0f); - player.angle = glm::mix(player.angle, 0.0f, t); - } - - // walking sound - if (player.gait == Gait::STOP) { - player.moving_time = 0.0f; - } else { - player.moving_time += delta_time; - } - auto play_walk_sound = [&]() { - glm::ivec3 block = glm::floor(player.render_pos); - block.y -= 1; - BlockType id = get_block_tpye(block); - if (id == 0) { - return; - } - std::string name = BlockManager::name_form_id(id); - std::string sound = "block/" + name + "/walk.ogg"; - m_audio.play_3d(sound, player.render_pos); - }; - if (player.gait == Gait::WALK) { - if (player.moving_time >= ClientPlayer::WALK_SOUND_INTERVAL) { - player.moving_time = 0.0f; - play_walk_sound(); - } - } - if (player.gait == Gait::RUN) { - if (player.moving_time >= ClientPlayer::RUN_SOUND_INTERVAL) { - player.moving_time = 0.0f; - play_walk_sound(); - } - } - - m_render_player_data.emplace_back( - player.name, player.uuid, player.render_pos, player.render_yaw, - player.render_pitch, player.gait, player.angle); - } - { - auto gait = m_player.get_gait(); - auto& walk_time = m_player.walk_time(); - auto& angle = m_player.angle(); - if (gait == Gait::WALK || gait == Gait::RUN) { - - walk_time += delta_time; - - float speed = gait == Gait::RUN ? 14.0f : 8.0f; - float amp = gait == Gait::RUN ? 50.0f : 35.0f; - // float amp = 90.0f; - angle = glm::sin(walk_time * speed) * glm::radians(amp); - } else if (gait == Gait::STOP) { - float t = glm::clamp(delta_time * 10.0f, 0.0f, 1.0f); - angle = glm::mix(angle, 0.0f, t); - } - - m_render_player_data.emplace_back( - m_player.get_name(), m_player.get_uuid(), - m_player.get_player_pos(), m_player.yaw(), m_player.pitch(), - m_player.get_gait(), m_player.angle()); - } - } + update_players(delta_time); // sound PendingSound pending_sound; @@ -963,6 +881,104 @@ void ClientWorld::update(float delta_time) { } } +void ClientWorld::update_players(float dt) { + + auto update_renderinfo = [this, dt](entt::entity player) { + auto& transform = m_registry.get(player); + transform.render_pos = + glm::mix(transform.render_pos, transform.pos, 0.15f); + if (Math::distance2(transform.render_pos, m_player.get_player_pos()) > + m_rendering_distance * CHUNK_SIZE * m_rendering_distance * + CHUNK_SIZE) { + return; + } + auto& view_angles = m_registry.get(player); + view_angles.render_yaw = + glm::mix(view_angles.render_yaw, view_angles.yaw, 0.15); + view_angles.render_pitch = + glm::mix(view_angles.render_pitch, view_angles.pitch, 0.15); + + if (transform.gait == Gait::WALK || transform.gait == Gait::RUN) { + + transform.walk_time += dt; + + float speed = transform.gait == Gait::RUN ? 14.0f : 8.0f; + float amp = transform.gait == Gait::RUN ? 50.0f : 35.0f; + // float amp = 90.0f; + view_angles.angle = + glm::sin(transform.walk_time * speed) * glm::radians(amp); + } else if (transform.gait == Gait::STOP) { + float t = glm::clamp(dt * 10.0f, 0.0f, 1.0f); + view_angles.angle = glm::mix(view_angles.angle, 0.0f, t); + } + + // walking sound + if (transform.gait == Gait::STOP) { + transform.moving_time = 0.0f; + } else { + transform.moving_time += dt; + } + auto play_walk_sound = [&]() { + glm::ivec3 block = glm::floor(transform.render_pos); + block.y -= 1; + BlockType id = get_block_tpye(block); + if (id == 0) { + return; + } + std::string name = BlockManager::name_form_id(id); + std::string sound = "block/" + name + "/walk.ogg"; + m_audio.play_3d(sound, transform.render_pos); + }; + if (transform.gait == Gait::WALK) { + if (transform.moving_time >= ClientPlayer::WALK_SOUND_INTERVAL) { + transform.moving_time = 0.0f; + play_walk_sound(); + } + } + if (transform.gait == Gait::RUN) { + if (transform.moving_time >= ClientPlayer::RUN_SOUND_INTERVAL) { + transform.moving_time = 0.0f; + play_walk_sound(); + } + } + auto& player_info = m_registry.get(player); + m_render_player_data.emplace_back( + player_info.name, player_info.uuid, transform.render_pos, + view_angles.render_yaw, view_angles.render_pitch, transform.gait, + view_angles.angle); + }; + + { + std::lock_guard lock(m_registry_mutex); + + for (auto& [_, player] : m_player_entities) { + update_renderinfo(player); + } + { + auto gait = m_player.get_gait(); + auto& walk_time = m_player.walk_time(); + auto& angle = m_player.angle(); + if (gait == Gait::WALK || gait == Gait::RUN) { + + walk_time += dt; + + float speed = gait == Gait::RUN ? 14.0f : 8.0f; + float amp = gait == Gait::RUN ? 50.0f : 35.0f; + // float amp = 90.0f; + angle = glm::sin(walk_time * speed) * glm::radians(amp); + } else if (gait == Gait::STOP) { + float t = glm::clamp(dt * 10.0f, 0.0f, 1.0f); + angle = glm::mix(angle, 0.0f, t); + } + + m_render_player_data.emplace_back( + m_player.get_name(), m_player.get_uuid(), + m_player.get_player_pos(), m_player.yaw(), m_player.pitch(), + m_player.get_gait(), m_player.angle()); + } + } +} + bool ClientWorld::handle_event(const Event& e) { return std::visit( Overloaded{[this](const MouseButtonEvent& e) {