aboutsummaryrefslogtreecommitdiff
path: root/src/crepe/system
diff options
context:
space:
mode:
Diffstat (limited to 'src/crepe/system')
-rw-r--r--src/crepe/system/CollisionSystem.cpp454
-rw-r--r--src/crepe/system/CollisionSystem.h248
-rw-r--r--src/crepe/system/PhysicsSystem.cpp10
3 files changed, 706 insertions, 6 deletions
diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp
index c74ca1d..2d5ce9d 100644
--- a/src/crepe/system/CollisionSystem.cpp
+++ b/src/crepe/system/CollisionSystem.cpp
@@ -1,5 +1,457 @@
+#include <cmath>
+#include <algorithm>
+#include <cstddef>
+#include <functional>
+#include <utility>
+#include <variant>
+#include <optional>
+
+#include "api/Event.h"
+#include "api/EventManager.h"
+#include "api/BoxCollider.h"
+#include "api/CircleCollider.h"
+#include "api/Metadata.h"
+#include "api/Vector2.h"
+#include "api/Rigidbody.h"
+#include "api/Transform.h"
+
+#include "ComponentManager.h"
#include "CollisionSystem.h"
+#include "Collider.h"
+#include "types.h"
+#include "util/OptionalRef.h"
using namespace crepe;
-void CollisionSystem::update() {}
+void CollisionSystem::update() {
+ std::vector<CollisionInternal> all_colliders;
+ game_object_id_t id = 0;
+ RefVector<Rigidbody> rigidbodies = this->component_manager.get_components_by_type<Rigidbody>();
+ // Collisions can only happen on object with a rigidbody
+ for(Rigidbody& rigidbody : rigidbodies) {
+ if (!rigidbody.active) continue;
+ id = rigidbody.game_object_id;
+ Transform& transform = this->component_manager.get_components_by_id<Transform>(id).front().get();
+ // Check if the boxcollider is active and has the same id as the rigidbody.
+ RefVector<BoxCollider> boxcolliders = this->component_manager.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
+ }
+ );
+ }
+ // Check if the circlecollider is active and has the same id as the rigidbody.
+ RefVector<CircleCollider> circlecolliders = this->component_manager.get_components_by_type<CircleCollider>();
+ 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
+ }
+ );
+ }
+ }
+
+ // Check between all colliders if there is a collision
+ std::vector<std::pair<CollisionInternal,CollisionInternal>> collided = this->gather_collisions(all_colliders);
+
+ // For both objects call the collision handler
+ 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);
+ }
+}
+
+void CollisionSystem::collision_handler_request(CollisionInternal& this_data,CollisionInternal& other_data){
+
+ 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);
+
+ OptionalRef<Metadata> this_metadata = this->component_manager.get_components_by_id<Metadata>(this_data.id).front().get();
+ OptionalRef<Metadata> other_metadata = this->component_manager.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;
+ }
+ }
+
+ // 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,
+ };
+
+ // Determine if static needs to be called
+ this->determine_collision_handler(collision_info);
+}
+
+
+std::pair<vec2,CollisionSystem::Direction> CollisionSystem::collision_handler(CollisionInternal& data1,CollisionInternal& data2,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);
+ 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);
+ 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);
+ 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);
+ break;
+ }
+ }
+
+ 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;
+ if(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;
+ if(data1.rigidbody.data.linear_velocity.x != 0)
+ resolution.x = data1.rigidbody.data.linear_velocity.x * (resolution.y/data1.rigidbody.data.linear_velocity.y);
+ }
+
+ return std::make_pair(resolution,resolution_direction);
+}
+
+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;
+ }
+ }
+
+ 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;
+
+ // 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 = circle_collider1.radius + circle_collider2.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 CircleCollider& circle_collider, const BoxCollider& box_collider, const vec2& circle_position, const vec2& box_position) const
+{
+ vec2 delta = circle_position - box_position;
+
+ // 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;
+}
+
+
+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){
+ static_collision_handler(info);
+ };
+ // Call collision event for user
+ CollisionEvent data(info);
+ EventManager::get_instance().trigger_event<CollisionEvent>(data, info.this_collider.game_object_id);
+}
+
+void CollisionSystem::static_collision_handler(CollisionInfo& info){
+ // Move object back using calculate move back value
+ info.this_transform.position += info.resolution;
+
+ // If bounce is enabled mirror velocity
+ if(info.this_rigidbody.data.elastisity_coefficient > 0) {
+ if(info.resolution_direction == Direction::BOTH)
+ {
+ info.this_rigidbody.data.linear_velocity.y = -info.this_rigidbody.data.linear_velocity.y * info.this_rigidbody.data.elastisity_coefficient;
+ info.this_rigidbody.data.linear_velocity.x = -info.this_rigidbody.data.linear_velocity.x * info.this_rigidbody.data.elastisity_coefficient;
+ }
+ else if(info.resolution_direction == Direction::Y_DIRECTION) {
+ info.this_rigidbody.data.linear_velocity.y = -info.this_rigidbody.data.linear_velocity.y * info.this_rigidbody.data.elastisity_coefficient;
+ }
+ else if(info.resolution_direction == Direction::X_DIRECTION){
+ info.this_rigidbody.data.linear_velocity.x = -info.this_rigidbody.data.linear_velocity.x * info.this_rigidbody.data.elastisity_coefficient;
+ }
+ }
+ // Stop movement if bounce is disabled
+ else {
+ info.this_rigidbody.data.linear_velocity = {0,0};
+ }
+}
+
+std::vector<std::pair<CollisionSystem::CollisionInternal,CollisionSystem::CollisionInternal>> CollisionSystem::gather_collisions(std::vector<CollisionInternal> & colliders) {
+
+
+ // 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;
+ 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;
+}
+
+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::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);
+ }
+ }
+ 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));
+
+}
diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h
index c1a70d8..85ae5ad 100644
--- a/src/crepe/system/CollisionSystem.h
+++ b/src/crepe/system/CollisionSystem.h
@@ -1,13 +1,261 @@
#pragma once
+#include <vector>
+#include <variant>
+#include <optional>
+
+#include "api/Rigidbody.h"
+#include "api/Transform.h"
+#include "api/BoxCollider.h"
+#include "api/CircleCollider.h"
+#include "api/Metadata.h"
+#include "api/Vector2.h"
+
+#include "Collider.h"
#include "System.h"
+
namespace crepe {
+
+//! A system responsible for detecting and handling collisions between colliders.
class CollisionSystem : public System {
public:
using System::System;
+private:
+
+ //! A variant type that can hold either a BoxCollider or a CircleCollider.
+ using collider_variant = std::variant<std::reference_wrapper<BoxCollider>, std::reference_wrapper<CircleCollider>>;
+
+ //! Enum representing the types of collider pairs for collision detection.
+ enum class CollisionInternalType {
+ BOX_BOX,
+ CIRCLE_CIRCLE,
+ BOX_CIRCLE,
+ CIRCLE_BOX,
+ };
+
+ /**
+ * \brief A structure to store the collision data of a single collider.
+ *
+ * This structure all components and id that are for needed within this system when calculating or handeling collisions.
+ * The transform and rigidbody are mostly needed for location and rotation.
+ * In rigidbody additional info is written about what the body of the object is,
+ * and how it should respond on a collision.
+ */
+ struct CollisionInternal {
+ game_object_id_t id = 0;
+ collider_variant collider;
+ Transform& transform;
+ Rigidbody& rigidbody;
+ };
+
+ //! Enum representing movement directions during collision resolution.
+ enum class Direction {
+ //! No movement required.
+ NONE,
+ //! Movement in the X direction.
+ X_DIRECTION,
+ //! Movement in the Y direction.
+ Y_DIRECTION,
+ //! Movement in both X and Y directions.
+ BOTH
+ };
+
+public:
+ /**
+ * \brief Structure representing detailed collision information between two colliders.
+ *
+ * Includes information about the colliding objects and the resolution data for handling the collision.
+ */
+ struct CollisionInfo{
+ Collider& this_collider;
+ Transform& this_transform;
+ Rigidbody& this_rigidbody;
+ Metadata& this_metadata;
+ Collider& other_collider;
+ Transform& other_transform;
+ Rigidbody& other_rigidbody;
+ Metadata& other_metadata;
+ //! The resolution vector for the collision.
+ vec2 resolution;
+ //! The direction of movement for resolving the collision.
+ Direction resolution_direction = Direction::NONE;
+ };
+
+public:
+
+ //! Updates the collision system by checking for collisions between colliders and handling them.
void update() override;
+private:
+
+ /**
+ * \brief Determines the type of collider pair from two colliders.
+ *
+ * Uses std::holds_alternative to identify the types of the provided colliders.
+ *
+ * \param collider1 First collider variant (BoxCollider or CircleCollider).
+ * \param collider2 Second collider variant (BoxCollider or CircleCollider).
+ * \return The combined type of the two colliders.
+ */
+ CollisionInternalType get_collider_type(const collider_variant& collider1,const collider_variant& collider2) const;
+
+ /**
+ * \brief Calculates the current position of a collider.
+ *
+ * Combines the Collider offset, Transform position, and Rigidbody offset to compute the position of the collider.
+ *
+ * \param collider_offset The offset of the collider.
+ * \param transform The Transform of the associated game object.
+ * \param rigidbody The Rigidbody of the associated game object.
+ * \return The calculated position of the collider.
+ */
+ vec2 get_current_position(const vec2& collider_offset, const Transform& transform, const Rigidbody& rigidbody) const;
+
+private:
+
+ /**
+ * \brief Handles collision resolution between two colliders.
+ *
+ * Processes collision data and adjusts objects to resolve collisions and/or calls the user oncollision script function.
+ *
+ * \param data1 Collision data for the first collider.
+ * \param data2 Collision data for the second collider.
+ */
+ void collision_handler_request(CollisionInternal& data1,CollisionInternal& data2);
+
+ /**
+ * \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> collision_handler(CollisionInternal& data1,CollisionInternal& data2 ,CollisionInternalType type);
+
+ /**
+ * \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 BoxCollider& box_collider1,const BoxCollider& box_collider2,const vec2& position1,const vec2& position2) const;
+
+ /**
+ * \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 position1 The position of the first CircleCollider.
+ * \param position2 The position of the second CircleCollider.
+ * \return The resolution vector for the collision.
+ */
+ vec2 get_circle_circle_resolution(const CircleCollider& circle_collider1, const CircleCollider& circle_collider2, const vec2& final_position1, const vec2& final_position2) const;
+
+ /**
+ * \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 BocCollider.
+ * \return The resolution vector for the collision.
+ */
+ vec2 get_circle_box_resolution(const CircleCollider& circle_collider, const BoxCollider& box_collider, const vec2& circle_position, const vec2& box_position) const;
+
+ /**
+ * \brief Determines the appropriate collision handler for a collision.
+ *
+ * Decides the correct resolution process based on the dynamic or static nature of the colliders involved.
+ *
+ * \param info Collision information containing data about both colliders.
+ */
+ void determine_collision_handler(CollisionInfo& info);
+
+ /**
+ * \brief Handles collisions involving static objects.
+ *
+ * Resolves collisions by adjusting positions and modifying velocities if bounce is enabled.
+ *
+ * \param info Collision information containing data about both colliders.
+ */
+ void static_collision_handler(CollisionInfo& info);
+private:
+
+ /**
+ * \brief Checks for collisions between colliders.
+ *
+ * Identifies collisions and generates pairs of colliding objects for further processing.
+ *
+ * \param colliders A collection of all active colliders.
+ * \return A list of collision pairs with their associated data.
+ */
+ std::vector<std::pair<CollisionInternal,CollisionInternal>> gather_collisions(std::vector<CollisionInternal> & colliders);
+
+ /**
+ * \brief Checks for collision between two colliders.
+ *
+ * Calls the appropriate collision detection function based on the collider types.
+ *
+ * \param first_info Collision data for the first collider.
+ * \param second_info Collision data for the second collider.
+ * \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, CollisionInternalType type) const;
+
+ /**
+ * \brief Detects collisions between two BoxColliders.
+ *
+ * \param box1 The first BoxCollider.
+ * \param box2 The second BoxCollider.
+ * \param transform1 Transform of the first object.
+ * \param transform2 Transform of the second object.
+ * \param rigidbody1 Rigidbody of the first object.
+ * \param rigidbody2 Rigidbody of the second object.
+ * \return True if a collision is detected, otherwise false.
+ */
+ bool get_box_box_collision(const BoxCollider& box1, const BoxCollider& box2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) const;
+
+ /**
+ * \brief Check collision for box on circle collider
+ *
+ * \param box1 The BoxCollider
+ * \param circle2 The CircleCollider
+ * \param transform1 Transform of the first object.
+ * \param transform2 Transform of the second object.
+ * \param rigidbody1 Rigidbody of the first object.
+ * \param rigidbody2 Rigidbody of the second object.
+ * \return True if a collision is detected, otherwise false.
+ */
+ bool get_box_circle_collision(const BoxCollider& box1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) const;
+
+ /**
+ * \brief Check collision for circle on circle collider
+ *
+ * \param circle1 First CircleCollider
+ * \param circle2 Second CircleCollider
+ * \param transform1 Transform of the first object.
+ * \param transform2 Transform of the second object.
+ * \param rigidbody1 Rigidbody of the first object.
+ * \param rigidbody2 Rigidbody of the second object.
+ * \return True if a collision is detected, otherwise false.
+ *
+ * \return status of collision
+ */
+ bool get_circle_circle_collision(const CircleCollider& circle1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) const;
};
} // namespace crepe
diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp
index 514a4b3..b5da042 100644
--- a/src/crepe/system/PhysicsSystem.cpp
+++ b/src/crepe/system/PhysicsSystem.cpp
@@ -25,17 +25,17 @@ void PhysicsSystem::update() {
if (transform.game_object_id == rigidbody.game_object_id) {
// Add gravity
- if (rigidbody.data.use_gravity) {
+ if (rigidbody.data.gravity_scale > 0) {
rigidbody.data.linear_velocity.y
+= (rigidbody.data.mass * rigidbody.data.gravity_scale
* gravity);
}
// Add damping
- if (rigidbody.data.angular_damping != 0) {
- rigidbody.data.angular_velocity *= rigidbody.data.angular_damping;
+ if (rigidbody.data.angular_velocity_coefficient > 0) {
+ rigidbody.data.angular_velocity *= rigidbody.data.angular_velocity_coefficient;
}
- if (rigidbody.data.linear_damping != vec2{0, 0}) {
- rigidbody.data.linear_velocity *= rigidbody.data.linear_damping;
+ if (rigidbody.data.linear_velocity_coefficient.x > 0 && rigidbody.data.linear_velocity_coefficient.y > 0) {
+ rigidbody.data.linear_velocity *= rigidbody.data.linear_velocity_coefficient;
}
// Max velocity check