From fb11fee955bf6638cfb038b53c202cdbb405b274 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Sun, 29 Mar 2026 20:19:08 +0800 Subject: [PATCH] feat: add collision detection --- include/Cubed/AABB.hpp | 19 ++ include/Cubed/app.hpp | 4 +- include/Cubed/config.hpp | 12 +- include/Cubed/gameplay/player.hpp | 26 ++- include/Cubed/gameplay/world.hpp | 3 +- src/gameplay/player.cpp | 364 ++++++++++++++++++------------ src/gameplay/world.cpp | 9 +- src/renderer.cpp | 7 + 8 files changed, 276 insertions(+), 168 deletions(-) create mode 100644 include/Cubed/AABB.hpp diff --git a/include/Cubed/AABB.hpp b/include/Cubed/AABB.hpp new file mode 100644 index 0000000..88dc295 --- /dev/null +++ b/include/Cubed/AABB.hpp @@ -0,0 +1,19 @@ +#pragma once +#include +struct AABB { + glm::vec3 min{0.0f, 0.0f, 0.0f}; + glm::vec3 max{0.0f, 0.0f, 0.0f}; + + AABB(glm::vec3 min_point, glm::vec3 max_point): + min(min_point), + max(max_point) + { + + } + + 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); + } +}; \ No newline at end of file diff --git a/include/Cubed/app.hpp b/include/Cubed/app.hpp index 5439669..1ea7a7b 100644 --- a/include/Cubed/app.hpp +++ b/include/Cubed/app.hpp @@ -30,14 +30,14 @@ private: Window m_window{m_renderer}; - GLuint m_texture_array; + GLuint m_texture_array = 0; inline static double last_time = glfwGetTime(); inline static double current_time = glfwGetTime(); inline static double delta_time = 0.0f; inline static double fps_time_count = 0.0f; inline static int frame_count = 0; - inline static int fps; + inline static int fps = 0; void init(); diff --git a/include/Cubed/config.hpp b/include/Cubed/config.hpp index 596a77a..825ea8a 100644 --- a/include/Cubed/config.hpp +++ b/include/Cubed/config.hpp @@ -103,13 +103,13 @@ constexpr float SQUARE_TEXTURE_POS[6][2] = { }; struct Vertex { - float x, y, z; - float s, t; - float layer; + float x = 0.0f, y = 0.0f, z = 0.0f; + float s = 0.0f, t = 0.0f; + float layer = 0.0f; }; struct Vertex2D { - float x, y; - float s, t; - float layer; + float x = 0.0f, y = 0.0f; + float s = 0.0f, t = 0.0f; + float layer = 0.0f; }; \ No newline at end of file diff --git a/include/Cubed/gameplay/player.hpp b/include/Cubed/gameplay/player.hpp index d2258af..0e90b18 100644 --- a/include/Cubed/gameplay/player.hpp +++ b/include/Cubed/gameplay/player.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -21,22 +22,35 @@ private: float m_sensitivity = 0.15f; float m_speed = 4.5f; - + float down_speed = 0.0f; + bool can_up = true; + float speed = 0; + glm::vec3 direction = glm::vec3(0.0f, 0.0f, 0.0f); + glm::vec3 move_distance {0.0f, 0.0f, 0.0f}; // player is tow block tall, the pos is the lower pos - glm::vec3 m_player_pos = glm::vec3(0.0f, 15.0f, 0.0f); - glm::vec3 m_front = glm::vec3(0, 0, -1); - glm::vec3 m_right; - MoveState m_move_state; + glm::vec3 m_player_pos {0.0f, 15.0f, 0.0f}; + glm::vec3 m_front {0, 0, -1}; + glm::vec3 m_right {0, 0, 0}; + glm::vec3 m_size {0.6f, 1.8f, 0.6f}; + MoveState m_move_state {}; std::optional m_look_block = std::nullopt; - std::string m_name; + std::string m_name {}; World& m_world; bool ray_cast(const glm::vec3& start, const glm::vec3& dir, glm::ivec3& block_pos, glm::vec3& normal, float distance = 4.0f); + void update_direction(); + void update_lookup_block(); + void update_move(float delta_time); + void update_x_move(); + void update_y_move(); + void update_z_move(); + public: Player(World& world, const std::string& name); ~Player(); + AABB get_aabb() const; const glm::vec3& get_front() const; const std::optional& get_look_block_pos() const; const glm::vec3& get_player_pos() const; diff --git a/include/Cubed/gameplay/world.hpp b/include/Cubed/gameplay/world.hpp index 70983be..1e7ac5f 100644 --- a/include/Cubed/gameplay/world.hpp +++ b/include/Cubed/gameplay/world.hpp @@ -2,6 +2,7 @@ #include #include +#include #include class Player; @@ -16,7 +17,7 @@ public: World(); ~World(); - bool can_move(const glm::vec3& new_pos) const; + bool can_move(const AABB& player_box) const; const BlockRenderData& get_block_render_data(int x, int y ,int z); const std::optional& get_look_block_pos(const std::string& name) const; Player& get_player(const std::string& name); diff --git a/src/gameplay/player.cpp b/src/gameplay/player.cpp index 0c31ad1..40375e6 100644 --- a/src/gameplay/player.cpp +++ b/src/gameplay/player.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -14,6 +15,27 @@ Player::Player(World& world, const std::string& name) : Player::~Player() { } + +AABB Player::get_aabb() const { + float half_width = m_size.x / 2.0f; + float half_depth = m_size.z / 2.0f; + + glm::vec3 min{ + m_player_pos.x - half_width, + m_player_pos.y, + m_player_pos.z - half_depth + }; + + glm::vec3 max { + m_player_pos.x + half_width, + m_player_pos.y + m_size.y, + m_player_pos.z + half_depth + }; + + return AABB{min, max}; + +} + const glm::vec3& Player::get_front() const { return m_front; } @@ -109,152 +131,8 @@ void Player::set_player_pos(const glm::vec3& pos) { } void Player::update(float delta_time) { - static float speed = 0; - static glm::vec3 direction = glm::vec3(0.0f, 0.0f, 0.0f); - if (m_move_state.forward || m_move_state.back || m_move_state.left || m_move_state.right || m_move_state.up) { - direction = glm::vec3(0.0f, 0.0f, 0.0f); - speed += ACCELERATION * delta_time; - if (speed > m_speed) { - speed = m_speed; - } - } else { - speed += -DECELERATION * delta_time; - if (speed < 0) { - speed = 0; - direction = glm::vec3(0.0f, 0.0f, 0.0f); - } - } - - m_right = glm::normalize(glm::cross(m_front, glm::vec3(0.0f, 1.0f, 0.0f))); - glm::vec3 move_dir_front = glm::vec3(0.0f); - glm::vec3 move_dir_right = glm::vec3(0.0f); - glm::vec3 move_dir = glm::vec3(0.0f); - if (m_move_state.forward) { - move_dir_front += glm::normalize(glm::vec3(m_front.x, 0.0f, m_front.z)); - - } - if (m_move_state.back) { - move_dir_front -= glm::normalize(glm::vec3(m_front.x, 0.0f, m_front.z)); - - } - if (m_move_state.left) { - move_dir_right -= glm::normalize(glm::vec3(m_right.x, 0.0f, m_right.z)); - - } - if (m_move_state.right) { - move_dir_right += glm::normalize(glm::vec3(m_right.x, 0.0f, m_right.z)); - } - move_dir = move_dir_front + move_dir_right; - - if (glm::length(move_dir) > 0.001f) { - direction = glm::normalize(move_dir); - } - - if (glm::length(direction) > 0.001f) { - auto new_pos = m_player_pos + direction * speed * delta_time; - new_pos.y += 1.0f; - if (m_world.can_move(new_pos)) { - new_pos.y -= 1.0f; - m_player_pos = new_pos; - } else { - if (glm::length(move_dir_front) > 0.001f) { - if (std::abs(move_dir_front.x) > std::abs(move_dir_front.z)) { - move_dir_front.z = 0.0f; - } else { - move_dir_front.x = 0.0f; - } - direction = glm::normalize(move_dir_front); - auto new_pos = m_player_pos + direction * speed * delta_time; - new_pos.y += 1.0f; - if (m_world.can_move(new_pos)) { - new_pos.y -= 1.0f; - m_player_pos = new_pos; - } else { - direction = glm::vec3(0.0f, 0.0f, 0.0f); - } - } - if (glm::length(move_dir_right) > 0.001f) { - if (std::abs(move_dir_right.x) > std::abs(move_dir_right.z)) { - move_dir_right.z = 0.0f; - } else { - move_dir_right.x = 0.0f; - } - direction = glm::normalize(move_dir_right); - auto new_pos = m_player_pos + direction * speed * delta_time; - new_pos.y += 1.0f; - if (m_world.can_move(new_pos)) { - new_pos.y -= 1.0f; - m_player_pos = new_pos; - } else { - direction = glm::vec3(0.0f, 0.0f, 0.0f); - } - } - } - } - - - - - if (m_move_state.up) { - auto new_pos = m_player_pos + glm::vec3(0.0f, 1.0f, 0.0f) * speed * 2.0f * delta_time; - new_pos.y += 2.0f; - if (!m_world.is_block(glm::floor(new_pos))) { - new_pos.y -= 2.0f; - m_player_pos = new_pos; - } - } - - /* - if (m_move_state.down) { - m_player_pos -= glm::vec3(0.0f, 1.0f, 0.0f) * speed; - } - */ - // calculate the block that is looked - glm::ivec3 block_pos; - glm::vec3 block_normal; - if(ray_cast(glm::vec3(m_player_pos.x, (m_player_pos.y + 1.6f), m_player_pos.z), m_front, block_pos, block_normal)) { - m_look_block = std::move(LookBlock{block_pos, glm::floor(block_normal)}); - } else { - m_look_block = std::nullopt; - } - - if (m_look_block != std::nullopt) { - if (Input::get_input_state().mouse_state.left) { - if (m_world.is_block(m_look_block->pos)) { - m_world.set_block(m_look_block->pos, 0); - - } - Input::get_input_state().mouse_state.left = false; - } - if (Input::get_input_state().mouse_state.right) { - glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal; - if (!m_world.is_block(near_pos)) { - glm::ivec3 p_pos = glm::floor(m_player_pos); - if ((near_pos != p_pos) && (near_pos != (p_pos + glm::ivec3(0 ,1, 0))) && (near_pos != (p_pos + glm::ivec3(0, 2, 0)))) { - m_world.set_block(near_pos, 1); - } - - } - Input::get_input_state().mouse_state.right = false; - } - } - - - static float down_speed = 0.0f; - - if (!m_world.is_block(glm::floor(m_player_pos)) && !m_move_state.up) { - down_speed += G * delta_time; - m_player_pos -= glm::vec3(0.0f, 1.0f, 0.0f) * down_speed * delta_time; - } else { - down_speed = 0.0f; - } - - if (m_player_pos.y < -50.0f) { - m_player_pos = glm::vec3(0.0f, 16.0f, 0.0f); - } - - - + update_move(delta_time); + update_lookup_block(); } void Player::update_player_move_state(int key, int action) { @@ -325,4 +203,198 @@ void Player::update_front_vec(float offset_x, float offset_y) { m_front.z = -cos(glm::radians(m_yaw)) * cos(glm::radians(m_pitch)); m_front = glm::normalize(m_front); +} + +void Player::update_direction() { + m_right = glm::normalize(glm::cross(m_front, glm::vec3(0.0f, 1.0f, 0.0f))); + + glm::vec3 move_dir_front = glm::vec3(0.0f); + glm::vec3 move_dir_right = glm::vec3(0.0f); + glm::vec3 move_dir = glm::vec3(0.0f); + if (m_move_state.forward) { + move_dir_front += glm::normalize(glm::vec3(m_front.x, 0.0f, m_front.z)); + + } + if (m_move_state.back) { + move_dir_front -= glm::normalize(glm::vec3(m_front.x, 0.0f, m_front.z)); + + } + if (m_move_state.left) { + move_dir_right -= glm::normalize(glm::vec3(m_right.x, 0.0f, m_right.z)); + + } + if (m_move_state.right) { + move_dir_right += glm::normalize(glm::vec3(m_right.x, 0.0f, m_right.z)); + } + move_dir = move_dir_front + move_dir_right; + + if (glm::length(move_dir) > 0.001f) { + direction = glm::normalize(move_dir); + } +} + +void Player::update_lookup_block() { + // calculate the block that is looked + glm::ivec3 block_pos; + glm::vec3 block_normal; + if(ray_cast(glm::vec3(m_player_pos.x, (m_player_pos.y + 1.6f), m_player_pos.z), m_front, block_pos, block_normal)) { + m_look_block = LookBlock{block_pos, glm::floor(block_normal)}; + } else { + m_look_block = std::nullopt; + } + + if (m_look_block != std::nullopt) { + if (Input::get_input_state().mouse_state.left) { + if (m_world.is_block(m_look_block->pos)) { + m_world.set_block(m_look_block->pos, 0); + + } + Input::get_input_state().mouse_state.left = false; + } + if (Input::get_input_state().mouse_state.right) { + glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal; + if (!m_world.is_block(near_pos)) { + glm::ivec3 p_pos = glm::floor(m_player_pos); + if ((near_pos != p_pos) && (near_pos != (p_pos + glm::ivec3(0 ,1, 0))) ) { + m_world.set_block(near_pos, 1); + } + + } + Input::get_input_state().mouse_state.right = false; + } + } +} + +void Player::update_move(float delta_time) { + // calculate speed + if (m_move_state.forward || m_move_state.back || m_move_state.left || m_move_state.right || m_move_state.up) { + direction = glm::vec3(0.0f, 0.0f, 0.0f); + speed += ACCELERATION * delta_time; + if (speed > m_speed) { + speed = m_speed; + } + } else { + speed += -DECELERATION * delta_time; + if (speed < 0) { + speed = 0; + direction = glm::vec3(0.0f, 0.0f, 0.0f); + } + } + + update_direction(); + + move_distance = {direction.x * speed * delta_time, 0.0f, direction.z * speed * delta_time}; + static float up_a = 0.0f; + static float y_a = 0.0f; + if (m_move_state.up && can_up) { + up_a = 100.f; + can_up = false; + + } + + y_a = -G + up_a; + down_speed += y_a * delta_time; + move_distance.y = down_speed * delta_time; + up_a -= 490.0f * delta_time; + if (up_a < 0.0f) { + up_a = 0.0f; + } + // y + update_y_move(); + // x + update_x_move(); + + update_z_move(); + + if (m_player_pos.y < -15.0f) { + m_player_pos.y = 15.0f; + } + +} + +void Player::update_x_move() { + m_player_pos.x += move_distance.x; + AABB player_box = get_aabb(); + 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); + + for (int x = minx; x <= maxx; ++x) { + for (int y = miny; y <= maxy; ++y) { + for (int z = minz; z <= maxz; ++z) { + if (m_world.is_block(glm::vec3{x, y, z})) { + AABB block_box = { + 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)} + }; + if (player_box.intersects(block_box)) { + + m_player_pos.x -= move_distance.x; + return; + } + } + } + } + } +} + +void Player::update_y_move() { + m_player_pos.y += move_distance.y; + AABB player_box = get_aabb(); + 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); + + for (int x = minx; x <= maxx; ++x) { + for (int y = miny; y <= maxy; ++y) { + for (int z = minz; z <= maxz; ++z) { + if (m_world.is_block(glm::vec3{x, y, z})) { + AABB block_box = { + 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)} + }; + if (player_box.intersects(block_box)) { + m_player_pos.y -= move_distance.y; + down_speed = 0.0f; + can_up = true; + return; + } + } + } + } + } +} + +void Player::update_z_move() { + m_player_pos.z += move_distance.z; + AABB player_box = get_aabb(); + 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); + + for (int x = minx; x <= maxx; ++x) { + for (int y = miny; y <= maxy; ++y) { + for (int z = minz; z <= maxz; ++z) { + if (m_world.is_block(glm::vec3{x, y, z})) { + AABB block_box = { + 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)} + }; + if (player_box.intersects(block_box)) { + m_player_pos.z -= move_distance.z; + return; + } + } + } + } + } } \ No newline at end of file diff --git a/src/gameplay/world.cpp b/src/gameplay/world.cpp index fe5585a..bea6b31 100644 --- a/src/gameplay/world.cpp +++ b/src/gameplay/world.cpp @@ -12,15 +12,10 @@ World::~World() { } -bool World::can_move(const glm::vec3& pos) const{ +bool World::can_move(const AABB& player_box) const{ - if (is_block(glm::floor(glm::vec3(pos.x, pos.y - 0.5f, pos.z)))) { - return false; - } - if (is_block(glm::floor(glm::vec3(pos.x, pos.y + 0.5f, pos.z)))) { - return false; - } + return true; } diff --git a/src/renderer.cpp b/src/renderer.cpp index ec7926a..3e3a796 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -12,6 +13,8 @@ #include #include + +#include Renderer::Renderer(const Camera& camera, World& world, const TextureManager& texture_manager): m_camera(camera), m_texture_manager(texture_manager), @@ -190,6 +193,10 @@ void Renderer::render_text() { glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_ui_proj)); Font::render_text(shader, std::string{"FPS: " + std::to_string(static_cast(App::get_fps()))}, 0.0f, 50.0f, 1.0f, glm::vec3(1.0f, 1.0f, 1.0f)); Font::render_text(shader, "Version: v0.0.1-Debug", 0.0f, 100.0f, 0.8f, glm::vec3(1.0f, 1.0f, 1.0f)); + const auto& player = m_world.get_player("TestPlayer"); + const auto& pos = player.get_player_pos(); + std::string player_pos = std::format("x: {:.2f} y: {:.2f} z: {:.2f}", pos.x, pos.y, pos.z); + Font::render_text(shader, player_pos, 0.0f, 150.0f, 0.8f, glm::vec3(1.0f, 1.0f, 1.0f)); glEnable(GL_DEPTH_TEST); }