aboutsummaryrefslogtreecommitdiff
path: root/src/crepe/system/CollisionSystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/crepe/system/CollisionSystem.cpp')
-rw-r--r--src/crepe/system/CollisionSystem.cpp889
1 files changed, 459 insertions, 430 deletions
diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp
index af8adce..571ac70 100644
--- a/src/crepe/system/CollisionSystem.cpp
+++ b/src/crepe/system/CollisionSystem.cpp
@@ -1,6 +1,7 @@
#include <algorithm>
#include <cmath>
#include <cstddef>
+#include <emmintrin.h>
#include <functional>
#include <optional>
#include <utility>
@@ -15,15 +16,25 @@
#include "api/Rigidbody.h"
#include "api/Transform.h"
#include "api/Vector2.h"
+#include "util/AbsolutePosition.h"
+#include "util/OptionalRef.h"
-#include "Collider.h"
#include "CollisionSystem.h"
#include "types.h"
-#include "util/OptionalRef.h"
using namespace crepe;
+using enum Rigidbody::BodyType;
+
+CollisionSystem::CollisionInfo CollisionSystem::CollisionInfo::operator-() const {
+ return {
+ .self = this->other,
+ .other = this->self,
+ .resolution = -this->resolution,
+ .resolution_direction = this->resolution_direction,
+ };
+}
-void CollisionSystem::update() {
+void CollisionSystem::fixed_update() {
std::vector<CollisionInternal> all_colliders;
game_object_id_t id = 0;
ComponentManager & mgr = this->mediator.component_manager;
@@ -33,15 +44,17 @@ void CollisionSystem::update() {
if (!rigidbody.active) continue;
id = rigidbody.game_object_id;
Transform & transform = mgr.get_components_by_id<Transform>(id).front().get();
+ Metadata & metadata = mgr.get_components_by_id<Metadata>(id).front().get();
// Check if the boxcollider is active and has the same id as the rigidbody.
RefVector<BoxCollider> boxcolliders = mgr.get_components_by_type<BoxCollider>();
for (BoxCollider & boxcollider : boxcolliders) {
if (boxcollider.game_object_id != id) continue;
if (!boxcollider.active) continue;
- all_colliders.push_back({.id = id,
- .collider = collider_variant{boxcollider},
- .transform = transform,
- .rigidbody = rigidbody});
+ all_colliders.push_back(
+ {.id = id,
+ .collider = collider_variant {boxcollider},
+ .info = {transform, rigidbody, metadata}}
+ );
}
// Check if the circlecollider is active and has the same id as the rigidbody.
RefVector<CircleCollider> circlecolliders
@@ -49,312 +62,461 @@ void CollisionSystem::update() {
for (CircleCollider & circlecollider : circlecolliders) {
if (circlecollider.game_object_id != id) continue;
if (!circlecollider.active) continue;
- all_colliders.push_back({.id = id,
- .collider = collider_variant{circlecollider},
- .transform = transform,
- .rigidbody = rigidbody});
+ all_colliders.push_back(
+ {.id = id,
+ .collider = collider_variant {circlecollider},
+ .info = {transform, rigidbody, metadata}}
+ );
}
}
- // Check between all colliders if there is a collision
+ // Check between all colliders if there is a collision (collision handling)
std::vector<std::pair<CollisionInternal, CollisionInternal>> collided
= this->gather_collisions(all_colliders);
- // For both objects call the collision handler
+ // For the object convert the info and call the collision handler if needed
for (auto & collision_pair : collided) {
- this->collision_handler_request(collision_pair.first, collision_pair.second);
- this->collision_handler_request(collision_pair.second, collision_pair.first);
+ // Convert internal struct to external struct
+ 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);
}
}
-void CollisionSystem::collision_handler_request(CollisionInternal & this_data,
- CollisionInternal & other_data) {
+// Below is for collision detection
+std::vector<std::pair<CollisionSystem::CollisionInternal, CollisionSystem::CollisionInternal>>
+CollisionSystem::gather_collisions(std::vector<CollisionInternal> & colliders) {
- CollisionInternalType type
- = this->get_collider_type(this_data.collider, other_data.collider);
- std::pair<vec2, CollisionSystem::Direction> resolution_data
- = this->collision_handler(this_data, other_data, type);
- ComponentManager & mgr = this->mediator.component_manager;
- OptionalRef<Metadata> this_metadata
- = mgr.get_components_by_id<Metadata>(this_data.id).front().get();
- OptionalRef<Metadata> other_metadata
- = mgr.get_components_by_id<Metadata>(other_data.id).front().get();
- OptionalRef<Collider> this_collider;
- OptionalRef<Collider> other_collider;
- switch (type) {
- case CollisionInternalType::BOX_BOX: {
- this_collider = std::get<std::reference_wrapper<BoxCollider>>(this_data.collider);
- other_collider
- = std::get<std::reference_wrapper<BoxCollider>>(other_data.collider);
- break;
- }
- case CollisionInternalType::BOX_CIRCLE: {
- this_collider = std::get<std::reference_wrapper<BoxCollider>>(this_data.collider);
- other_collider
- = std::get<std::reference_wrapper<CircleCollider>>(other_data.collider);
- break;
- }
- case CollisionInternalType::CIRCLE_BOX: {
- this_collider
- = std::get<std::reference_wrapper<CircleCollider>>(this_data.collider);
- other_collider
- = std::get<std::reference_wrapper<BoxCollider>>(other_data.collider);
- break;
- }
- case CollisionInternalType::CIRCLE_CIRCLE: {
- this_collider
- = std::get<std::reference_wrapper<CircleCollider>>(this_data.collider);
- other_collider
- = std::get<std::reference_wrapper<CircleCollider>>(other_data.collider);
- break;
+ // TODO:
+ // If no colliders skip
+ // Check if colliders has rigidbody if not skip
+
+ // TODO:
+ // If amount is higer than lets say 16 for now use quadtree otwerwise skip
+ // Quadtree code
+ // Quadtree is placed over the input vector
+
+ // Return data of collided colliders which are variants
+ std::vector<std::pair<CollisionInternal, CollisionInternal>> collisions_ret;
+ //using visit to visit the variant to access the active and id.
+ for (size_t i = 0; i < colliders.size(); ++i) {
+ for (size_t j = i + 1; j < colliders.size(); ++j) {
+ 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 (!detect_collision(colliders[i], colliders[j], type)) continue;
+ //fet
+ collisions_ret.emplace_back(colliders[i], colliders[j]);
}
}
+ return collisions_ret;
+}
- // collision info
- crepe::CollisionSystem::CollisionInfo collision_info{
- .this_collider = this_collider,
- .this_transform = this_data.transform,
- .this_rigidbody = this_data.rigidbody,
- .this_metadata = this_metadata,
- .other_collider = other_collider,
- .other_transform = other_data.transform,
- .other_rigidbody = other_data.rigidbody,
- .other_metadata = other_metadata,
- .resolution = resolution_data.first,
- .resolution_direction = resolution_data.second,
- };
+bool CollisionSystem::should_collide(
+ const CollisionInternal & self, const CollisionInternal & other
+) const {
+
+ const Rigidbody::Data & self_rigidbody = self.info.rigidbody.data;
+ const Rigidbody::Data & other_rigidbody = other.info.rigidbody.data;
+ const Metadata & self_metadata = self.info.metadata;
+ const Metadata & other_metadata = other.info.metadata;
+
+ // Check collision layers
+ if (self_rigidbody.collision_layers.contains(other_rigidbody.collision_layer)) return true;
+ if (other_rigidbody.collision_layers.contains(self_rigidbody.collision_layer)) return true;
+
+ // Check names
+ if (self_rigidbody.collision_names.contains(other_metadata.name)) return true;
+ if (other_rigidbody.collision_names.contains(self_metadata.name)) return true;
+
+ // Check tags
+ if (self_rigidbody.collision_tags.contains(other_metadata.tag)) return true;
+ if (other_rigidbody.collision_tags.contains(self_metadata.tag)) return true;
- // Determine if static needs to be called
- this->determine_collision_handler(collision_info);
+ return false;
}
-std::pair<vec2, CollisionSystem::Direction>
-CollisionSystem::collision_handler(CollisionInternal & data1, CollisionInternal & data2,
- CollisionInternalType type) {
+CollisionSystem::CollisionInternalType CollisionSystem::get_collider_type(
+ const collider_variant & collider1, const collider_variant & collider2
+) const {
+ if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider1)) {
+ if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) {
+ return CollisionInternalType::CIRCLE_CIRCLE;
+ } else {
+ return CollisionInternalType::CIRCLE_BOX;
+ }
+ } else {
+ if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) {
+ return CollisionInternalType::BOX_CIRCLE;
+ } else {
+ return CollisionInternalType::BOX_BOX;
+ }
+ }
+}
+
+bool CollisionSystem::detect_collision(
+ CollisionInternal & self, CollisionInternal & other, const CollisionInternalType & type
+) {
vec2 resolution;
switch (type) {
case CollisionInternalType::BOX_BOX: {
- const BoxCollider & collider1
- = std::get<std::reference_wrapper<BoxCollider>>(data1.collider);
- const BoxCollider & collider2
- = std::get<std::reference_wrapper<BoxCollider>>(data2.collider);
- vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform,
- data1.rigidbody);
- vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform,
- data2.rigidbody);
- resolution = this->get_box_box_resolution(collider1, collider2, collider_pos1,
- collider_pos2);
+ // Box-Box collision detection
+ 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};
+ // Get resolution vector from box-box collision detection
+ resolution = this->get_box_box_detection(BOX1, BOX2);
+ // If no collision (NaN values), return false
+ if (resolution.is_nan()) return false;
break;
}
case CollisionInternalType::BOX_CIRCLE: {
- const BoxCollider & collider1
- = std::get<std::reference_wrapper<BoxCollider>>(data1.collider);
- const CircleCollider & collider2
- = std::get<std::reference_wrapper<CircleCollider>>(data2.collider);
- vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform,
- data1.rigidbody);
- vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform,
- data2.rigidbody);
- resolution = -this->get_circle_box_resolution(collider2, collider1, collider_pos2,
- collider_pos1);
+ // Box-Circle collision detection
+ const BoxColliderInternal BOX1
+ = {.collider = std::get<std::reference_wrapper<BoxCollider>>(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};
+ // Get resolution vector from box-circle collision detection
+ resolution = this->get_box_circle_detection(BOX1, CIRCLE2);
+ // If no collision (NaN values), return false
+ if (resolution.is_nan()) return false;
+ // Invert the resolution vector for proper collision response
+ resolution = -resolution;
break;
}
case CollisionInternalType::CIRCLE_CIRCLE: {
- const CircleCollider & collider1
- = std::get<std::reference_wrapper<CircleCollider>>(data1.collider);
- const CircleCollider & collider2
- = std::get<std::reference_wrapper<CircleCollider>>(data2.collider);
- vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform,
- data1.rigidbody);
- vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform,
- data2.rigidbody);
- resolution = this->get_circle_circle_resolution(collider1, collider2,
- collider_pos1, collider_pos2);
+ // Circle-Circle collision detection
+ 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};
+ // Get resolution vector from circle-circle collision detection
+ resolution = this->get_circle_circle_detection(CIRCLE1, CIRCLE2);
+ // If no collision (NaN values), return false
+ if (resolution.is_nan()) return false;
break;
}
case CollisionInternalType::CIRCLE_BOX: {
- const CircleCollider & collider1
- = std::get<std::reference_wrapper<CircleCollider>>(data1.collider);
- const BoxCollider & collider2
- = std::get<std::reference_wrapper<BoxCollider>>(data2.collider);
- vec2 collider_pos1 = this->get_current_position(collider1.offset, data1.transform,
- data1.rigidbody);
- vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform,
- data2.rigidbody);
- resolution = this->get_circle_box_resolution(collider1, collider2, collider_pos1,
- collider_pos2);
+ // Circle-Box collision detection
+ const CircleColliderInternal CIRCLE1
+ = {.collider = std::get<std::reference_wrapper<CircleCollider>>(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};
+ // Get resolution vector from box-circle collision detection (order swapped)
+ resolution = this->get_box_circle_detection(BOX2, CIRCLE1);
+ // If no collision (NaN values), return false
+ if (resolution.is_nan()) return false;
break;
}
+ case CollisionInternalType::NONE:
+ // No collision detection needed if the type is NONE
+ return false;
+ break;
}
+ // Store the calculated resolution vector for the 'self' collider
+ self.resolution = resolution;
+ // Calculate the resolution direction based on the rigidbody data
+ self.resolution_direction
+ = this->resolution_correction(self.resolution, self.info.rigidbody.data);
+ // For the 'other' collider, the resolution is the opposite direction of 'self'
+ other.resolution = -self.resolution;
+ other.resolution_direction = self.resolution_direction;
+
+ // Return true if a collision was detected and resolution was calculated
+ return true;
+}
- Direction resolution_direction = Direction::NONE;
- if (resolution.x != 0 && resolution.y != 0) {
- resolution_direction = Direction::BOTH;
- } else if (resolution.x != 0) {
- resolution_direction = Direction::X_DIRECTION;
- //checks if the other velocity has a value and if this object moved
- if (data1.rigidbody.data.linear_velocity.x != 0
- && data1.rigidbody.data.linear_velocity.y != 0)
- resolution.y = -data1.rigidbody.data.linear_velocity.y
- * (resolution.x / data1.rigidbody.data.linear_velocity.x);
- } else if (resolution.y != 0) {
- resolution_direction = Direction::Y_DIRECTION;
- //checks if the other velocity has a value and if this object moved
- if (data1.rigidbody.data.linear_velocity.x != 0
- && data1.rigidbody.data.linear_velocity.y != 0)
- resolution.x = -data1.rigidbody.data.linear_velocity.x
- * (resolution.y / data1.rigidbody.data.linear_velocity.y);
- }
+vec2 CollisionSystem::get_box_box_detection(
+ const BoxColliderInternal & box1, const BoxColliderInternal & box2
+) const {
+ vec2 resolution {NAN, NAN};
+ // Get current positions of colliders
+ vec2 pos1 = AbsolutePosition::get_position(box1.transform, box1.collider.offset);
+ vec2 pos2 = AbsolutePosition::get_position(box2.transform, box2.collider.offset);
- return std::make_pair(resolution, resolution_direction);
-}
+ // Scale dimensions
+ vec2 scaled_box1 = box1.collider.dimensions * box1.transform.scale;
+ vec2 scaled_box2 = box2.collider.dimensions * box2.transform.scale;
+ vec2 delta = pos2 - pos1;
-vec2 CollisionSystem::get_box_box_resolution(const BoxCollider & box_collider1,
- const BoxCollider & box_collider2,
- const vec2 & final_position1,
- const vec2 & final_position2) const {
- vec2 resolution; // Default resolution vector
- vec2 delta = final_position2 - final_position1;
-
- // Compute half-dimensions of the boxes
- float half_width1 = box_collider1.dimensions.x / 2.0;
- float half_height1 = box_collider1.dimensions.y / 2.0;
- float half_width2 = box_collider2.dimensions.x / 2.0;
- float half_height2 = box_collider2.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;
+ // Calculate half-extents (half width and half height)
+ float half_width1 = scaled_box1.x / 2.0;
+ float half_height1 = scaled_box1.y / 2.0;
+ float half_width2 = scaled_box2.x / 2.0;
+ float half_height2 = scaled_box2.y / 2.0;
+
+ 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) {
+ resolution = {0, 0};
+ 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;
}
-vec2 CollisionSystem::get_circle_circle_resolution(const CircleCollider & circle_collider1,
- const CircleCollider & circle_collider2,
- const vec2 & final_position1,
- const vec2 & final_position2) const {
- vec2 delta = final_position2 - final_position1;
+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);
- // Compute the distance between the two circle centers
- float distance = std::sqrt(delta.x * delta.x + delta.y * delta.y);
+ // Scale dimensions
+ vec2 scaled_box = box.collider.dimensions * box.transform.scale;
+ float scaled_circle_radius = circle.collider.radius * circle.transform.scale;
- // Compute the combined radii of the two circles
- float combined_radius = circle_collider1.radius + circle_collider2.radius;
+ // Calculate box half-extents
+ float half_width = scaled_box.x / 2.0f;
+ float half_height = scaled_box.y / 2.0f;
- // Compute the penetration depth
- float penetration_depth = combined_radius - distance;
+ // 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));
- // Normalize the delta vector to get the collision direction
- vec2 collision_normal = delta / distance;
+ 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;
- // Compute the resolution vector
- vec2 resolution = -collision_normal * penetration_depth;
+ // 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);
- return resolution;
+ // 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 {NAN, NAN};
}
-vec2 CollisionSystem::get_circle_box_resolution(const CircleCollider & circle_collider,
- const BoxCollider & box_collider,
- const vec2 & circle_position,
- const vec2 & box_position) const {
- vec2 delta = circle_position - box_position;
+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);
- // Compute half-dimensions of the box
- float half_width = box_collider.dimensions.x / 2.0f;
- float half_height = box_collider.dimensions.y / 2.0f;
+ // Scale dimensions
+ float scaled_circle1 = circle1.collider.radius * circle1.transform.scale;
+ float scaled_circle2 = circle2.collider.radius * circle2.transform.scale;
- // 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);
+ float distance_x = final_position1.x - final_position2.x;
+ float distance_y = final_position1.y - final_position2.y;
+ float distance_squared = distance_x * distance_x + distance_y * distance_y;
+
+ // Calculate the sum of the radii
+ float radius_sum = scaled_circle1 + scaled_circle2;
- // Find the vector from the circle center to the closest point
- vec2 closest_delta = delta - closest_point;
+ // 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;
- // 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 the distance between the two circle centers
+ float distance = std::sqrt(delta.x * delta.x + delta.y * delta.y);
- // Compute penetration depth
- float penetration_depth = circle_collider.radius - distance;
+ // Compute the combined radii of the two circles
+ float combined_radius = scaled_circle1 + scaled_circle2;
- // Compute the resolution vector
- vec2 resolution = collision_normal * penetration_depth;
+ // Compute the penetration depth
+ float penetration_depth = combined_radius - distance;
- return resolution;
+ // 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;
+ }
+ // No collision
+ return vec2 {NAN, NAN};
+ ;
+}
+
+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.
+ Direction resolution_direction = Direction::NONE;
+ // If both are not zero a perfect corner has been hit
+ if (resolution.x != 0 && resolution.y != 0) {
+ resolution_direction = Direction::BOTH;
+ // If x is not zero a horizontal action was latest action.
+ } else if (resolution.x != 0) {
+ resolution_direction = Direction::X_DIRECTION;
+ // If both are 0 resolution y should not be changed (y_velocity can be 0 by kinematic object movement)
+ if (rigidbody.linear_velocity.x != 0 && rigidbody.linear_velocity.y != 0)
+ resolution.y
+ = -rigidbody.linear_velocity.y * (resolution.x / rigidbody.linear_velocity.x);
+ } else if (resolution.y != 0) {
+ resolution_direction = Direction::Y_DIRECTION;
+ // If both are 0 resolution x should not be changed (x_velocity can be 0 by kinematic object movement)
+ if (rigidbody.linear_velocity.x != 0 && rigidbody.linear_velocity.y != 0)
+ resolution.x
+ = -rigidbody.linear_velocity.x * (resolution.y / rigidbody.linear_velocity.y);
+ }
+
+ return resolution_direction;
}
-void CollisionSystem::determine_collision_handler(CollisionInfo & info) {
- // Check rigidbody type for static
- if (info.this_rigidbody.data.body_type == Rigidbody::BodyType::STATIC) return;
- // If second body is static perform the static collision handler in this system
- if (info.other_rigidbody.data.body_type == Rigidbody::BodyType::STATIC) {
- this->static_collision_handler(info);
+CollisionSystem::CollisionInfo CollisionSystem::get_collision_info(
+ const CollisionInternal & in_self, const CollisionInternal & in_other
+) const {
+
+ crepe::CollisionSystem::ColliderInfo self {
+ .transform = in_self.info.transform,
+ .rigidbody = in_self.info.rigidbody,
+ .metadata = in_self.info.metadata,
};
- // Call collision event for user
- CollisionEvent data(info);
- EventManager & emgr = this->mediator.event_manager;
- emgr.trigger_event<CollisionEvent>(data, info.this_collider.game_object_id);
+
+ crepe::CollisionSystem::ColliderInfo other {
+ .transform = in_other.info.transform,
+ .rigidbody = in_other.info.rigidbody,
+ .metadata = in_other.info.metadata,
+ };
+
+ struct CollisionInfo collision_info {
+ .self = self, .other = other, .resolution = in_self.resolution,
+ .resolution_direction = in_self.resolution_direction,
+ };
+ return collision_info;
}
-void CollisionSystem::static_collision_handler(CollisionInfo & info) {
+void CollisionSystem::determine_collision_handler(const CollisionInfo & info) {
+ Rigidbody::BodyType self_type = info.self.rigidbody.data.body_type;
+ Rigidbody::BodyType other_type = info.other.rigidbody.data.body_type;
+ bool self_kinematic = info.self.rigidbody.data.kinematic_collision;
+ bool other_kinematic = info.other.rigidbody.data.kinematic_collision;
+ // Inverted collision info
+ CollisionInfo inverted = -info;
+ // If both objects are static skip handle call collision script
+ if (self_type == STATIC && other_type == STATIC) return;
+
+ // First body is not dynamic
+ if (self_type != DYNAMIC) {
+ bool static_collision = self_type == STATIC && other_type == DYNAMIC;
+ bool kinematic_collision
+ = self_type == KINEMATIC && other_type == DYNAMIC && self_kinematic;
+
+ // Handle collision
+ if (static_collision || kinematic_collision) this->static_collision_handler(inverted);
+ // Call scripts
+ this->call_collision_events(inverted);
+ return;
+ }
+
+ // Second body is not dynamic
+ if (other_type != DYNAMIC) {
+ bool static_collision = other_type == STATIC;
+ bool kinematic_collision = other_type == KINEMATIC && other_kinematic;
+ // Handle collision
+ if (static_collision || kinematic_collision) this->static_collision_handler(info);
+ // Call scripts
+ this->call_collision_events(info);
+ return;
+ }
+
+ // Dynamic
+ // Handle collision
+ this->dynamic_collision_handler(info);
+ // Call scripts
+ this->call_collision_events(info);
+}
+
+void CollisionSystem::static_collision_handler(const CollisionInfo & info) {
+
+ vec2 & transform_pos = info.self.transform.position;
+ float elasticity = info.self.rigidbody.data.elasticity_coefficient;
+ vec2 & rigidbody_vel = info.self.rigidbody.data.linear_velocity;
+
// Move object back using calculate move back value
- info.this_transform.position += info.resolution;
+ transform_pos += info.resolution;
switch (info.resolution_direction) {
case Direction::BOTH:
//bounce
- if (info.this_rigidbody.data.elastisity_coefficient > 0) {
- info.this_rigidbody.data.linear_velocity
- = -info.this_rigidbody.data.linear_velocity
- * info.this_rigidbody.data.elastisity_coefficient;
+ if (elasticity > 0) {
+ rigidbody_vel = -rigidbody_vel * elasticity;
}
//stop movement
else {
- info.this_rigidbody.data.linear_velocity = {0, 0};
+ rigidbody_vel = {0, 0};
}
break;
case Direction::Y_DIRECTION:
// Bounce
- if (info.this_rigidbody.data.elastisity_coefficient > 0) {
- info.this_rigidbody.data.linear_velocity.y
- = -info.this_rigidbody.data.linear_velocity.y
- * info.this_rigidbody.data.elastisity_coefficient;
+ if (elasticity > 0) {
+ rigidbody_vel.y = -rigidbody_vel.y * elasticity;
}
// Stop movement
else {
- info.this_rigidbody.data.linear_velocity.y = 0;
- info.this_transform.position.x -= info.resolution.x;
+ rigidbody_vel.y = 0;
+ transform_pos.x -= info.resolution.x;
}
break;
case Direction::X_DIRECTION:
// Bounce
- if (info.this_rigidbody.data.elastisity_coefficient > 0) {
- info.this_rigidbody.data.linear_velocity.x
- = -info.this_rigidbody.data.linear_velocity.x
- * info.this_rigidbody.data.elastisity_coefficient;
+ if (elasticity > 0) {
+ rigidbody_vel.x = -rigidbody_vel.x * elasticity;
}
// Stop movement
else {
- info.this_rigidbody.data.linear_velocity.x = 0;
- info.this_transform.position.y -= info.resolution.y;
+ rigidbody_vel.x = 0;
+ transform_pos.y -= info.resolution.y;
}
break;
case Direction::NONE:
@@ -363,213 +525,80 @@ void CollisionSystem::static_collision_handler(CollisionInfo & info) {
}
}
-std::vector<std::pair<CollisionSystem::CollisionInternal, CollisionSystem::CollisionInternal>>
-CollisionSystem::gather_collisions(std::vector<CollisionInternal> & colliders) {
+void CollisionSystem::dynamic_collision_handler(const CollisionInfo & info) {
- // TODO:
- // If no colliders skip
- // Check if colliders has rigidbody if not skip
+ vec2 & self_transform_pos = info.self.transform.position;
+ vec2 & other_transform_pos = info.other.transform.position;
+ float self_elasticity = info.self.rigidbody.data.elasticity_coefficient;
+ float other_elasticity = info.other.rigidbody.data.elasticity_coefficient;
+ vec2 & self_rigidbody_vel = info.self.rigidbody.data.linear_velocity;
+ vec2 & other_rigidbody_vel = info.other.rigidbody.data.linear_velocity;
- // TODO:
- // If amount is higer than lets say 16 for now use quadtree otwerwise skip
- // Quadtree code
- // Quadtree is placed over the input vector
+ self_transform_pos += info.resolution / 2;
+ other_transform_pos += -(info.resolution / 2);
- // Return data of collided colliders which are variants
- std::vector<std::pair<CollisionInternal, CollisionInternal>> collisions_ret;
- //using visit to visit the variant to access the active and id.
- for (size_t i = 0; i < colliders.size(); ++i) {
- for (size_t j = i + 1; j < colliders.size(); ++j) {
- if (colliders[i].id == colliders[j].id) continue;
- if (!have_common_layer(colliders[i].rigidbody.data.collision_layers,
- colliders[j].rigidbody.data.collision_layers))
- continue;
- CollisionInternalType type
- = get_collider_type(colliders[i].collider, colliders[j].collider);
- if (!get_collision(
- {
- .collider = colliders[i].collider,
- .transform = colliders[i].transform,
- .rigidbody = colliders[i].rigidbody,
- },
- {
- .collider = colliders[j].collider,
- .transform = colliders[j].transform,
- .rigidbody = colliders[j].rigidbody,
- },
- type))
- continue;
- collisions_ret.emplace_back(colliders[i], colliders[j]);
- }
- }
-
- return collisions_ret;
-}
-
-bool CollisionSystem::have_common_layer(const std::set<int> & layers1,
- const std::set<int> & layers2) {
+ switch (info.resolution_direction) {
+ case Direction::BOTH:
+ if (self_elasticity > 0) {
+ self_rigidbody_vel = -self_rigidbody_vel * self_elasticity;
+ } else {
+ self_rigidbody_vel = {0, 0};
+ }
- // Check if any number is equal in the layers
- for (int num : layers1) {
- if (layers2.contains(num)) {
- // Common layer found
- return true;
+ if (other_elasticity > 0) {
+ other_rigidbody_vel = -other_rigidbody_vel * other_elasticity;
+ } else {
+ other_rigidbody_vel = {0, 0};
+ }
break;
- }
- }
- // No common layer found
- return false;
-}
+ case Direction::Y_DIRECTION:
+ if (self_elasticity > 0) {
+ self_rigidbody_vel.y = -self_rigidbody_vel.y * self_elasticity;
+ }
+ // Stop movement
+ else {
+ self_rigidbody_vel.y = 0;
+ self_transform_pos.x -= info.resolution.x;
+ }
-CollisionSystem::CollisionInternalType
-CollisionSystem::get_collider_type(const collider_variant & collider1,
- const collider_variant & collider2) const {
- if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider1)) {
- if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) {
- return CollisionInternalType::CIRCLE_CIRCLE;
- } else {
- return CollisionInternalType::CIRCLE_BOX;
- }
- } else {
- if (std::holds_alternative<std::reference_wrapper<CircleCollider>>(collider2)) {
- return CollisionInternalType::BOX_CIRCLE;
- } else {
- return CollisionInternalType::BOX_BOX;
- }
- }
-}
+ if (other_elasticity > 0) {
+ other_rigidbody_vel.y = -other_rigidbody_vel.y * other_elasticity;
+ }
+ // Stop movement
+ else {
+ other_rigidbody_vel.y = 0;
+ other_transform_pos.x -= info.resolution.x;
+ }
+ break;
+ case Direction::X_DIRECTION:
+ if (self_elasticity > 0) {
+ self_rigidbody_vel.x = -self_rigidbody_vel.x * self_elasticity;
+ }
+ // Stop movement
+ else {
+ self_rigidbody_vel.x = 0;
+ self_transform_pos.y -= info.resolution.y;
+ }
-bool CollisionSystem::get_collision(const CollisionInternal & first_info,
- const CollisionInternal & second_info,
- CollisionInternalType type) const {
- switch (type) {
- case CollisionInternalType::BOX_BOX: {
- const BoxCollider & box_collider1
- = std::get<std::reference_wrapper<BoxCollider>>(first_info.collider);
- const BoxCollider & box_collider2
- = std::get<std::reference_wrapper<BoxCollider>>(second_info.collider);
- return this->get_box_box_collision(box_collider1, box_collider2,
- first_info.transform, second_info.transform,
- second_info.rigidbody, second_info.rigidbody);
- }
- case CollisionInternalType::BOX_CIRCLE: {
- const BoxCollider & box_collider
- = std::get<std::reference_wrapper<BoxCollider>>(first_info.collider);
- const CircleCollider & circle_collider
- = std::get<std::reference_wrapper<CircleCollider>>(second_info.collider);
- return this->get_box_circle_collision(
- box_collider, circle_collider, first_info.transform, second_info.transform,
- second_info.rigidbody, second_info.rigidbody);
- }
- case CollisionInternalType::CIRCLE_CIRCLE: {
- const CircleCollider & circle_collider1
- = std::get<std::reference_wrapper<CircleCollider>>(first_info.collider);
- const CircleCollider & circle_collider2
- = std::get<std::reference_wrapper<CircleCollider>>(second_info.collider);
- return this->get_circle_circle_collision(
- circle_collider1, circle_collider2, first_info.transform,
- second_info.transform, second_info.rigidbody, second_info.rigidbody);
- }
- case CollisionInternalType::CIRCLE_BOX: {
- const CircleCollider & circle_collider
- = std::get<std::reference_wrapper<CircleCollider>>(first_info.collider);
- const BoxCollider & box_collider
- = std::get<std::reference_wrapper<BoxCollider>>(second_info.collider);
- return this->get_box_circle_collision(
- box_collider, circle_collider, first_info.transform, second_info.transform,
- second_info.rigidbody, second_info.rigidbody);
- }
+ if (other_elasticity > 0) {
+ other_rigidbody_vel.x = -other_rigidbody_vel.x * other_elasticity;
+ }
+ // Stop movement
+ else {
+ other_rigidbody_vel.x = 0;
+ other_transform_pos.y -= info.resolution.y;
+ }
+ break;
+ case Direction::NONE:
+ // Not possible
+ break;
}
- return false;
}
-bool CollisionSystem::get_box_box_collision(const BoxCollider & box1, const BoxCollider & box2,
- const Transform & transform1,
- const Transform & transform2,
- const Rigidbody & rigidbody1,
- const Rigidbody & rigidbody2) const {
- // Get current positions of colliders
- vec2 final_position1 = this->get_current_position(box1.offset, transform1, rigidbody1);
- vec2 final_position2 = this->get_current_position(box2.offset, transform2, rigidbody2);
-
- // Calculate half-extents (half width and half height)
- float half_width1 = box1.dimensions.x / 2.0;
- float half_height1 = box1.dimensions.y / 2.0;
- float half_width2 = box2.dimensions.x / 2.0;
- float half_height2 = box2.dimensions.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);
-}
-
-bool CollisionSystem::get_box_circle_collision(const BoxCollider & box1,
- const CircleCollider & circle2,
- const Transform & transform1,
- const Transform & transform2,
- const Rigidbody & rigidbody1,
- const Rigidbody & rigidbody2) const {
- // Get current positions of colliders
- vec2 final_position1 = this->get_current_position(box1.offset, transform1, rigidbody1);
- vec2 final_position2 = this->get_current_position(circle2.offset, transform2, rigidbody2);
-
- // Calculate box half-extents
- float half_width = box1.dimensions.x / 2.0;
- float half_height = box1.dimensions.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 < circle2.radius * circle2.radius;
-}
-
-bool CollisionSystem::get_circle_circle_collision(const CircleCollider & circle1,
- const CircleCollider & circle2,
- const Transform & transform1,
- const Transform & transform2,
- const Rigidbody & rigidbody1,
- const Rigidbody & rigidbody2) const {
- // Get current positions of colliders
- vec2 final_position1 = this->get_current_position(circle1.offset, transform1, rigidbody1);
- vec2 final_position2 = this->get_current_position(circle2.offset, transform2, rigidbody2);
-
- float distance_x = final_position1.x - final_position2.x;
- float distance_y = final_position1.y - final_position2.y;
- float distance_squared = distance_x * distance_x + distance_y * distance_y;
-
- // Calculate the sum of the radii
- float radius_sum = circle1.radius + circle2.radius;
-
- // 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;
-}
-
-vec2 CollisionSystem::get_current_position(const vec2 & collider_offset,
- const Transform & transform,
- const Rigidbody & rigidbody) const {
- // 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;
-
- // Rotate
- float rotated_total_offset_x1
- = total_offset.x * cos(radians1) - total_offset.y * sin(radians1);
- float rotated_total_offset_y1
- = total_offset.x * sin(radians1) + total_offset.y * cos(radians1);
-
- // Final positions considering scaling and rotation
- return (transform.position + vec2(rotated_total_offset_x1, rotated_total_offset_y1));
+void CollisionSystem::call_collision_events(const CollisionInfo & info) {
+ CollisionEvent data(info);
+ CollisionEvent data_inverted(-info);
+ EventManager & emgr = this->mediator.event_manager;
+ emgr.trigger_event<CollisionEvent>(data, info.self.transform.game_object_id);
+ emgr.trigger_event<CollisionEvent>(data_inverted, info.other.transform.game_object_id);
}