aboutsummaryrefslogtreecommitdiff
path: root/src/crepe/system
diff options
context:
space:
mode:
Diffstat (limited to 'src/crepe/system')
-rw-r--r--src/crepe/system/AnimatorSystem.cpp31
-rw-r--r--src/crepe/system/AnimatorSystem.h3
-rw-r--r--src/crepe/system/CollisionSystem.cpp548
-rw-r--r--src/crepe/system/CollisionSystem.h298
-rw-r--r--src/crepe/system/InputSystem.cpp8
-rw-r--r--src/crepe/system/ParticleSystem.h10
-rw-r--r--src/crepe/system/PhysicsSystem.cpp13
-rw-r--r--src/crepe/system/PhysicsSystem.h4
-rw-r--r--src/crepe/system/RenderSystem.cpp40
-rw-r--r--src/crepe/system/RenderSystem.h23
-rw-r--r--src/crepe/system/ScriptSystem.h2
11 files changed, 921 insertions, 59 deletions
diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp
index 8bb6465..549c35d 100644
--- a/src/crepe/system/AnimatorSystem.cpp
+++ b/src/crepe/system/AnimatorSystem.cpp
@@ -1,8 +1,8 @@
-#include <cstdint>
+
#include "../api/Animator.h"
-#include "../facade/SDLContext.h"
#include "../manager/ComponentManager.h"
+#include "api/LoopTimer.h"
#include "AnimatorSystem.h"
@@ -10,15 +10,30 @@ using namespace crepe;
void AnimatorSystem::update() {
ComponentManager & mgr = this->mediator.component_manager;
-
+ LoopTimer & timer = this->mediator.timer;
RefVector<Animator> animations = mgr.get_components_by_type<Animator>();
- uint64_t tick = SDLContext::get_instance().get_ticks();
+ double elapsed_time = timer.get_current_time();
+
for (Animator & a : animations) {
if (!a.active) continue;
- // (10 frames per second)
- a.curr_row = (tick / 100) % a.row;
- a.spritesheet.mask.x = (a.curr_row * a.spritesheet.mask.w) + a.curr_col;
- a.spritesheet.mask = a.spritesheet.mask;
+
+ Animator::Data & ctx = a.data;
+ float frame_duration = 1.0f / ctx.fps;
+
+ int last_frame = ctx.row;
+
+ int cycle_end = (ctx.cycle_end == -1) ? a.max_rows : ctx.cycle_end;
+ int total_frames = cycle_end - ctx.cycle_start;
+
+ int curr_frame = static_cast<int>(elapsed_time / frame_duration) % total_frames;
+
+ ctx.row = ctx.cycle_start + curr_frame;
+ a.spritesheet.mask.x = ctx.row * a.spritesheet.mask.w;
+ a.spritesheet.mask.y = (ctx.col * a.spritesheet.mask.h);
+
+ if (!ctx.looping && curr_frame == ctx.cycle_start && last_frame == total_frames - 1) {
+ a.active = false;
+ }
}
}
diff --git a/src/crepe/system/AnimatorSystem.h b/src/crepe/system/AnimatorSystem.h
index f8179a9..7d3f565 100644
--- a/src/crepe/system/AnimatorSystem.h
+++ b/src/crepe/system/AnimatorSystem.h
@@ -2,9 +2,6 @@
#include "System.h"
-//TODO:
-// control if flip works with animation system
-
namespace crepe {
/**
diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp
index c74ca1d..44a0431 100644
--- a/src/crepe/system/CollisionSystem.cpp
+++ b/src/crepe/system/CollisionSystem.cpp
@@ -1,5 +1,551 @@
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <functional>
+#include <optional>
+#include <utility>
+#include <variant>
+
+#include "../manager/ComponentManager.h"
+#include "../manager/EventManager.h"
+#include "api/BoxCollider.h"
+#include "api/CircleCollider.h"
+#include "api/Event.h"
+#include "api/Metadata.h"
+#include "api/Rigidbody.h"
+#include "api/Transform.h"
+#include "api/Vector2.h"
+
+#include "Collider.h"
#include "CollisionSystem.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;
+ ComponentManager & mgr = this->mediator.component_manager;
+ RefVector<Rigidbody> rigidbodies = mgr.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 = mgr.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 = 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});
+ }
+ // Check if the circlecollider is active and has the same id as the rigidbody.
+ RefVector<CircleCollider> circlecolliders
+ = mgr.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);
+ 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;
+ }
+ }
+
+ // 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) {
+ this->static_collision_handler(info);
+ };
+ // 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);
+}
+
+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;
+ 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) {
+
+ // Check if any number is equal in the layers
+ for (int num : layers1) {
+ if (layers2.contains(num)) {
+ // Common layer found
+ return true;
+ break;
+ }
+ }
+ // No common layer found
+ return false;
+}
+
+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..5b136c6 100644
--- a/src/crepe/system/CollisionSystem.h
+++ b/src/crepe/system/CollisionSystem.h
@@ -1,13 +1,311 @@
#pragma once
+#include <optional>
+#include <variant>
+#include <vector>
+
+#include "api/BoxCollider.h"
+#include "api/CircleCollider.h"
+#include "api/Event.h"
+#include "api/Metadata.h"
+#include "api/Rigidbody.h"
+#include "api/Transform.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 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 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 BoxCollider.
+ * \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 if two collision layers have at least one common layer.
+ *
+ * This function checks if there is any overlapping layer between the two inputs.
+ * It compares each layer from the first input to see
+ * if it exists in the second input. If at least one common layer is found,
+ * the function returns true, indicating that the two colliders share a common
+ * collision layer.
+ *
+ * \param layers1 all collision layers for the first collider.
+ * \param layers2 all collision layers for the second collider.
+ * \return Returns true if there is at least one common layer, false otherwise.
+ */
+
+ bool have_common_layer(const std::set<int> & layers1, const std::set<int> & layers2);
+
+ /**
+ * \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;
+};
+
+/**
+ * \brief Event triggered during a collision between objects.
+ */
+class CollisionEvent : public Event {
+public:
+ crepe::CollisionSystem::CollisionInfo info;
+ CollisionEvent(const crepe::CollisionSystem::CollisionInfo & collisionInfo)
+ : info(collisionInfo) {}
};
} // namespace crepe
diff --git a/src/crepe/system/InputSystem.cpp b/src/crepe/system/InputSystem.cpp
index 7cc8d30..aaa8bdf 100644
--- a/src/crepe/system/InputSystem.cpp
+++ b/src/crepe/system/InputSystem.cpp
@@ -24,10 +24,10 @@ void InputSystem::update() {
RefVector<Transform> transform_vec
= mgr.get_components_by_id<Transform>(current_cam.game_object_id);
Transform & cam_transform = transform_vec.front().get();
- int camera_origin_x
- = cam_transform.position.x + current_cam.offset.x - (current_cam.viewport_size.x / 2);
- int camera_origin_y
- = cam_transform.position.y + current_cam.offset.y - (current_cam.viewport_size.y / 2);
+ int camera_origin_x = cam_transform.position.x + current_cam.data.postion_offset.x
+ - (current_cam.viewport_size.x / 2);
+ int camera_origin_y = cam_transform.position.y + current_cam.data.postion_offset.y
+ - (current_cam.viewport_size.y / 2);
for (const SDLContext::EventData & event : event_list) {
int world_mouse_x = event.mouse_position.x + camera_origin_x;
diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h
index c647284..068f01c 100644
--- a/src/crepe/system/ParticleSystem.h
+++ b/src/crepe/system/ParticleSystem.h
@@ -25,7 +25,7 @@ public:
private:
/**
* \brief Emits a particle from the specified emitter based on its emission properties.
- *
+ *
* \param emitter Reference to the ParticleEmitter.
* \param transform Const reference to the Transform component associated with the emitter.
*/
@@ -34,7 +34,7 @@ private:
/**
* \brief Calculates the number of times particles should be emitted based on emission rate
* and update count.
- *
+ *
* \param count Current update count.
* \param emission Emission rate.
* \return The number of particles to emit.
@@ -44,7 +44,7 @@ private:
/**
* \brief Checks whether particles are within the emitter’s boundary, resets or stops
* particles if they exit.
- *
+ *
* \param emitter Reference to the ParticleEmitter.
* \param transform Const reference to the Transform component associated with the emitter.
*/
@@ -52,7 +52,7 @@ private:
/**
* \brief Generates a random angle for particle emission within the specified range.
- *
+ *
* \param min_angle Minimum emission angle in degrees.
* \param max_angle Maximum emission angle in degrees.
* \return Random angle in degrees.
@@ -61,7 +61,7 @@ private:
/**
* \brief Generates a random speed for particle emission within the specified range.
- *
+ *
* \param min_speed Minimum emission speed.
* \param max_speed Maximum emission speed.
* \return Random speed.
diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp
index bebcf3d..ebf4439 100644
--- a/src/crepe/system/PhysicsSystem.cpp
+++ b/src/crepe/system/PhysicsSystem.cpp
@@ -25,17 +25,20 @@ 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
diff --git a/src/crepe/system/PhysicsSystem.h b/src/crepe/system/PhysicsSystem.h
index 227ab69..26152a5 100644
--- a/src/crepe/system/PhysicsSystem.h
+++ b/src/crepe/system/PhysicsSystem.h
@@ -6,7 +6,7 @@ namespace crepe {
/**
* \brief System that controls all physics
- *
+ *
* This class is a physics system that uses a rigidbody and transform to add physics to a game
* object.
*/
@@ -15,7 +15,7 @@ public:
using System::System;
/**
* \brief updates the physics system.
- *
+ *
* It calculates new velocties and changes the postion in the transform.
*/
void update() override;
diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp
index 92dba43..26f2c85 100644
--- a/src/crepe/system/RenderSystem.cpp
+++ b/src/crepe/system/RenderSystem.cpp
@@ -17,13 +17,19 @@
using namespace crepe;
using namespace std;
-void RenderSystem::clear_screen() { this->context.clear_screen(); }
+void RenderSystem::clear_screen() {
+ SDLContext & ctx = this->mediator.sdl_context;
+ ctx.clear_screen();
+}
-void RenderSystem::present_screen() { this->context.present_screen(); }
+void RenderSystem::present_screen() {
+ SDLContext & ctx = this->mediator.sdl_context;
+ ctx.present_screen();
+}
-const Camera & RenderSystem::update_camera() {
+SDLContext::CameraValues RenderSystem::update_camera() {
ComponentManager & mgr = this->mediator.component_manager;
-
+ SDLContext & ctx = this->mediator.sdl_context;
RefVector<Camera> cameras = mgr.get_components_by_type<Camera>();
if (cameras.size() == 0) throw std::runtime_error("No cameras in current scene");
@@ -32,16 +38,18 @@ const Camera & RenderSystem::update_camera() {
if (!cam.active) continue;
const Transform & transform
= mgr.get_components_by_id<Transform>(cam.game_object_id).front().get();
- this->context.set_camera(cam);
- this->cam_pos = transform.position + cam.offset;
- return cam;
+ SDLContext::CameraValues cam_val = ctx.set_camera(cam);
+ cam_val.cam_pos = transform.position + cam.data.postion_offset;
+ return cam_val;
}
throw std::runtime_error("No active cameras in current scene");
}
bool sorting_comparison(const Sprite & a, const Sprite & b) {
- if (a.sorting_in_layer < b.sorting_in_layer) return true;
- if (a.sorting_in_layer == b.sorting_in_layer) return a.order_in_layer < b.order_in_layer;
+ if (a.data.sorting_in_layer != b.data.sorting_in_layer)
+ return a.data.sorting_in_layer < b.data.sorting_in_layer;
+ if (a.data.order_in_layer != b.data.order_in_layer)
+ return a.data.order_in_layer < b.data.order_in_layer;
return false;
}
@@ -59,10 +67,11 @@ void RenderSystem::update() {
this->present_screen();
}
-bool RenderSystem::render_particle(const Sprite & sprite, const Camera & cam,
+bool RenderSystem::render_particle(const Sprite & sprite, const SDLContext::CameraValues & cam,
const double & scale) {
ComponentManager & mgr = this->mediator.component_manager;
+ SDLContext & ctx = this->mediator.sdl_context;
vector<reference_wrapper<ParticleEmitter>> emitters
= mgr.get_components_by_id<ParticleEmitter>(sprite.game_object_id);
@@ -77,10 +86,9 @@ bool RenderSystem::render_particle(const Sprite & sprite, const Camera & cam,
for (const Particle & p : em.data.particles) {
if (!p.active) continue;
- this->context.draw(SDLContext::RenderContext{
+ ctx.draw(SDLContext::RenderContext{
.sprite = sprite,
.cam = cam,
- .cam_pos = this->cam_pos,
.pos = p.position,
.angle = p.angle,
.scale = scale,
@@ -89,12 +97,12 @@ bool RenderSystem::render_particle(const Sprite & sprite, const Camera & cam,
}
return rendering_particles;
}
-void RenderSystem::render_normal(const Sprite & sprite, const Camera & cam,
+void RenderSystem::render_normal(const Sprite & sprite, const SDLContext::CameraValues & cam,
const Transform & tm) {
- this->context.draw(SDLContext::RenderContext{
+ SDLContext & ctx = this->mediator.sdl_context;
+ ctx.draw(SDLContext::RenderContext{
.sprite = sprite,
.cam = cam,
- .cam_pos = this->cam_pos,
.pos = tm.position,
.angle = tm.rotation,
.scale = tm.scale,
@@ -103,7 +111,7 @@ void RenderSystem::render_normal(const Sprite & sprite, const Camera & cam,
void RenderSystem::render() {
ComponentManager & mgr = this->mediator.component_manager;
- const Camera & cam = this->update_camera();
+ const SDLContext::CameraValues & cam = this->update_camera();
RefVector<Sprite> sprites = mgr.get_components_by_type<Sprite>();
RefVector<Sprite> sorted_sprites = this->sort(sprites);
diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h
index 096d058..e270a6b 100644
--- a/src/crepe/system/RenderSystem.h
+++ b/src/crepe/system/RenderSystem.h
@@ -18,7 +18,7 @@ class Transform;
* \brief Manages rendering operations for all game objects.
*
* RenderSystem is responsible for rendering, clearing and presenting the screen, and
- * managing the active camera.
+ * managing the active camera.
*/
class RenderSystem : public System {
public:
@@ -37,7 +37,7 @@ private:
void present_screen();
//! Updates the active camera used for rendering.
- const Camera & update_camera();
+ SDLContext::CameraValues update_camera();
//! Renders the whole screen
void render();
@@ -52,20 +52,22 @@ private:
* constructor is now protected i cannot make tmp inside
* \return true if particles have been rendered
*/
- bool render_particle(const Sprite & sprite, const Camera & cam, const double & scale);
+ bool render_particle(const Sprite & sprite, const SDLContext::CameraValues & cam,
+ const double & scale);
/**
- * \brief renders a sprite with a Transform component on the screen
+ * \brief renders a sprite with a Transform component on the screen
*
* \param sprite the sprite component that holds all the data
- * \param tm the Transform component that holds the position,rotation and scale
+ * \param tm the Transform component that holds the position,rotation and scale
*/
- void render_normal(const Sprite & sprite, const Camera & cam, const Transform & tm);
+ void render_normal(const Sprite & sprite, const SDLContext::CameraValues & cam,
+ const Transform & tm);
/**
* \brief sort a vector sprite objects with
*
- * \param objs the vector that will do a sorting algorithm on
+ * \param objs the vector that will do a sorting algorithm on
* \return returns a sorted reference vector
*/
RefVector<Sprite> sort(RefVector<Sprite> & objs) const;
@@ -75,13 +77,6 @@ private:
* \todo Implement a text component and a button component.
* \todo Consider adding text input functionality.
*/
-
-private:
- // FIXME: retrieve sdlcontext via mediator after #PR57
- SDLContext & context = SDLContext::get_instance();
-
- //! camera postion in the current scene
- vec2 cam_pos;
};
} // namespace crepe
diff --git a/src/crepe/system/ScriptSystem.h b/src/crepe/system/ScriptSystem.h
index 936e9ca..3db1b1e 100644
--- a/src/crepe/system/ScriptSystem.h
+++ b/src/crepe/system/ScriptSystem.h
@@ -8,7 +8,7 @@ class Script;
/**
* \brief Script system
- *
+ *
* The script system is responsible for all \c BehaviorScript components, and
* calls the methods on classes derived from \c Script.
*/