From 2a37965c797e1ef03ec10ea8bf503d7605465f11 Mon Sep 17 00:00:00 2001 From: JAROWMR Date: Fri, 22 Nov 2024 21:17:30 +0100 Subject: refactored collision system --- src/crepe/system/CollisionSystem.cpp | 317 ++++++++++++++++++----------------- src/crepe/system/CollisionSystem.h | 133 ++++----------- 2 files changed, 196 insertions(+), 254 deletions(-) (limited to 'src/crepe/system') diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp index 8d9b356..f61a1dd 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -1,8 +1,11 @@ #include #include #include +#include #include #include +#include +#include #include "api/Event.h" #include "api/EventManager.h" @@ -16,6 +19,7 @@ #include "CollisionSystem.h" #include "Collider.h" #include "types.h" +#include "util/OptionalRef.h" using namespace crepe; @@ -24,69 +28,88 @@ void CollisionSystem::update() { ComponentManager & mgr = this->component_manager; std::vector> boxcolliders = mgr.get_components_by_type(); std::vector> circlecolliders = mgr.get_components_by_type(); + + std::vector all_colliders; + // Add BoxCollider references + for (auto& box : boxcolliders) { + all_colliders.push_back(collider_stor{box}); + } + + // Add CircleCollider references + for (auto& circle : circlecolliders) { + all_colliders.push_back(collider_stor{circle}); + } // Check between all colliders if there is a collision - std::vector> collided = check_collisions(boxcolliders,circlecolliders); + std::vector> collided = check_collisions(all_colliders); // For both objects call the collision handler for (auto& collision_pair : collided) { - collision_handler(collision_pair.first,collision_pair.second); - collision_handler(collision_pair.second,collision_pair.first); + collision_handler_request(collision_pair.first,collision_pair.second); + collision_handler_request(collision_pair.second,collision_pair.first); } } -void CollisionSystem::collision_handler(CollidedInfoStor& data1,CollidedInfoStor& data2){ +void CollisionSystem::collision_handler_request(CollidedInfoStor& data1,CollidedInfoStor& data2){ - // Data needed for collision handler info - const Collider* collider1 = nullptr; - const Collider* collider2 = nullptr; - vec2 move_back; + ColliderStorType type = check_collider_type(data1.collider,data2.collider); + std::pair move_back_data = collision_handler(data1,data2,type); - // Check collision type and get values for handler - if (std::holds_alternative>(data1.collider)) { - if (std::holds_alternative>(data2.collider)) { - - // Get colliders from variant to be used to determine collision handler info - const BoxCollider& box_collider1 = std::get>(data1.collider).get(); - const BoxCollider& box_collider2 = std::get>(data2.collider).get(); - collider1 = &box_collider1; - collider2 = &box_collider2; - - // TODO: send with the collider info to this function because this is calculated previously - // Get the current position of the collider - vec2 final_position1 = current_position(box_collider1,data1.transform,data1.rigidbody); - vec2 final_position2 = current_position(box_collider2,data2.transform,data2.rigidbody); - - // Determine move_back value for smallest overlap (x or y) - move_back = box_box_collision_move_back(box_collider1,box_collider2,final_position1,final_position2); - + OptionalRef collider1; + OptionalRef collider2; + switch (type) { + case ColliderStorType::BOX_BOX:{ + collider1 = std::get>(data1.collider); + collider2 = std::get>(data2.collider); } - else { - // TODO: calcualte Box - Circle collision info - const BoxCollider& box_collider = std::get>(data1.collider).get(); - const CircleCollider& circle_collider = std::get>(data2.collider).get(); - collider1 = &box_collider; - collider2 = &circle_collider; + case ColliderStorType::BOX_CIRCLE:{ + collider1 = std::get>(data1.collider); + collider2 = std::get>(data2.collider); + } + case ColliderStorType::CIRCLE_BOX:{ + collider1 = std::get>(data1.collider); + collider2 = std::get>(data2.collider); + } + case ColliderStorType::CIRCLE_CIRCLE:{ + collider1 = std::get>(data1.collider); + collider2 = std::get>(data2.collider); } } - else { - if (std::holds_alternative>(data2.collider)) { - // TODO: calcualte Circle - Circle collision info - const CircleCollider& circle_collider1 = std::get>(data1.collider).get(); - const CircleCollider& circle_collider2 = std::get>(data2.collider).get(); - collider1 = &circle_collider1; - collider2 = &circle_collider2; + + // collision info + crepe::CollisionSystem::CollisionInfo collision_info{ + .first={ collider1, data1.transform, data1.rigidbody }, + .second={ collider2, data2.transform, data2.rigidbody }, + .move_back_value = move_back_data.first, + .move_back_direction = move_back_data.second, + }; + + // Determine if static needs to be called + determine_collision_handler(collision_info); +} + + +std::pair CollisionSystem::collision_handler(CollidedInfoStor& data1,CollidedInfoStor& data2,ColliderStorType type) { + vec2 move_back; + switch (type) { + case ColliderStorType::BOX_BOX: { + const BoxCollider & collider1 = std::get>(data1.collider); + const BoxCollider & collider2 = std::get>(data2.collider); + vec2 collider_pos1 = current_position(collider1.offset, data1.transform, data1.rigidbody); + vec2 collider_pos2 = current_position(collider2.offset, data2.transform, data2.rigidbody); + move_back = box_box_move_back(collider1,collider2,collider_pos1,collider_pos2); } - else { - // TODO: calcualte Circle - Box collision info - const CircleCollider& circle_collider = std::get>(data1.collider); - const BoxCollider& box_collider = std::get>(data2.collider); - collider1 = &circle_collider; - collider2 = &box_collider; + case ColliderStorType::BOX_CIRCLE: { + + } + case ColliderStorType::CIRCLE_CIRCLE: { + + } + case ColliderStorType::CIRCLE_BOX: { + } } - // One vaue is calculated for moving back. Calculate the other value (x or y) relateive to the move_back value. Direction move_back_direction = Direction::NONE; if(move_back.x != 0 && move_back.y > 0) { move_back_direction = Direction::BOTH; @@ -100,20 +123,10 @@ void CollisionSystem::collision_handler(CollidedInfoStor& data1,CollidedInfoStor move_back.x = data1.rigidbody.data.linear_velocity.x * (move_back.y/data1.rigidbody.data.linear_velocity.y); } - // collision info - crepe::CollisionSystem::CollisionInfo collision_info{ - .first={ *collider1, data1.transform, data1.rigidbody }, - .second={ *collider2, data2.transform, data2.rigidbody }, - .move_back_value = move_back, - .move_back_direction = move_back_direction, - }; - - // Determine if static needs to be called - determine_collision_handler(collision_info); + return {move_back,move_back_direction}; } - -vec2 CollisionSystem::box_box_collision_move_back(const BoxCollider& box_collider1,const BoxCollider& box_collider2,vec2 final_position1,vec2 final_position2) +vec2 CollisionSystem::box_box_move_back(const BoxCollider& box_collider1,const BoxCollider& box_collider2,vec2 final_position1,vec2 final_position2) { vec2 resolution; // Default resolution vector vec2 delta = final_position2 - final_position1; @@ -185,10 +198,9 @@ void CollisionSystem::static_collision_handler(CollisionInfo& info){ } } -std::vector> CollisionSystem::check_collisions(const std::vector>& boxcolliders, const std::vector>& circlecolliders) { - ComponentManager & mgr = this->component_manager; - std::vector> collisions_ret; - +std::vector> CollisionSystem::check_collisions(std::vector & colliders) { + + // TODO: // If no colliders skip // Check if colliders has rigidbody if not skip @@ -198,99 +210,104 @@ std::vector(game_object_id_1).front().get(); - if(!transform1.active) continue; - Rigidbody& rigidbody1 = mgr.get_components_by_id(game_object_id_1).front().get(); - if(!rigidbody1.active) continue; - - // Check BoxCollider vs BoxCollider - for (size_t j = i + 1; j < boxcolliders.size(); ++j) { - if(!boxcolliders[j].get().active) continue; - // Skip self collision - int game_object_id_2 = boxcolliders[j].get().game_object_id; - if (game_object_id_1 == game_object_id_2) continue; - - // Fetch components for the second box collider - Transform & transform2 = mgr.get_components_by_id(boxcolliders[j].get().game_object_id).front().get(); - if(!transform2.active) continue; - Rigidbody & rigidbody2 = mgr.get_components_by_id(boxcolliders[j].get().game_object_id).front().get(); - if(!rigidbody2.active) continue; - // Check collision - if (check_box_box_collision(boxcolliders[i], boxcolliders[j], transform1, transform2, rigidbody1, rigidbody2)) { - collisions_ret.emplace_back(std::make_pair( - CollidedInfoStor{boxcolliders[i].get(), transform1, rigidbody1}, - CollidedInfoStor{boxcolliders[j].get(), transform2, rigidbody2} - )); - } - } + // Function to retrieve active transform and rigidbody components for a given game_object_id + auto get_active_transform_and_rigidbody = [&](game_object_id_t game_object_id) + -> std::optional, std::reference_wrapper>> { + RefVector transforms = this->component_manager.get_components_by_id(game_object_id); + if (transforms.empty()) return std::nullopt; + + RefVector rigidbodies = this->component_manager.get_components_by_id(game_object_id); + if (rigidbodies.empty()) return std::nullopt; + + Transform& transform = transforms.front().get(); + if (!transform.active) return std::nullopt; + + Rigidbody& rigidbody = rigidbodies.front().get(); + if (!rigidbody.active) return std::nullopt; - // Check BoxCollider vs CircleCollider - for (size_t j = 0; j < circlecolliders.size(); ++j) { - if(!circlecolliders[j].get().active) continue; - // Skip self collision - int game_object_id_2 = circlecolliders[j].get().game_object_id; - if (game_object_id_1 == game_object_id_2) continue; - - // Fetch components for the second collider (circle) - Transform & transform2 = mgr.get_components_by_id(circlecolliders[j].get().game_object_id).front().get(); - if(!transform2.active) continue; - Rigidbody & rigidbody2 = mgr.get_components_by_id(circlecolliders[j].get().game_object_id).front().get(); - if(!rigidbody2.active) continue; - - // Check collision - if (check_box_circle_collision(boxcolliders[i], circlecolliders[j], transform1, transform2, rigidbody1, rigidbody2)) { - - collisions_ret.emplace_back(std::make_pair( - CollidedInfoStor{boxcolliders[i].get(), transform1, rigidbody1}, - CollidedInfoStor{circlecolliders[j].get(), transform2, rigidbody2} - )); + // Return the active components + return std::make_pair(std::ref(transform), std::ref(rigidbody)); + }; + + + std::vector> collisions_ret; + for (size_t i = 0; i < colliders.size(); ++i) { + std::visit([&](auto& inner_collider_ref) { + if (inner_collider_ref.get().active) return; + auto inner_components = get_active_transform_and_rigidbody(inner_collider_ref.get().game_object_id); + if (inner_components) return; + for (size_t j = i + 1; j < colliders.size(); ++j) { + std::visit([&](auto& outer_collider_ref) { + if (outer_collider_ref.get().active) return; + if (inner_collider_ref.get().game_object_id == outer_collider_ref.get().game_object_id) return; + auto outer_components = get_active_transform_and_rigidbody(inner_collider_ref.get().game_object_id); + if (outer_components) return; + ColliderStorType type = check_collider_type(colliders[i],colliders[j]); + check_collision(colliders[i],*inner_components,colliders[j],*outer_components,type); + collisions_ret.emplace_back( + CollidedInfoStor{colliders[i], inner_components->first.get(), inner_components->second.get()}, + CollidedInfoStor{colliders[j], outer_components->first.get(), outer_components->second.get()} + ); + }, colliders[j]); } + }, colliders[i]); + } + + return collisions_ret; +} + +CollisionSystem::ColliderStorType CollisionSystem::check_collider_type(const collider_stor& collider1,const collider_stor& collider2){ + if(std::holds_alternative>(collider1)){ + if(std::holds_alternative>(collider2)) + { + return ColliderStorType::CIRCLE_CIRCLE; + } + else { + return ColliderStorType::CIRCLE_BOX; } } - // Check CircleCollider vs CircleCollider - for (size_t i = 0; i < circlecolliders.size(); ++i) { - if(!circlecolliders[i].get().active) continue; - // Fetch components for the first circle collider - int game_object_id_1 = circlecolliders[i].get().game_object_id; - Transform & transform1 = mgr.get_components_by_id(circlecolliders[i].get().game_object_id).front().get(); - if(!transform1.active) continue; - Rigidbody & rigidbody1 = mgr.get_components_by_id(circlecolliders[i].get().game_object_id).front().get(); - if(!rigidbody1.active) continue; - - for (size_t j = i + 1; j < circlecolliders.size(); ++j) { - if(!circlecolliders[j].get().active) continue; - // Skip self collision - int game_object_id_2 = circlecolliders[j].get().game_object_id; - if (game_object_id_1 == game_object_id_2) continue; - - // Fetch components for the second circle collider - Transform & transform2 = mgr.get_components_by_id(circlecolliders[j].get().game_object_id).front().get(); - if(!transform2.active) continue; - Rigidbody & rigidbody2 = mgr.get_components_by_id(circlecolliders[j].get().game_object_id).front().get(); - if(!rigidbody2.active) continue; - - // Check collision - if (check_circle_circle_collision(circlecolliders[i], circlecolliders[j], transform1, transform2, rigidbody1, rigidbody2)) { - collisions_ret.emplace_back(std::make_pair( - CollidedInfoStor{circlecolliders[i].get(), transform1, rigidbody1}, - CollidedInfoStor{circlecolliders[j].get(), transform2, rigidbody2} - )); - } + else { + if(std::holds_alternative>(collider2)) + { + return ColliderStorType::BOX_CIRCLE; + } + else { + return ColliderStorType::BOX_BOX; } } - return collisions_ret; } +bool CollisionSystem::check_collision(const collider_stor& collider1,std::pair, std::reference_wrapper> components1,const collider_stor& collider2,std::pair, std::reference_wrapper> components2, ColliderStorType type){ + switch (type) { + case ColliderStorType::BOX_BOX: { + const BoxCollider & box_collider1 = std::get>(collider1); + const BoxCollider & box_collider2 = std::get>(collider2); + return check_box_box_collision(box_collider1,box_collider2,components1.first.get(),components2.first.get(),components1.second.get(),components2.second.get()); + } + case ColliderStorType::BOX_CIRCLE: { + const BoxCollider & box_collider = std::get>(collider1); + const CircleCollider & circle_collider = std::get>(collider2); + return check_box_circle_collision(box_collider,circle_collider,components1.first.get(),components2.first.get(),components1.second.get(),components2.second.get()); + } + case ColliderStorType::CIRCLE_CIRCLE: { + const CircleCollider & circle_collider1 = std::get>(collider1); + const CircleCollider & circle_collider2 = std::get>(collider2); + return check_circle_circle_collision(circle_collider1,circle_collider2,components1.first.get(),components2.first.get(),components1.second.get(),components2.second.get()); + } + case ColliderStorType::CIRCLE_BOX: { + const BoxCollider & box_collider = std::get>(collider1); + const CircleCollider & circle_collider = std::get>(collider2); + return check_box_circle_collision(box_collider,circle_collider,components1.first.get(),components2.first.get(),components1.second.get(),components2.second.get()); + } + } +} + + bool CollisionSystem::check_box_box_collision(const BoxCollider& box1, const BoxCollider& box2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) { // Get current positions of colliders - vec2 final_position1 = current_position(box1,transform1,rigidbody1); - vec2 final_position2 = current_position(box2,transform2,rigidbody2); + vec2 final_position1 = current_position(box1.offset,transform1,rigidbody1); + vec2 final_position2 = current_position(box2.offset,transform2,rigidbody2); // Calculate half-extents (half width and half height) float half_width1 = box1.width / 2.0; @@ -307,8 +324,8 @@ bool CollisionSystem::check_box_box_collision(const BoxCollider& box1, const Box bool CollisionSystem::check_box_circle_collision(const BoxCollider& box1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) { // Get current positions of colliders - vec2 final_position1 = current_position(box1, transform1, rigidbody1); - vec2 final_position2 = current_position(circle2, transform2, rigidbody2); + vec2 final_position1 = current_position(box1.offset, transform1, rigidbody1); + vec2 final_position2 = current_position(circle2.offset, transform2, rigidbody2); // Calculate box half-extents float half_width = box1.width / 2.0; @@ -329,8 +346,8 @@ bool CollisionSystem::check_box_circle_collision(const BoxCollider& box1, const bool CollisionSystem::check_circle_circle_collision(const CircleCollider& circle1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) { // Get current positions of colliders - vec2 final_position1 = current_position(circle1,transform1,rigidbody1); - vec2 final_position2 = current_position(circle2,transform2,rigidbody2); + vec2 final_position1 = current_position(circle1.offset,transform1,rigidbody1); + vec2 final_position2 = current_position(circle2.offset,transform2,rigidbody2); float distance_x = final_position1.x - final_position2.x; float distance_y = final_position1.y - final_position2.y; @@ -343,12 +360,12 @@ bool CollisionSystem::check_circle_circle_collision(const CircleCollider& circle return distance_squared <= radius_sum * radius_sum; } -vec2 CollisionSystem::current_position(const Collider& collider, const Transform& transform, const Rigidbody& rigidbody) { +vec2 CollisionSystem::current_position(vec2 collider_offset, const Transform& transform, const Rigidbody& rigidbody) { // Get the rotation in radians float radians1 = transform.rotation * (M_PI / 180.0); // Calculate total offset with scale - vec2 total_offset = (rigidbody.data.offset + collider.offset) * transform.scale; + vec2 total_offset = (rigidbody.data.offset + collider_offset) * transform.scale; // Rotate float rotated_total_offset_x1 = total_offset.x * cos(radians1) - total_offset.y * sin(radians1); diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h index 44fff4e..80f8818 100644 --- a/src/crepe/system/CollisionSystem.h +++ b/src/crepe/system/CollisionSystem.h @@ -25,6 +25,15 @@ private: // using collider_stor = std::variant; using collider_stor = std::variant, std::reference_wrapper>; + //! A enum that is used to tell the pair of the collider stor in a std::pair. + enum class ColliderStorType { + BOX_BOX, + CIRCLE_CIRCLE, + BOX_CIRCLE, + CIRCLE_BOX, + }; + + /** * \brief A structure to store the collision data of a single collider. * @@ -32,7 +41,7 @@ private: */ struct CollidedInfoStor { //! Store either BoxCollider or CircleCollider - collider_stor collider; + collider_stor& collider; Transform& transform; Rigidbody& rigidbody; }; @@ -45,6 +54,8 @@ private: BOTH }; + + public: /** * \brief A structure representing the collision information between two colliders. @@ -73,121 +84,35 @@ public: //! Updates the collision system by checking for collisions between colliders and handling them. void update() override; +private: //generic + ColliderStorType check_collider_type(const collider_stor& collider1,const collider_stor& collider2); + + vec2 current_position(vec2 collider_offset, const Transform& transform, const Rigidbody& rigidbody); + +private:// handeling + + void collision_handler_request(CollidedInfoStor& data1,CollidedInfoStor& data2); + + std::pair collision_handler(CollidedInfoStor& data1,CollidedInfoStor& data2 ,ColliderStorType type); + -private: - /** - * \brief Handles a collision between two colliders. - * - * This function calculates the necessary response to resolve the collision, including - * moving the objects back to prevent overlap and applying any velocity changes. - * - * \param data1 The collision data for the first collider. - * \param data2 The collision data for the second collider. - */ - void collision_handler(CollidedInfoStor& data1,CollidedInfoStor& data2); - /** - * \brief Resolves the movement of two box colliders that are colliding. - * - * This function calculates the smallest overlap (along the X or Y axis) and determines - * the move-back vector to prevent overlap. - * - * \param box_collider1 The first box collider. - * \param box_collider2 The second box collider. - * \param final_position1 The final position of the first box collider. - * \param final_position2 The final position of the second box collider. - * \return The move-back vector to resolve the collision. - */ - vec2 box_box_collision_move_back(const BoxCollider& box_collider1,const BoxCollider& box_collider2,vec2 position1,vec2 position2); + vec2 box_box_move_back(const BoxCollider& box_collider1,const BoxCollider& box_collider2,vec2 position1,vec2 position2); - /** - * \brief Determines the appropriate collision handler based on the rigidbody types of the colliding objects. - * - * This function checks if the second object is static, and if so, it calls the static collision handler. - * Otherwise, it triggers a collision event. - * - * \param info The collision information containing the colliders, transforms, rigidbodies, and move-back data. - */ void determine_collision_handler(CollisionInfo& info); - /** - * \brief Handles the collision with a static object. - * - * This function resolves the collision by moving the object back and applying any bounce or stop behavior. - * - * \param info The collision information containing the colliders, transforms, rigidbodies, and move-back data. - */ void static_collision_handler(CollisionInfo& info); -private: //detection +private: // detection - /** - * \brief Checks for collisions between box colliders and circle colliders - * and returns the collision pairs that need to be resolved. - * - * \param boxcolliders A vector of references to all box colliders. - * \param circlecolliders A vector of references to all circle colliders. - * \return A vector of pairs containing collision information for the detected collisions. - */ - std::vector> check_collisions(const std::vector>& boxcolliders, const std::vector>& circlecolliders); + std::vector> check_collisions(std::vector & colliders); + + bool check_collision(const collider_stor& collider1,std::pair, std::reference_wrapper> components1,const collider_stor& collider2,std::pair, std::reference_wrapper> components2,CollisionSystem::ColliderStorType type); - /** - * \brief Checks for a collision between two box colliders. - * - * This function checks if two box colliders overlap based on their positions and dimensions. - * - * \param box1 The first box collider. - * \param box2 The second box collider. - * \param transform1 The transform component of the first box collider. - * \param transform2 The transform component of the second box collider. - * \param rigidbody1 The rigidbody component of the first box collider. - * \param rigidbody2 The rigidbody component of the second box collider. - * \return True if the two box colliders overlap, otherwise false. - */ bool check_box_box_collision(const BoxCollider& box1, const BoxCollider& box2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2); - /** - * \brief Checks for a collision between a box collider and a circle collider. - * - * This function checks if a box collider overlaps with a circle collider based on their positions - * and dimensions. - * - * \param box1 The box collider. - * \param circle2 The circle collider. - * \param transform1 The transform component of the box collider. - * \param transform2 The transform component of the circle collider. - * \param rigidbody1 The rigidbody component of the box collider. - * \param rigidbody2 The rigidbody component of the circle collider. - * \return True if the box collider and circle collider overlap, otherwise false. - */ bool check_box_circle_collision(const BoxCollider& box1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2); - - /** - * \brief Checks for a collision between two circle colliders. - * - * This function checks if two circle colliders overlap based on their positions and radii. - * - * \param circle1 The first circle collider. - * \param circle2 The second circle collider. - * \param transform1 The transform component of the first circle collider. - * \param transform2 The transform component of the second circle collider. - * \param rigidbody1 The rigidbody component of the first circle collider. - * \param rigidbody2 The rigidbody component of the second circle collider. - * \return True if the two circle colliders overlap, otherwise false. - */ + bool check_circle_circle_collision(const CircleCollider& circle1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2); - - /** - * \brief Gets the current position of a collider by combining its transform and rigidbody data. - * - * This function calculates the current position of the collider by considering its transform and - * rigidbody velocity. - * - * \param collider The collider whose position is being determined. - * \param transform The transform component associated with the collider. - * \param rigidbody The rigidbody component associated with the collider. - * \return The current position of the collider as a vec2. - */ - vec2 current_position(const Collider& collider, const Transform& transform, const Rigidbody& rigidbody); }; } // namespace crepe -- cgit v1.2.3