diff options
Diffstat (limited to 'src/crepe')
-rw-r--r-- | src/crepe/system/CollisionSystem.cpp | 337 | ||||
-rw-r--r-- | src/crepe/system/CollisionSystem.h | 61 |
2 files changed, 129 insertions, 269 deletions
diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp index e4f6cb3..a9a0523 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -75,11 +75,11 @@ void CollisionSystem::update() { // For both objects call the collision handler for (auto & collision_pair : collided) { // Determine type - CollisionInternalType type = this->get_collider_type(collision_pair.first.collider, collision_pair.second.collider); + //CollisionInternalType type = this->get_collider_type(collision_pair.first.collider, collision_pair.second.collider); // Determine resolution - std::pair<vec2, CollisionSystem::Direction> resolution_data = this->get_collision_resolution(collision_pair.first, collision_pair.second, type); + //std::pair<vec2, CollisionSystem::Direction> resolution_data = this->get_collision_resolution(collision_pair.first, collision_pair.second, type); // Convert internal struct to external struct - CollisionInfo info = this->get_collision_info(collision_pair.first, collision_pair.second,type,resolution_data.first,resolution_data.second); + CollisionInfo info = this->get_collision_info(collision_pair.first, collision_pair.second); // Determine if and/or what collison handler is needed. this->determine_collision_handler(info); } @@ -87,7 +87,7 @@ void CollisionSystem::update() { // Below is for collision detection std::vector<std::pair<CollisionSystem::CollisionInternal, CollisionSystem::CollisionInternal>> -CollisionSystem::gather_collisions(const std::vector<CollisionInternal> & colliders) const { +CollisionSystem::gather_collisions(std::vector<CollisionInternal> & colliders) { // TODO: // If no colliders skip @@ -106,7 +106,7 @@ CollisionSystem::gather_collisions(const std::vector<CollisionInternal> & collid if (colliders[i].id == colliders[j].id) continue; if (!should_collide(colliders[i], colliders[j])) continue; CollisionInternalType type = get_collider_type(colliders[i].collider, colliders[j].collider); - if (!get_collision(colliders[i],colliders[j],type)) continue; + if (!detect_collision(colliders[i],colliders[j],type)) continue; //fet collisions_ret.emplace_back(colliders[i], colliders[j]); } @@ -156,8 +156,8 @@ CollisionSystem::get_collider_type(const collider_variant & collider1, } } -bool CollisionSystem::get_collision(const CollisionInternal & self,const CollisionInternal & other,const CollisionInternalType & type) const { - +bool CollisionSystem::detect_collision(CollisionInternal & self,CollisionInternal & other,const CollisionInternalType & type) { + vec2 resolution; switch (type) { case CollisionInternalType::BOX_BOX: { const BoxColliderInternal BOX1 = { @@ -170,8 +170,10 @@ bool CollisionSystem::get_collision(const CollisionInternal & self,const Collisi .transform = other.info.transform, .rigidbody = other.info.rigidbody }; - - return this->get_box_box_collision(BOX1, BOX2); + resolution = this->get_box_box_detection(BOX1, BOX2); + if(resolution == vec2{-1,-1}) return false; + break; + } case CollisionInternalType::BOX_CIRCLE: { const BoxColliderInternal BOX1 = { @@ -184,7 +186,9 @@ bool CollisionSystem::get_collision(const CollisionInternal & self,const Collisi .transform = other.info.transform, .rigidbody = other.info.rigidbody }; - return this->get_box_circle_collision(BOX1, CIRCLE2); + resolution = this->get_box_circle_detection(BOX1, CIRCLE2); + if(resolution == vec2{-1,-1}) return false; + break; } case CollisionInternalType::CIRCLE_CIRCLE: { const CircleColliderInternal CIRCLE1 = { @@ -197,7 +201,9 @@ bool CollisionSystem::get_collision(const CollisionInternal & self,const Collisi .transform = other.info.transform, .rigidbody = other.info.rigidbody }; - return this->get_circle_circle_collision(CIRCLE1,CIRCLE2); + resolution = this->get_circle_circle_detection(CIRCLE1,CIRCLE2); + if(resolution == vec2{-1,-1}) return false; + break; } case CollisionInternalType::CIRCLE_BOX: { const CircleColliderInternal CIRCLE1 = { @@ -210,22 +216,31 @@ bool CollisionSystem::get_collision(const CollisionInternal & self,const Collisi .transform = other.info.transform, .rigidbody = other.info.rigidbody }; - return this->get_box_circle_collision(BOX2, CIRCLE1); + resolution = this->get_box_circle_detection(BOX2, CIRCLE1); + if(resolution == vec2{-1,-1}) return false; + break; } case CollisionInternalType::NONE: break; } - return false; + self.resolution = resolution; + self.resolution_direction = this->resolution_correction(self.resolution, self.info.rigidbody.data); + other.resolution = -self.resolution; + other.resolution_direction = self.resolution_direction; + return true; } -bool CollisionSystem::get_box_box_collision(const BoxColliderInternal & box1, const BoxColliderInternal & box2) const { +vec2 CollisionSystem::get_box_box_detection(const BoxColliderInternal & box1, const BoxColliderInternal & box2) const { + vec2 resolution; // Get current positions of colliders - vec2 final_position1 = AbsolutePosition::get_position(box1.transform, box1.collider.offset); - vec2 final_position2 = AbsolutePosition::get_position(box2.transform, box2.collider.offset); + vec2 pos1 = AbsolutePosition::get_position(box1.transform, box1.collider.offset); + vec2 pos2 = AbsolutePosition::get_position(box2.transform, box2.collider.offset); // Scale dimensions vec2 scaled_box1 = box1.collider.dimensions * box1.transform.scale; vec2 scaled_box2 = box2.collider.dimensions * box2.transform.scale; + vec2 delta = pos2 - pos1; + // Calculate half-extents (half width and half height) float half_width1 = scaled_box1.x / 2.0; @@ -233,42 +248,77 @@ bool CollisionSystem::get_box_box_collision(const BoxColliderInternal & box1, co float half_width2 = scaled_box2.x / 2.0; float half_height2 = scaled_box2.y / 2.0; - // Check if the boxes overlap along the X and Y axes - return (final_position1.x + half_width1 > final_position2.x - half_width2 - && final_position1.x - half_width1 < final_position2.x + half_width2 - && final_position1.y + half_height1 > final_position2.y - half_height2 - && final_position1.y - half_height1 < final_position2.y + half_height2); + if (pos1.x + half_width1 > pos2.x - half_width2 + && pos1.x - half_width1 < pos2.x + half_width2 + && pos1.y + half_height1 > pos2.y - half_height2 + && pos1.y - half_height1 < pos2.y + half_height2) + { + float overlap_x = (half_width1 + half_width2) - std::abs(delta.x); + float overlap_y = (half_height1 + half_height2) - std::abs(delta.y); + if (overlap_x > 0 && overlap_y > 0) { + // Determine the direction of resolution + if (overlap_x < overlap_y) { + // Resolve along the X-axis (smallest overlap) + resolution.x = (delta.x > 0) ? -overlap_x : overlap_x; + } else if (overlap_y < overlap_x) { + // Resolve along the Y-axis (smallest overlap) + resolution.y = (delta.y > 0) ? -overlap_y : overlap_y; + } else { + // Equal overlap, resolve both directions with preference + resolution.x = (delta.x > 0) ? -overlap_x : overlap_x; + resolution.y = (delta.y > 0) ? -overlap_y : overlap_y; + } + } + return resolution; + } + return vec2{-1,-1}; } -bool CollisionSystem::get_box_circle_collision(const BoxColliderInternal & box1, const CircleColliderInternal & circle2) const { - // Get current positions of colliders - vec2 final_position1 = AbsolutePosition::get_position(box1.transform, box1.collider.offset); - vec2 final_position2 = AbsolutePosition::get_position(circle2.transform, circle2.collider.offset); - - // Scale dimensions - vec2 scaled_box = box1.collider.dimensions * box1.transform.scale; - float scaled_circle = circle2.collider.radius * circle2.transform.scale; - - // Calculate box half-extents - float half_width = scaled_box.x / 2.0; - float half_height = scaled_box.y / 2.0; - - // Find the closest point on the box to the circle's center - float closest_x = std::max(final_position1.x - half_width, - std::min(final_position2.x, final_position1.x + half_width)); - float closest_y = std::max(final_position1.y - half_height, - std::min(final_position2.y, final_position1.y + half_height)); - - // Calculate the distance squared between the circle's center and the closest point on the box - float distance_x = final_position2.x - closest_x; - float distance_y = final_position2.y - closest_y; - float distance_squared = distance_x * distance_x + distance_y * distance_y; - - // Compare distance squared with the square of the circle's radius - return distance_squared < scaled_circle * scaled_circle; +vec2 CollisionSystem::get_box_circle_detection(const BoxColliderInternal & box, const CircleColliderInternal & circle) const { + /// Get current positions of colliders + vec2 box_pos = AbsolutePosition::get_position(box.transform, box.collider.offset); + vec2 circle_pos = AbsolutePosition::get_position(circle.transform, circle.collider.offset); + + // Scale dimensions + vec2 scaled_box = box.collider.dimensions * box.transform.scale; + float scaled_circle_radius = circle.collider.radius * circle.transform.scale; + + // Calculate box half-extents + float half_width = scaled_box.x / 2.0f; + float half_height = scaled_box.y / 2.0f; + + // Find the closest point on the box to the circle's center + float closest_x = std::max(box_pos.x - half_width, std::min(circle_pos.x, box_pos.x + half_width)); + float closest_y = std::max(box_pos.y - half_height, std::min(circle_pos.y, box_pos.y + half_height)); + + float distance_x = circle_pos.x - closest_x; + float distance_y = circle_pos.y - closest_y; + float distance_squared = distance_x * distance_x + distance_y * distance_y; + if(distance_squared < scaled_circle_radius * scaled_circle_radius){ + vec2 delta = circle_pos - box_pos; + + // Clamp circle center to the nearest point on the box + vec2 closest_point; + closest_point.x = std::clamp(delta.x, -half_width, half_width); + closest_point.y = std::clamp(delta.y, -half_height, half_height); + + // Find the vector from the circle center to the closest point + vec2 closest_delta = delta - closest_point; + + float distance = std::sqrt(closest_delta.x * closest_delta.x + closest_delta.y * closest_delta.y); + vec2 collision_normal = closest_delta / distance; + + // Compute penetration depth + float penetration_depth = scaled_circle_radius - distance; + + // Compute the resolution vector + return vec2{collision_normal * penetration_depth}; + } + // No collision + return vec2{-1,-1}; } -bool CollisionSystem::get_circle_circle_collision(const CircleColliderInternal & circle1, const CircleColliderInternal & circle2) const { +vec2 CollisionSystem::get_circle_circle_detection(const CircleColliderInternal & circle1, const CircleColliderInternal & circle2) const { // Get current positions of colliders vec2 final_position1 = AbsolutePosition::get_position(circle1.transform, circle1.collider.offset); vec2 final_position2 = AbsolutePosition::get_position(circle2.transform, circle2.collider.offset); @@ -284,86 +334,35 @@ bool CollisionSystem::get_circle_circle_collision(const CircleColliderInternal & // Calculate the sum of the radii float radius_sum = scaled_circle1 + scaled_circle2; - // Check if the distance between the centers is less than or equal to the sum of the radii - return distance_squared < radius_sum * radius_sum; -} + // Check for collision (distance squared must be less than the square of the radius sum) + if (distance_squared < radius_sum * radius_sum) { + vec2 delta = final_position2 - final_position1; -std::pair<vec2, CollisionSystem::Direction> -CollisionSystem::get_collision_resolution(const CollisionInternal & self, const CollisionInternal & other, - const CollisionInternalType & type) const { - vec2 resolution; - // Fet resolution form correct type - switch (type) { - case CollisionInternalType::BOX_BOX: { + // Compute the distance between the two circle centers + float distance = std::sqrt(delta.x * delta.x + delta.y * delta.y); - const BoxColliderInternal BOX1 = { - .collider = std::get<std::reference_wrapper<BoxCollider>>(self.collider), - .transform = self.info.transform, - .rigidbody = self.info.rigidbody - }; - const BoxColliderInternal BOX2 = { - .collider = std::get<std::reference_wrapper<BoxCollider>>(other.collider), - .transform = other.info.transform, - .rigidbody = other.info.rigidbody - }; - resolution = this->get_box_box_resolution(BOX1, BOX2); - break; - } - case CollisionInternalType::BOX_CIRCLE: { + // Compute the combined radii of the two circles + float combined_radius = scaled_circle1 + scaled_circle2; - const BoxColliderInternal BOX1 = { - .collider = std::get<std::reference_wrapper<BoxCollider>>(self.collider), - .transform = self.info.transform, - .rigidbody = self.info.rigidbody - }; - const CircleColliderInternal CIRCLE1 = { - .collider = std::get<std::reference_wrapper<CircleCollider>>(other.collider), - .transform = other.info.transform, - .rigidbody = other.info.rigidbody - }; - resolution = -this->get_circle_box_resolution(CIRCLE1,BOX1); - break; - } - case CollisionInternalType::CIRCLE_CIRCLE: { - const CircleColliderInternal CIRCLE1 = { - .collider = std::get<std::reference_wrapper<CircleCollider>>(self.collider), - .transform = self.info.transform, - .rigidbody = self.info.rigidbody - }; - const CircleColliderInternal CIRCLE2 = { - .collider = std::get<std::reference_wrapper<CircleCollider>>(other.collider), - .transform = other.info.transform, - .rigidbody = other.info.rigidbody - }; - - resolution = this->get_circle_circle_resolution(CIRCLE1, CIRCLE2); - break; - } - case CollisionInternalType::CIRCLE_BOX: { + // Compute the penetration depth + float penetration_depth = combined_radius - distance; - const BoxColliderInternal BOX1 = { - .collider = std::get<std::reference_wrapper<BoxCollider>>(other.collider), - .transform = other.info.transform, - .rigidbody = other.info.rigidbody - }; - const CircleColliderInternal CIRCLE1 = { - .collider = std::get<std::reference_wrapper<CircleCollider>>(self.collider), - .transform = self.info.transform, - .rigidbody = self.info.rigidbody - }; - resolution = -this->get_circle_box_resolution(CIRCLE1,BOX1); + // Normalize the delta vector to get the collision direction + vec2 collision_normal = delta / distance; - - resolution = this->get_circle_box_resolution(CIRCLE1, BOX1); - break; - } - case CollisionInternalType::NONE: - break; + // Compute the resolution vector + vec2 resolution = -collision_normal * penetration_depth; + + return resolution; } + // No collision + return vec2{-1,-1}; +} + +CollisionSystem::Direction CollisionSystem::resolution_correction(vec2 & resolution,const Rigidbody::Data & rigidbody) { // Calculate the other value to move back correctly // If only X or Y has a value determine what is should be to move back. - const Rigidbody::Data & rigidbody = self.info.rigidbody.data; Direction resolution_direction = Direction::NONE; // If both are not zero a perfect corner has been hit if (resolution.x != 0 && resolution.y != 0) { @@ -381,102 +380,10 @@ CollisionSystem::get_collision_resolution(const CollisionInternal & self, const resolution.x = -rigidbody.linear_velocity.x * (resolution.y / rigidbody.linear_velocity.y); } - return std::make_pair(resolution, resolution_direction); -} - -vec2 CollisionSystem::get_box_box_resolution(const BoxColliderInternal & self, const BoxColliderInternal & other) const { - vec2 resolution; // Default resolution vector - vec2 self_pos = AbsolutePosition::get_position(self.transform, self.collider.offset); - vec2 other_pos = AbsolutePosition::get_position(other.transform, other.collider.offset); - vec2 delta = other_pos - self_pos; - - vec2 scaled_box1 = self.collider.dimensions * self.transform.scale; - vec2 scaled_box2 = other.collider.dimensions * other.transform.scale; - - // Compute half-dimensions of the boxes - float half_width1 = self.collider.dimensions.x / 2.0; - float half_height1 = self.collider.dimensions.y / 2.0; - float half_width2 = other.collider.dimensions.x / 2.0; - float half_height2 = other.collider.dimensions.y / 2.0; - - // Calculate overlaps along X and Y axes - float overlap_x = (half_width1 + half_width2) - std::abs(delta.x); - float overlap_y = (half_height1 + half_height2) - std::abs(delta.y); - - // Check if there is a collision should always be true - if (overlap_x > 0 && overlap_y > 0) { - // Determine the direction of resolution - if (overlap_x < overlap_y) { - // Resolve along the X-axis (smallest overlap) - resolution.x = (delta.x > 0) ? -overlap_x : overlap_x; - } else if (overlap_y < overlap_x) { - // Resolve along the Y-axis (smallest overlap) - resolution.y = (delta.y > 0) ? -overlap_y : overlap_y; - } else { - // Equal overlap, resolve both directions with preference - resolution.x = (delta.x > 0) ? -overlap_x : overlap_x; - resolution.y = (delta.y > 0) ? -overlap_y : overlap_y; - } - } - - return resolution; -} - -vec2 CollisionSystem::get_circle_circle_resolution(const CircleColliderInternal & self, const CircleColliderInternal & other) const { - vec2 self_pos = AbsolutePosition::get_position(self.transform, self.collider.offset); - vec2 other_pos = AbsolutePosition::get_position(other.transform, other.collider.offset); - vec2 delta = other_pos - self_pos; - - // Compute the distance between the two circle centers - float distance = std::sqrt(delta.x * delta.x + delta.y * delta.y); - - // Compute the combined radii of the two circles - float combined_radius = self.collider.radius + other.collider.radius; - - // Compute the penetration depth - float penetration_depth = combined_radius - distance; - - // Normalize the delta vector to get the collision direction - vec2 collision_normal = delta / distance; - - // Compute the resolution vector - vec2 resolution = -collision_normal * penetration_depth; - - return resolution; -} - -vec2 CollisionSystem::get_circle_box_resolution(const CircleColliderInternal & circle, const BoxColliderInternal & box) const { - vec2 self_pos = AbsolutePosition::get_position(box.transform, box.collider.offset); - vec2 other_pos = AbsolutePosition::get_position(circle.transform, circle.collider.offset); - vec2 delta = other_pos - self_pos; - - // Compute half-dimensions of the box - float half_width = box.collider.dimensions.x / 2.0f; - float half_height = box.collider.dimensions.y / 2.0f; - - // Clamp circle center to the nearest point on the box - vec2 closest_point; - closest_point.x = std::clamp(delta.x, -half_width, half_width); - closest_point.y = std::clamp(delta.y, -half_height, half_height); - - // Find the vector from the circle center to the closest point - vec2 closest_delta = delta - closest_point; - - // Normalize the delta to get the collision direction - float distance - = std::sqrt(closest_delta.x * closest_delta.x + closest_delta.y * closest_delta.y); - vec2 collision_normal = closest_delta / distance; - - // Compute penetration depth - float penetration_depth = circle.collider.radius - distance; - - // Compute the resolution vector - vec2 resolution = collision_normal * penetration_depth; - - return resolution; + return resolution_direction; } -CollisionSystem::CollisionInfo CollisionSystem::get_collision_info(const CollisionInternal & in_self, const CollisionInternal & in_other,const CollisionInternalType & type,const vec2 & resolution,const CollisionSystem::Direction & resolution_direction) const{ +CollisionSystem::CollisionInfo CollisionSystem::get_collision_info(const CollisionInternal & in_self, const CollisionInternal & in_other) const{ ComponentManager & mgr = this->mediator.component_manager; @@ -495,8 +402,8 @@ CollisionSystem::CollisionInfo CollisionSystem::get_collision_info(const Collisi struct CollisionInfo collision_info{ .self = self, .other = other, - .resolution = resolution, - .resolution_direction = resolution_direction, + .resolution = in_self.resolution, + .resolution_direction = in_self.resolution_direction, }; return collision_info; } diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h index a695e61..7792e50 100644 --- a/src/crepe/system/CollisionSystem.h +++ b/src/crepe/system/CollisionSystem.h @@ -123,58 +123,11 @@ private: * \param data1 Collision data for the first collider. * \param data2 Collision data for the second collider. */ - CollisionInfo get_collision_info(const CollisionInternal & this_data, const CollisionInternal & other_data,const CollisionInternalType & type,const vec2 & resolution,const CollisionSystem::Direction & resolution_direction) const; //done + CollisionInfo get_collision_info(const CollisionInternal & this_data, const CollisionInternal & other_data) const; //done - /** - * \brief Resolves collision between two colliders and calculates the movement required. - * - * Determines the displacement and direction needed to separate colliders based on their types. - * - * \param data1 Collision data for the first collider. - * \param data2 Collision data for the second collider. - * \param type The type of collider pair. - * \return A pair containing the resolution vector and direction for the first collider. - */ - std::pair<vec2, Direction> get_collision_resolution(const CollisionInternal & data1,const CollisionInternal & data2, const CollisionInternalType & type) const; //done - - /** - * \brief Calculates the resolution vector for two BoxColliders. - * - * Computes the displacement required to separate two overlapping BoxColliders. - * - * \param box_collider1 The first BoxCollider. - * \param box_collider2 The second BoxCollider. - * \param position1 The position of the first BoxCollider. - * \param position2 The position of the second BoxCollider. - * \return The resolution vector for the collision. - */ - vec2 get_box_box_resolution(const BoxColliderInternal & self, const BoxColliderInternal & other) const; //done - /** - * \brief Calculates the resolution vector for two CircleCollider. - * - * Computes the displacement required to separate two overlapping CircleCollider. - * - * \param circle_collider1 The first CircleCollider. - * \param circle_collider2 The second CircleCollider. - * \param final_position1 The position of the first CircleCollider. - * \param final_position2 The position of the second CircleCollider. - * \return The resolution vector for the collision. - */ - vec2 get_circle_circle_resolution(const CircleColliderInternal & self, const CircleColliderInternal & other) const; //done + Direction resolution_correction(vec2 & resolution,const Rigidbody::Data & rigidbody); - /** - * \brief Calculates the resolution vector for two CircleCollider. - * - * Computes the displacement required to separate two overlapping CircleCollider. - * - * \param circle_collider The first CircleCollider. - * \param box_collider The second CircleCollider. - * \param circle_position The position of the CircleCollider. - * \param box_position The position of the BoxCollider. - * \return The resolution vector for the collision. - */ - vec2 get_circle_box_resolution(const CircleColliderInternal & circle, const BoxColliderInternal & box) const; //done /** * \brief Determines the appropriate collision handler for a collision. @@ -222,7 +175,7 @@ private: * \return A list of collision pairs with their associated data. */ std::vector<std::pair<CollisionInternal, CollisionInternal>> - gather_collisions(const std::vector<CollisionInternal> & colliders) const; //done + gather_collisions(std::vector<CollisionInternal> & colliders); //done /** * \brief Checks if the settings allow collision @@ -249,7 +202,7 @@ private: * \param type The type of collider pair. * \return True if a collision is detected, otherwise false. */ - bool get_collision(const CollisionInternal & first_info, const CollisionInternal & second_info, const CollisionInternalType & type) const; + bool detect_collision(CollisionInternal & first_info, CollisionInternal & second_info, const CollisionInternalType & type); /** * \brief Detects collisions between two BoxColliders. @@ -262,7 +215,7 @@ private: * \param rigidbody2 Rigidbody of the second object. * \return True if a collision is detected, otherwise false. */ - bool get_box_box_collision(const BoxColliderInternal & box1, const BoxColliderInternal & box2) const; + vec2 get_box_box_detection(const BoxColliderInternal & box1, const BoxColliderInternal & box2) const; /** * \brief Check collision for box on circle collider @@ -275,7 +228,7 @@ private: * \param rigidbody2 Rigidbody of the second object. * \return True if a collision is detected, otherwise false. */ - bool get_box_circle_collision(const BoxColliderInternal & box1, const CircleColliderInternal & circle2) const; + vec2 get_box_circle_detection(const BoxColliderInternal & box1, const CircleColliderInternal & circle2) const; /** * \brief Check collision for circle on circle collider @@ -290,7 +243,7 @@ private: * * \return status of collision */ - bool get_circle_circle_collision(const CircleColliderInternal & circle1, const CircleColliderInternal & circle2) const; + vec2 get_circle_circle_detection(const CircleColliderInternal & circle1, const CircleColliderInternal & circle2) const; }; /** |