aboutsummaryrefslogtreecommitdiff
path: root/src/crepe/system
diff options
context:
space:
mode:
authormax-001 <maxsmits21@kpnmail.nl>2024-12-20 12:01:36 +0100
committermax-001 <maxsmits21@kpnmail.nl>2024-12-20 12:01:36 +0100
commit79d3a9f4311e6684b6df83a15ca7844f58c1959c (patch)
treeb2883e83f61cee9edf290a6a7228c7f0b1fbae8a /src/crepe/system
parent9140b73e4af7aa925b53e4fb4e6aa7f4ea2e3385 (diff)
parent03aea832aa0bc2edba2cc5ab4d9f8eba42d355be (diff)
Merge remote-tracking branch 'origin/master' into max/game
Diffstat (limited to 'src/crepe/system')
-rw-r--r--src/crepe/system/AISystem.cpp2
-rw-r--r--src/crepe/system/AISystem.h2
-rw-r--r--src/crepe/system/AnimatorSystem.cpp4
-rw-r--r--src/crepe/system/AnimatorSystem.h2
-rw-r--r--src/crepe/system/AudioSystem.cpp2
-rw-r--r--src/crepe/system/AudioSystem.h2
-rw-r--r--src/crepe/system/CMakeLists.txt4
-rw-r--r--src/crepe/system/CollisionSystem.cpp870
-rw-r--r--src/crepe/system/CollisionSystem.h292
-rw-r--r--src/crepe/system/EventSystem.cpp9
-rw-r--r--src/crepe/system/EventSystem.h21
-rw-r--r--src/crepe/system/InputSystem.cpp88
-rw-r--r--src/crepe/system/InputSystem.h46
-rw-r--r--src/crepe/system/ParticleSystem.cpp8
-rw-r--r--src/crepe/system/ParticleSystem.h2
-rw-r--r--src/crepe/system/PhysicsSystem.cpp3
-rw-r--r--src/crepe/system/PhysicsSystem.h2
-rw-r--r--src/crepe/system/RenderSystem.cpp52
-rw-r--r--src/crepe/system/RenderSystem.h28
-rw-r--r--src/crepe/system/ReplaySystem.cpp54
-rw-r--r--src/crepe/system/ReplaySystem.h44
-rw-r--r--src/crepe/system/ScriptSystem.cpp18
-rw-r--r--src/crepe/system/ScriptSystem.h23
-rw-r--r--src/crepe/system/System.h10
24 files changed, 905 insertions, 683 deletions
diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
index 680dbb8..0f35010 100644
--- a/src/crepe/system/AISystem.cpp
+++ b/src/crepe/system/AISystem.cpp
@@ -10,7 +10,7 @@
using namespace crepe;
using namespace std::chrono;
-void AISystem::update() {
+void AISystem::fixed_update() {
const Mediator & mediator = this->mediator;
ComponentManager & mgr = mediator.component_manager;
LoopTimerManager & loop_timer = mediator.loop_timer;
diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h
index d5f8a8e..04807cf 100644
--- a/src/crepe/system/AISystem.h
+++ b/src/crepe/system/AISystem.h
@@ -20,7 +20,7 @@ public:
using System::System;
//! Update the AI system
- void update() override;
+ void fixed_update() override;
private:
/**
diff --git a/src/crepe/system/AnimatorSystem.cpp b/src/crepe/system/AnimatorSystem.cpp
index 107b25d..e5ab2fa 100644
--- a/src/crepe/system/AnimatorSystem.cpp
+++ b/src/crepe/system/AnimatorSystem.cpp
@@ -1,5 +1,3 @@
-
-
#include "../api/Animator.h"
#include "../manager/ComponentManager.h"
#include "../manager/LoopTimerManager.h"
@@ -10,7 +8,7 @@
using namespace crepe;
using namespace std::chrono;
-void AnimatorSystem::update() {
+void AnimatorSystem::frame_update() {
ComponentManager & mgr = this->mediator.component_manager;
LoopTimerManager & timer = this->mediator.loop_timer;
RefVector<Animator> animations = mgr.get_components_by_type<Animator>();
diff --git a/src/crepe/system/AnimatorSystem.h b/src/crepe/system/AnimatorSystem.h
index 7d3f565..092e131 100644
--- a/src/crepe/system/AnimatorSystem.h
+++ b/src/crepe/system/AnimatorSystem.h
@@ -22,7 +22,7 @@ public:
* Animator components, moving the animations forward and managing their behavior (e.g.,
* looping).
*/
- void update() override;
+ void frame_update() override;
};
} // namespace crepe
diff --git a/src/crepe/system/AudioSystem.cpp b/src/crepe/system/AudioSystem.cpp
index b1aa0f8..d4e8b9f 100644
--- a/src/crepe/system/AudioSystem.cpp
+++ b/src/crepe/system/AudioSystem.cpp
@@ -7,7 +7,7 @@
using namespace crepe;
using namespace std;
-void AudioSystem::update() {
+void AudioSystem::fixed_update() {
ComponentManager & component_manager = this->mediator.component_manager;
ResourceManager & resource_manager = this->mediator.resource_manager;
RefVector<AudioSource> components
diff --git a/src/crepe/system/AudioSystem.h b/src/crepe/system/AudioSystem.h
index 2ddc443..56fc98c 100644
--- a/src/crepe/system/AudioSystem.h
+++ b/src/crepe/system/AudioSystem.h
@@ -11,7 +11,7 @@ namespace crepe {
class AudioSystem : public System {
public:
using System::System;
- void update() override;
+ void fixed_update() override;
private:
/**
diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt
index 0e2db76..52369d0 100644
--- a/src/crepe/system/CMakeLists.txt
+++ b/src/crepe/system/CMakeLists.txt
@@ -8,6 +8,8 @@ target_sources(crepe PUBLIC
AudioSystem.cpp
AnimatorSystem.cpp
InputSystem.cpp
+ EventSystem.cpp
+ ReplaySystem.cpp
AISystem.cpp
)
@@ -20,5 +22,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
AudioSystem.h
AnimatorSystem.h
InputSystem.h
+ EventSystem.h
+ ReplaySystem.h
AISystem.h
)
diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp
index af8adce..654d4c6 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,6 +44,7 @@ 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) {
@@ -40,8 +52,7 @@ void CollisionSystem::update() {
if (!boxcollider.active) continue;
all_colliders.push_back({.id = id,
.collider = collider_variant{boxcollider},
- .transform = transform,
- .rigidbody = rigidbody});
+ .info = {transform, rigidbody, metadata}});
}
// Check if the circlecollider is active and has the same id as the rigidbody.
RefVector<CircleCollider> circlecolliders
@@ -51,310 +62,450 @@ void CollisionSystem::update() {
if (!circlecollider.active) continue;
all_colliders.push_back({.id = id,
.collider = collider_variant{circlecollider},
- .transform = transform,
- .rigidbody = rigidbody});
+ .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;
- // Determine if static needs to be called
- this->determine_collision_handler(collision_info);
+ // 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;
+
+ 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;
+ }
+ }
}
-std::pair<vec2, CollisionSystem::Direction>
-CollisionSystem::collision_handler(CollisionInternal & data1, CollisionInternal & data2,
- CollisionInternalType type) {
+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;
- // Find the vector from the circle center to the closest point
- vec2 closest_delta = delta - closest_point;
+ // Calculate the sum of the radii
+ float radius_sum = scaled_circle1 + scaled_circle2;
- // 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;
+ // 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;
- // Compute penetration depth
- float penetration_depth = circle_collider.radius - distance;
+ // Compute the distance between the two circle centers
+ float distance = std::sqrt(delta.x * delta.x + delta.y * delta.y);
- // Compute the resolution vector
- vec2 resolution = collision_normal * penetration_depth;
+ // Compute the combined radii of the two circles
+ float combined_radius = scaled_circle1 + scaled_circle2;
- return resolution;
+ // 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;
+ }
+ // No collision
+ return vec2{NAN, NAN};
+ ;
}
-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::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;
+}
+
+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 +514,80 @@ void CollisionSystem::static_collision_handler(CollisionInfo & info) {
}
}
-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
+void CollisionSystem::dynamic_collision_handler(const CollisionInfo & info) {
- // 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]);
- }
- }
+ 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;
- return collisions_ret;
-}
+ self_transform_pos += info.resolution / 2;
+ other_transform_pos += -(info.resolution / 2);
-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);
}
diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h
index 5b136c6..3fb9723 100644
--- a/src/crepe/system/CollisionSystem.h
+++ b/src/crepe/system/CollisionSystem.h
@@ -23,6 +23,42 @@ public:
using System::System;
private:
+ //! 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:
+ //! Structure representing components of the collider
+ struct ColliderInfo {
+ Transform & transform;
+ Rigidbody & rigidbody;
+ Metadata & metadata;
+ };
+
+ /**
+ * \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 {
+ ColliderInfo self;
+ ColliderInfo other;
+ //! The resolution vector for the collision.
+ vec2 resolution;
+ //! The direction of movement for resolving the collision.
+ Direction resolution_direction = Direction::NONE;
+ CollisionInfo operator-() const;
+ };
+
+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>>;
@@ -33,12 +69,13 @@ private:
CIRCLE_CIRCLE,
BOX_CIRCLE,
CIRCLE_BOX,
+ NONE,
};
/**
* \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.
+ * This structure all components and id that are for needed within this system when calculating or handling 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.
@@ -46,46 +83,26 @@ private:
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.
+ ColliderInfo info;
vec2 resolution;
- //! The direction of movement for resolving the collision.
Direction resolution_direction = Direction::NONE;
};
+ //! Structure of a collider with additional components
+ template <typename ColliderType>
+ struct ColliderInternal {
+ ColliderType & collider;
+ Transform & transform;
+ Rigidbody & rigidbody;
+ };
+ //! Predefined BoxColliderInternal. (System is only made for this type)
+ using BoxColliderInternal = ColliderInternal<BoxCollider>;
+ //! Predefined CircleColliderInternal. (System is only made for this type)
+ using CircleColliderInternal = ColliderInternal<CircleCollider>;
+
public:
//! Updates the collision system by checking for collisions between colliders and handling them.
- void update() override;
+ void fixed_update() override;
private:
/**
@@ -100,114 +117,90 @@ private:
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.
+ * \brief Converts internal collision data into user-accessible collision information.
*
- * Processes collision data and adjusts objects to resolve collisions and/or calls the user oncollision script function.
+ * This function processes collision data from two colliding entities and packages it
+ * into a structured format that is accessible for further use,
+ * such as resolving collisions and triggering user-defined collision scripts.
*
* \param data1 Collision data for the first collider.
* \param data2 Collision data for the second collider.
*/
- void collision_handler_request(CollisionInternal & data1, CollisionInternal & data2);
+ CollisionInfo get_collision_info(const CollisionInternal & data1,
+ const CollisionInternal & data2) const;
/**
- * \brief Resolves collision between two colliders and calculates the movement required.
+ * \brief Corrects the collision resolution vector and determines its direction.
*
- * Determines the displacement and direction needed to separate colliders based on their types.
+ * This function adjusts the provided resolution vector based on the
+ * rigidbody's linear velocity to ensure consistent collision correction. If the resolution
+ * vector has only one non-zero component (either x or y), the missing component is computed
+ * based on the rigidbody's velocity. If both components are non-zero, it indicates a corner
+ * collision. The function also identifies the direction of the resolution and returns it.
*
- * \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.
+ * \param resolution resolution vector that needs to be corrected
+ * \param rigidbody rigidbody data used to correct resolution
+ * \return A Direction indicating the resolution direction
*/
- std::pair<vec2, Direction> collision_handler(CollisionInternal & data1,
- CollisionInternal & data2,
- CollisionInternalType type);
+ Direction resolution_correction(vec2 & resolution, const Rigidbody::Data & rigidbody);
/**
- * \brief Calculates the resolution vector for two BoxColliders.
+ * \brief Determines the appropriate collision handler for a given collision event.
*
- * Computes the displacement required to separate two overlapping BoxColliders.
+ * This function identifies the correct collision resolution process based on the body types
+ * of the colliders involved in the collision. It delegates
+ * collision handling to specific handlers and calls collision event scripts
+ * as needed.
*
- * \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.
+ * \param info Collision information containing data about both colliders.
*/
- vec2 get_box_box_resolution(const BoxCollider & box_collider1,
- const BoxCollider & box_collider2, const vec2 & position1,
- const vec2 & position2) const;
+ void determine_collision_handler(const CollisionInfo & info);
/**
- * \brief Calculates the resolution vector for two CircleCollider.
+ * \brief Calls both collision script
*
- * Computes the displacement required to separate two overlapping CircleCollider.
+ * Calls both collision script to let user add additonal handling or handle full collision.
*
- * \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.
+ * \param info Collision information containing data about both colliders.
*/
- vec2 get_circle_circle_resolution(const CircleCollider & circle_collider1,
- const CircleCollider & circle_collider2,
- const vec2 & final_position1,
- const vec2 & final_position2) const;
+ void call_collision_events(const CollisionInfo & info);
/**
- * \brief Calculates the resolution vector for two CircleCollider.
- *
- * Computes the displacement required to separate two overlapping CircleCollider.
+ * \brief Handles collisions involving static objects.
*
- * \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.
+ * This function resolves collisions between static and dynamic objects by adjusting
+ * the position of the static object and modifying the velocity of the dynamic object
+ * if elasticity is enabled. The position of the static object is corrected
+ * based on the collision resolution, and the dynamic object's velocity is adjusted
+ * accordingly to reflect the collision response.
*
- * Decides the correct resolution process based on the dynamic or static nature of the colliders involved.
+ * The handling includes stopping movement, applying bouncing based on the elasticity
+ * coefficient, and adjusting the position of the dynamic object if needed.
*
* \param info Collision information containing data about both colliders.
*/
- void determine_collision_handler(CollisionInfo & info);
+ void static_collision_handler(const CollisionInfo & info);
/**
- * \brief Handles collisions involving static objects.
+ * \brief Handles collisions involving dynamic objects.
*
- * Resolves collisions by adjusting positions and modifying velocities if bounce is enabled.
+ * Resolves collisions between two dynamic objects by adjusting their positions and modifying
+ * their velocities based on the collision resolution. If elasticity is enabled,
+ * the velocity of both objects is reversed and scaled by the respective elasticity coefficient.
+ * The positions of the objects are adjusted based on the collision resolution.
*
* \param info Collision information containing data about both colliders.
*/
- void static_collision_handler(CollisionInfo & info);
+ void dynamic_collision_handler(const CollisionInfo & info);
private:
/**
* \brief Checks for collisions between colliders.
*
- * Identifies collisions and generates pairs of colliding objects for further processing.
+ * This function checks all active colliders and identifies pairs of colliding objects.
+ * For each identified collision, the appropriate collision data is returned as pairs for further processing.
*
* \param colliders A collection of all active colliders.
* \return A list of collision pairs with their associated data.
@@ -216,86 +209,77 @@ private:
gather_collisions(std::vector<CollisionInternal> & colliders);
/**
- * \brief Checks if two collision layers have at least one common layer.
+ * \brief Checks if the settings allow collision
*
- * 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.
+ * This function checks if there is any collison layer where each object is located in.
+ * After checking the layers it checks the names and at last the tags.
+ * if in all three sets nothing is found collision can not happen.
*
- * \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.
+ * \param this_rigidbody Rigidbody of first object
+ * \param other_rigidbody Rigidbody of second collider
+ * \param this_metadata Rigidbody of first object
+ * \param other_metadata Rigidbody of second object
+ * \return Returns true if there is at least one comparison found.
*/
-
- bool have_common_layer(const std::set<int> & layers1, const std::set<int> & layers2);
+ bool should_collide(const CollisionInternal & self,
+ const CollisionInternal & other) const; //done
/**
* \brief Checks for collision between two colliders.
*
- * Calls the appropriate collision detection function based on the collider types.
+ * This function determines whether two colliders are colliding based on their types.
+ * It calls the appropriate collision detection function based on the collider pair type and stores the collision resolution data.
+ * If a collision is detected, it returns true, otherwise false.
*
* \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;
+ bool detect_collision(CollisionInternal & first_info, CollisionInternal & second_info,
+ const CollisionInternalType & type);
/**
* \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.
+ * This function checks whether two `BoxCollider` are colliding based on their positions and scaled dimensions.
+ * If a collision is detected, it calculates the overlap along the X and Y axes and returns the resolution vector.
+ * If no collision is detected, it returns a vector with NaN values.
+
+ * \param box1 Information about the first BoxCollider.
+ * \param box2 Information about the second BoxCollider.
+ * \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}.
*/
- bool get_box_box_collision(const BoxCollider & box1, const BoxCollider & box2,
- const Transform & transform1, const Transform & transform2,
- const Rigidbody & rigidbody1,
- const Rigidbody & rigidbody2) const;
+ vec2 get_box_box_detection(const BoxColliderInternal & box1,
+ const BoxColliderInternal & box2) 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.
+ * This function detects if a collision occurs between a rectangular box and a circular collider.
+ * If a collision is detected, the function calculates the resolution vector to resolve the collision.
+ * If no collision is detected, it returns a vector with NaN values
+ *
+ * \param box1 Information about the BoxCollider.
+ * \param circle2 Information about the circleCollider.
+ * \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}.
*/
- bool get_box_circle_collision(const BoxCollider & box1, const CircleCollider & circle2,
- const Transform & transform1, const Transform & transform2,
- const Rigidbody & rigidbody1,
- const Rigidbody & rigidbody2) const;
+ vec2 get_box_circle_detection(const BoxColliderInternal & box1,
+ const CircleColliderInternal & circle2) 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.
+ * This function detects if a collision occurs between two circular colliders.
+ * If a collision is detected, it calculates the resolution vector to resolve the collision.
+ * If no collision is detected, it returns a vector with NaN values.
*
- * \return status of collision
+ * \param circle1 Information about the first circleCollider.
+ * \param circle2 Information about the second circleCollider.
+ * \return If colliding, returns the resolution vector; otherwise, returns {NaN, NaN}.
*/
- bool get_circle_circle_collision(const CircleCollider & circle1,
- const CircleCollider & circle2,
- const Transform & transform1,
- const Transform & transform2,
- const Rigidbody & rigidbody1,
- const Rigidbody & rigidbody2) const;
+ vec2 get_circle_circle_detection(const CircleColliderInternal & circle1,
+ const CircleColliderInternal & circle2) const;
};
/**
diff --git a/src/crepe/system/EventSystem.cpp b/src/crepe/system/EventSystem.cpp
new file mode 100644
index 0000000..7e168ab
--- /dev/null
+++ b/src/crepe/system/EventSystem.cpp
@@ -0,0 +1,9 @@
+#include "EventSystem.h"
+#include "../manager/EventManager.h"
+
+using namespace crepe;
+
+void EventSystem::fixed_update() {
+ EventManager & ev = this->mediator.event_manager;
+ ev.dispatch_events();
+}
diff --git a/src/crepe/system/EventSystem.h b/src/crepe/system/EventSystem.h
new file mode 100644
index 0000000..0ae48d2
--- /dev/null
+++ b/src/crepe/system/EventSystem.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include "System.h"
+
+namespace crepe {
+
+/**
+ * \brief EventManager dispatch helper system
+ */
+class EventSystem : public System {
+public:
+ using System::System;
+
+ /**
+ * \brief Dispatch queued events
+ * \see EventManager::dispatch_events
+ */
+ void fixed_update() override;
+};
+
+} // namespace crepe
diff --git a/src/crepe/system/InputSystem.cpp b/src/crepe/system/InputSystem.cpp
index fca540f..858a645 100644
--- a/src/crepe/system/InputSystem.cpp
+++ b/src/crepe/system/InputSystem.cpp
@@ -8,13 +8,11 @@
using namespace crepe;
-void InputSystem::update() {
+void InputSystem::fixed_update() {
ComponentManager & mgr = this->mediator.component_manager;
-
SDLContext & context = this->mediator.sdl_context;
std::vector<EventData> event_list = context.get_events();
- RefVector<Button> buttons = mgr.get_components_by_type<Button>();
- RefVector<Camera> cameras = mgr.get_components_by_type<Camera>();
+ const RefVector<Camera> cameras = mgr.get_components_by_type<Camera>();
OptionalRef<Camera> curr_cam_ref;
// Find the active camera
@@ -26,9 +24,8 @@ void InputSystem::update() {
if (!curr_cam_ref) return;
Camera & current_cam = curr_cam_ref;
- RefVector<Transform> transform_vec
- = mgr.get_components_by_id<Transform>(current_cam.game_object_id);
- Transform & cam_transform = transform_vec.front().get();
+ const Transform & cam_transform
+ = mgr.get_components_by_id<Transform>(current_cam.game_object_id).front();
vec2 camera_origin = cam_transform.position + current_cam.data.postion_offset
- (current_cam.viewport_size / 2);
@@ -52,7 +49,7 @@ void InputSystem::handle_mouse_event(const EventData & event, const vec2 & camer
EventManager & event_mgr = this->mediator.event_manager;
vec2 adjusted_mouse;
adjusted_mouse.x = event.data.mouse_data.mouse_position.x + camera_origin.x;
- adjusted_mouse.x = event.data.mouse_data.mouse_position.y + camera_origin.y;
+ adjusted_mouse.y = event.data.mouse_data.mouse_position.y + camera_origin.y;
// Check if the mouse is within the viewport
if ((adjusted_mouse.x < camera_origin.x
|| adjusted_mouse.x > camera_origin.x + current_cam.viewport_size.x
@@ -85,7 +82,8 @@ void InputSystem::handle_mouse_event(const EventData & event, const vec2 & camer
.mouse_pos = adjusted_mouse,
.button = event.data.mouse_data.mouse_button,
});
- this->handle_click(event.data.mouse_data.mouse_button, adjusted_mouse);
+ this->handle_click(event.data.mouse_data.mouse_button, adjusted_mouse,
+ current_cam);
}
break;
}
@@ -95,7 +93,7 @@ void InputSystem::handle_mouse_event(const EventData & event, const vec2 & camer
.mouse_pos = adjusted_mouse,
.mouse_delta = event.data.mouse_data.rel_mouse_move,
});
- this->handle_move(event, adjusted_mouse);
+ this->handle_move(event, adjusted_mouse, current_cam);
break;
case EventType::MOUSE_WHEEL:
@@ -153,63 +151,67 @@ void InputSystem::handle_non_mouse_event(const EventData & event) {
}
}
-void InputSystem::handle_move(const EventData & event_data, const vec2 & mouse_pos) {
+void InputSystem::handle_move(const EventData & event_data, const vec2 & mouse_pos,
+ const Camera & current_cam) {
ComponentManager & mgr = this->mediator.component_manager;
-
- RefVector<Button> buttons = mgr.get_components_by_type<Button>();
+ EventManager & event_mgr = this->mediator.event_manager;
+ const RefVector<Button> buttons = mgr.get_components_by_type<Button>();
for (Button & button : buttons) {
if (!button.active) continue;
- RefVector<Transform> transform_vec
- = mgr.get_components_by_id<Transform>(button.game_object_id);
- Transform & transform(transform_vec.front().get());
+ const Transform & transform
+ = mgr.get_components_by_id<Transform>(button.game_object_id).front();
+ const Transform & cam_transform
+ = mgr.get_components_by_id<Transform>(current_cam.game_object_id).front();
+ const Metadata & metadata
+ = mgr.get_components_by_id<Metadata>(button.game_object_id).front();
bool was_hovering = button.hover;
- if (this->is_mouse_inside_button(mouse_pos, button, transform)) {
+
+ if (this->is_mouse_inside_button(mouse_pos, button, transform, cam_transform)) {
button.hover = true;
- if (!button.on_mouse_enter) continue;
if (!was_hovering) {
- button.on_mouse_enter();
+ event_mgr.trigger_event<ButtonEnterEvent>(metadata);
}
} else {
button.hover = false;
- // Trigger the on_exit callback if the hover state just changed to false
- if (!button.on_mouse_exit) continue;
if (was_hovering) {
- button.on_mouse_exit();
+ event_mgr.trigger_event<ButtonExitEvent>(metadata);
}
}
}
}
-void InputSystem::handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos) {
+void InputSystem::handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos,
+ const Camera & current_cam) {
ComponentManager & mgr = this->mediator.component_manager;
-
- RefVector<Button> buttons = mgr.get_components_by_type<Button>();
-
+ EventManager & event_mgr = this->mediator.event_manager;
+ const RefVector<Button> buttons = mgr.get_components_by_type<Button>();
+ const Transform & cam_transform
+ = mgr.get_components_by_id<Transform>(current_cam.game_object_id).front();
for (Button & button : buttons) {
if (!button.active) continue;
- if (!button.on_click) continue;
- RefVector<Transform> transform_vec
- = mgr.get_components_by_id<Transform>(button.game_object_id);
- Transform & transform = transform_vec.front().get();
-
- if (this->is_mouse_inside_button(mouse_pos, button, transform)) {
-
- button.on_click();
+ const Metadata & metadata
+ = mgr.get_components_by_id<Metadata>(button.game_object_id).front();
+ const Transform & transform
+ = mgr.get_components_by_id<Transform>(button.game_object_id).front();
+ if (this->is_mouse_inside_button(mouse_pos, button, transform, cam_transform)) {
+ event_mgr.trigger_event<ButtonPressEvent>(metadata);
}
}
}
bool InputSystem::is_mouse_inside_button(const vec2 & mouse_pos, const Button & button,
- const Transform & transform) {
- int actual_x = transform.position.x + button.offset.x;
- int actual_y = transform.position.y + button.offset.y;
-
- int half_width = button.dimensions.x / 2;
- int half_height = button.dimensions.y / 2;
+ const Transform & transform,
+ const Transform & cam_transform) {
+ vec2 actual_pos = transform.position + button.offset;
+ if (!button.world_space) {
+ actual_pos += cam_transform.position;
+ }
+ vec2 half_dimensions = button.dimensions / 2;
- // Check if the mouse is within the button's boundaries
- return mouse_pos.x >= actual_x - half_width && mouse_pos.x <= actual_x + half_width
- && mouse_pos.y >= actual_y - half_height && mouse_pos.y <= actual_y + half_height;
+ return mouse_pos.x >= actual_pos.x - half_dimensions.x
+ && mouse_pos.x <= actual_pos.x + half_dimensions.x
+ && mouse_pos.y >= actual_pos.y - half_dimensions.y
+ && mouse_pos.y <= actual_pos.y + half_dimensions.y;
}
diff --git a/src/crepe/system/InputSystem.h b/src/crepe/system/InputSystem.h
index eefd9fe..2cb80e5 100644
--- a/src/crepe/system/InputSystem.h
+++ b/src/crepe/system/InputSystem.h
@@ -3,6 +3,8 @@
#include "../api/Config.h"
#include "../facade/EventData.h"
+#include "../api/Event.h"
+#include "../api/Metadata.h"
#include "../types.h"
#include "../util/OptionalRef.h"
@@ -13,6 +15,37 @@ namespace crepe {
class Camera;
class Button;
class Transform;
+//! Event triggered when a button is clicked
+class ButtonPressEvent : public Event {
+public:
+ //! Metadata of the button.
+ const Metadata & metadata;
+ /**
+ * \param metadata Metadata of the button pressed
+ */
+ ButtonPressEvent(const Metadata & metadata) : metadata(metadata){};
+};
+//! Event triggered when the mouse enters a button
+class ButtonEnterEvent : public Event {
+public:
+ //! Metadata of the button.
+ const Metadata & metadata;
+ /**
+ * \param metadata Metadata of the button pressed
+ */
+ ButtonEnterEvent(const Metadata & metadata) : metadata(metadata){};
+};
+//! Event triggered when the mouse leaves a button
+class ButtonExitEvent : public Event {
+public:
+ //! Metadata of the button.
+ const Metadata & metadata;
+ /**
+ * \param metadata Metadata of the button pressed
+ */
+ ButtonExitEvent(const Metadata & metadata) : metadata(metadata){};
+};
+
/**
* \brief Handles the processing of input events created by SDLContext
*
@@ -28,7 +61,7 @@ public:
* \brief Updates the system, processing all input events.
* This method processes all events and triggers corresponding actions.
*/
- void update() override;
+ void fixed_update() override;
private:
//! Stores the last position of the mouse when the button was pressed.
@@ -61,20 +94,24 @@ private:
* \param mouse_button The mouse button involved in the click.
* \param world_mouse_x The X coordinate of the mouse in world space.
* \param world_mouse_y The Y coordinate of the mouse in world space.
+ * \param current_cam The current active camera.
*
* This method processes the mouse click event and triggers the corresponding button action.
*/
- void handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos);
+ void handle_click(const MouseButton & mouse_button, const vec2 & mouse_pos,
+ const Camera & current_cam);
/**
* \brief Handles the mouse movement event.
* \param event_data The event data containing information about the mouse movement.
* \param world_mouse_x The X coordinate of the mouse in world space.
* \param world_mouse_y The Y coordinate of the mouse in world space.
+ * \param current_cam The current active camera.
*
* This method processes the mouse movement event and updates the button hover state.
*/
- void handle_move(const EventData & event_data, const vec2 & mouse_pos);
+ void handle_move(const EventData & event_data, const vec2 & mouse_pos,
+ const Camera & current_cam);
/**
* \brief Checks if the mouse position is inside the bounds of the button.
@@ -82,10 +119,11 @@ private:
* \param world_mouse_y The Y coordinate of the mouse in world space.
* \param button The button to check.
* \param transform The transform component of the button.
+ * \param cam_transform the transform of the current active camera
* \return True if the mouse is inside the button, false otherwise.
*/
bool is_mouse_inside_button(const vec2 & mouse_pos, const Button & button,
- const Transform & transform);
+ const Transform & transform, const Transform & cam_transform);
/**
* \brief Handles the button press event, calling the on_click callback if necessary.
diff --git a/src/crepe/system/ParticleSystem.cpp b/src/crepe/system/ParticleSystem.cpp
index 35a1d41..5e575e4 100644
--- a/src/crepe/system/ParticleSystem.cpp
+++ b/src/crepe/system/ParticleSystem.cpp
@@ -7,12 +7,13 @@
#include "../api/Transform.h"
#include "../manager/ComponentManager.h"
#include "../manager/LoopTimerManager.h"
+#include "util/AbsolutePosition.h"
#include "ParticleSystem.h"
using namespace crepe;
-void ParticleSystem::update() {
+void ParticleSystem::fixed_update() {
// Get all emitters
const Mediator & mediator = this->mediator;
LoopTimerManager & loop_timer = mediator.loop_timer;
@@ -48,9 +49,10 @@ void ParticleSystem::update() {
void ParticleSystem::emit_particle(ParticleEmitter & emitter, const Transform & transform) {
constexpr float DEG_TO_RAD = M_PI / 180.0;
- vec2 initial_position = emitter.data.offset + transform.position;
+ vec2 initial_position = AbsolutePosition::get_position(transform, emitter.data.offset);
float random_angle
- = this->generate_random_angle(emitter.data.min_angle, emitter.data.max_angle);
+ = this->generate_random_angle(emitter.data.min_angle + transform.rotation,
+ emitter.data.max_angle + transform.rotation);
float random_speed
= this->generate_random_speed(emitter.data.min_speed, emitter.data.max_speed);
diff --git a/src/crepe/system/ParticleSystem.h b/src/crepe/system/ParticleSystem.h
index 154521d..4296ff3 100644
--- a/src/crepe/system/ParticleSystem.h
+++ b/src/crepe/system/ParticleSystem.h
@@ -20,7 +20,7 @@ public:
* \brief Updates all particle emitters by emitting particles, updating particle states, and
* checking bounds.
*/
- void update() override;
+ void fixed_update() override;
private:
/**
diff --git a/src/crepe/system/PhysicsSystem.cpp b/src/crepe/system/PhysicsSystem.cpp
index 3b3b8ab..62f8132 100644
--- a/src/crepe/system/PhysicsSystem.cpp
+++ b/src/crepe/system/PhysicsSystem.cpp
@@ -12,8 +12,7 @@
using namespace crepe;
-void PhysicsSystem::update() {
-
+void PhysicsSystem::fixed_update() {
const Mediator & mediator = this->mediator;
ComponentManager & mgr = mediator.component_manager;
LoopTimerManager & loop_timer = mediator.loop_timer;
diff --git a/src/crepe/system/PhysicsSystem.h b/src/crepe/system/PhysicsSystem.h
index 26152a5..5ed624f 100644
--- a/src/crepe/system/PhysicsSystem.h
+++ b/src/crepe/system/PhysicsSystem.h
@@ -18,7 +18,7 @@ public:
*
* It calculates new velocties and changes the postion in the transform.
*/
- void update() override;
+ void fixed_update() override;
};
} // namespace crepe
diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp
index 505433a..698301e 100644
--- a/src/crepe/system/RenderSystem.cpp
+++ b/src/crepe/system/RenderSystem.cpp
@@ -2,17 +2,23 @@
#include <cassert>
#include <cmath>
#include <functional>
+#include <optional>
#include <stdexcept>
#include <vector>
#include "../api/Camera.h"
#include "../api/ParticleEmitter.h"
#include "../api/Sprite.h"
+#include "../api/Text.h"
#include "../api/Transform.h"
+#include "../facade/Font.h"
#include "../facade/SDLContext.h"
#include "../facade/Texture.h"
#include "../manager/ComponentManager.h"
#include "../manager/ResourceManager.h"
+#include "api/Text.h"
+#include "facade/Font.h"
+#include "util/AbsolutePosition.h"
#include "RenderSystem.h"
#include "types.h"
@@ -64,14 +70,37 @@ RefVector<Sprite> RenderSystem::sort(RefVector<Sprite> & objs) const {
return sorted_objs;
}
-void RenderSystem::update() {
+void RenderSystem::frame_update() {
this->clear_screen();
this->render();
+ this->render_text();
this->present_screen();
}
-bool RenderSystem::render_particle(const Sprite & sprite, const double & scale) {
+void RenderSystem::render_text() {
+ SDLContext & ctx = this->mediator.sdl_context;
+ ComponentManager & mgr = this->mediator.component_manager;
+ ResourceManager & resource_manager = this->mediator.resource_manager;
+
+ RefVector<Text> texts = mgr.get_components_by_type<Text>();
+
+ for (Text & text : texts) {
+ if (!text.active) continue;
+ if (!text.font.has_value())
+ text.font.emplace(ctx.get_font_from_name(text.font_family));
+
+ const Font & font = resource_manager.get<Font>(text.font.value());
+ const auto & transform
+ = mgr.get_components_by_id<Transform>(text.game_object_id).front().get();
+ ctx.draw_text(SDLContext::RenderText{
+ .text = text,
+ .font = font,
+ .transform = transform,
+ });
+ }
+}
+bool RenderSystem::render_particle(const Sprite & sprite, const Transform & transform) {
ComponentManager & mgr = this->mediator.component_manager;
SDLContext & ctx = this->mediator.sdl_context;
ResourceManager & resource_manager = this->mediator.resource_manager;
@@ -89,29 +118,30 @@ bool RenderSystem::render_particle(const Sprite & sprite, const double & scale)
for (const Particle & p : em.particles) {
if (!p.active) continue;
+ if (p.time_in_life < em.data.begin_lifespan) continue;
ctx.draw(SDLContext::RenderContext{
.sprite = sprite,
.texture = res,
.pos = p.position,
- .angle = p.angle,
- .scale = scale,
+ .angle = p.angle + transform.rotation,
+ .scale = transform.scale,
});
}
}
return rendering_particles;
}
-void RenderSystem::render_normal(const Sprite & sprite, const Transform & tm) {
+void RenderSystem::render_normal(const Sprite & sprite, const Transform & transform) {
SDLContext & ctx = this->mediator.sdl_context;
ResourceManager & resource_manager = this->mediator.resource_manager;
const Texture & res = resource_manager.get<Texture>(sprite.source);
-
+ vec2 pos = AbsolutePosition::get_position(transform, sprite.data.position_offset);
ctx.draw(SDLContext::RenderContext{
.sprite = sprite,
.texture = res,
- .pos = tm.position,
- .angle = tm.rotation,
- .scale = tm.scale,
+ .pos = pos,
+ .angle = transform.rotation,
+ .scale = transform.scale,
});
}
@@ -120,14 +150,16 @@ void RenderSystem::render() {
this->update_camera();
RefVector<Sprite> sprites = mgr.get_components_by_type<Sprite>();
+ ResourceManager & resource_manager = this->mediator.resource_manager;
RefVector<Sprite> sorted_sprites = this->sort(sprites);
+ RefVector<Text> text_components = mgr.get_components_by_type<Text>();
for (const Sprite & sprite : sorted_sprites) {
if (!sprite.active) continue;
const Transform & transform
= mgr.get_components_by_id<Transform>(sprite.game_object_id).front().get();
- bool rendered_particles = this->render_particle(sprite, transform.scale);
+ bool rendered_particles = this->render_particle(sprite, transform);
if (rendered_particles) continue;
diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h
index fc7b46e..627a743 100644
--- a/src/crepe/system/RenderSystem.h
+++ b/src/crepe/system/RenderSystem.h
@@ -10,7 +10,7 @@ namespace crepe {
class Camera;
class Sprite;
class Transform;
-
+class Text;
/**
* \brief Manages rendering operations for all game objects.
*
@@ -24,7 +24,7 @@ public:
* \brief Updates the RenderSystem for the current frame.
* This method is called to perform all rendering operations for the current game frame.
*/
- void update() override;
+ void frame_update() override;
private:
//! Clears the screen in preparation for rendering.
@@ -36,28 +36,28 @@ private:
//! Updates the active camera used for rendering.
void update_camera();
- //! Renders the whole screen
+ //! Renders all the sprites and particles
void render();
+ //! Renders all Text components
+ void render_text();
+
+private:
/**
* \brief Renders all the particles on the screen from a given sprite.
*
* \param sprite renders the particles with given texture
- * \param tm the Transform component for scale. This is not a const reference because each
- * particle has a position and rotation that needs to overwrite the transform position and
- * rotation without overwriting the current transform. and because the transform
- * constructor is now protected i cannot make tmp inside
+ * \param transform the component that holds the position, rotation, and scale.
* \return true if particles have been rendered
*/
- bool render_particle(const Sprite & sprite, const double & scale);
-
+ bool render_particle(const Sprite & sprite, const Transform & transform);
/**
* \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 transform the Transform component that holds the position,rotation and scale
*/
- void render_normal(const Sprite & sprite, const Transform & tm);
+ void render_normal(const Sprite & sprite, const Transform & transform);
/**
* \brief sort a vector sprite objects with
@@ -66,12 +66,6 @@ private:
* \return returns a sorted reference vector
*/
RefVector<Sprite> sort(RefVector<Sprite> & objs) const;
-
- /**
- * \todo Add text rendering using SDL_ttf for text components.
- * \todo Implement a text component and a button component.
- * \todo Consider adding text input functionality.
- */
};
} // namespace crepe
diff --git a/src/crepe/system/ReplaySystem.cpp b/src/crepe/system/ReplaySystem.cpp
new file mode 100644
index 0000000..efc3be4
--- /dev/null
+++ b/src/crepe/system/ReplaySystem.cpp
@@ -0,0 +1,54 @@
+#include "../manager/ReplayManager.h"
+#include "../manager/SystemManager.h"
+
+#include "EventSystem.h"
+#include "RenderSystem.h"
+#include "ReplaySystem.h"
+
+using namespace crepe;
+using namespace std;
+
+void ReplaySystem::fixed_update() {
+ ReplayManager & replay = this->mediator.replay_manager;
+ ReplayManager::State state = replay.get_state();
+ ReplayManager::State last_state = this->last_state;
+ this->last_state = state;
+
+ switch (state) {
+ case ReplayManager::IDLE:
+ break;
+ case ReplayManager::RECORDING: {
+ replay.frame_record();
+ break;
+ }
+ case ReplayManager::PLAYING: {
+ if (last_state != ReplayManager::PLAYING) this->playback_begin();
+ bool last = replay.frame_step();
+ if (last) this->playback_end();
+ break;
+ }
+ }
+}
+
+void ReplaySystem::playback_begin() {
+ SystemManager & systems = this->mediator.system_manager;
+ ComponentManager & components = this->mediator.component_manager;
+
+ this->playback = {
+ .components = components.save(),
+ .systems = systems.save(),
+ };
+
+ systems.disable_all();
+ systems.get_system<RenderSystem>().active = true;
+ systems.get_system<ReplaySystem>().active = true;
+ systems.get_system<EventSystem>().active = true;
+}
+
+void ReplaySystem::playback_end() {
+ SystemManager & systems = this->mediator.system_manager;
+ ComponentManager & components = this->mediator.component_manager;
+
+ components.restore(this->playback.components);
+ systems.restore(this->playback.systems);
+}
diff --git a/src/crepe/system/ReplaySystem.h b/src/crepe/system/ReplaySystem.h
new file mode 100644
index 0000000..bbc8d76
--- /dev/null
+++ b/src/crepe/system/ReplaySystem.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "../manager/ReplayManager.h"
+#include "../manager/SystemManager.h"
+
+#include "System.h"
+
+namespace crepe {
+
+/**
+ * \brief ReplayManager helper system
+ *
+ * This system records and replays recordings using ReplayManager.
+ */
+class ReplaySystem : public System {
+public:
+ using System::System;
+
+ void fixed_update() override;
+
+private:
+ //! Last ReplayManager state
+ ReplayManager::State last_state = ReplayManager::IDLE;
+
+ /**
+ * \brief Playback snapshot
+ *
+ * When starting playback, the component state is saved and most systems are disabled. This
+ * struct stores the engine state before ReplayManager::play is called.
+ */
+ struct Snapshot {
+ ComponentManager::Snapshot components;
+ SystemManager::Snapshot systems;
+ };
+ //! Before playback snapshot
+ Snapshot playback;
+
+ //! Snapshot state and disable systems during playback
+ void playback_begin();
+ //! Restore state from before \c playback_begin()
+ void playback_end();
+};
+
+} // namespace crepe
diff --git a/src/crepe/system/ScriptSystem.cpp b/src/crepe/system/ScriptSystem.cpp
index d6b2ca1..93b4853 100644
--- a/src/crepe/system/ScriptSystem.cpp
+++ b/src/crepe/system/ScriptSystem.cpp
@@ -7,9 +7,20 @@
using namespace std;
using namespace crepe;
-void ScriptSystem::update() {
- dbg_trace();
+void ScriptSystem::fixed_update() {
+ LoopTimerManager & timer = this->mediator.loop_timer;
+ duration_t delta_time = timer.get_scaled_fixed_delta_time();
+ this->update(&Script::fixed_update, delta_time);
+}
+
+void ScriptSystem::frame_update() {
+ LoopTimerManager & timer = this->mediator.loop_timer;
+ duration_t delta_time = timer.get_delta_time();
+ this->update(&Script::frame_update, delta_time);
+}
+void ScriptSystem::update(void (Script::*update_function)(duration_t),
+ const duration_t & delta_time) {
ComponentManager & mgr = this->mediator.component_manager;
RefVector<BehaviorScript> behavior_scripts = mgr.get_components_by_type<BehaviorScript>();
@@ -23,6 +34,7 @@ void ScriptSystem::update() {
script->init();
script->initialized = true;
}
- script->update();
+
+ (*script.*update_function)(delta_time);
}
}
diff --git a/src/crepe/system/ScriptSystem.h b/src/crepe/system/ScriptSystem.h
index 3db1b1e..257b615 100644
--- a/src/crepe/system/ScriptSystem.h
+++ b/src/crepe/system/ScriptSystem.h
@@ -1,5 +1,7 @@
#pragma once
+#include "../manager/LoopTimerManager.h"
+
#include "System.h"
namespace crepe {
@@ -9,20 +11,27 @@ 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.
+ * The script system is responsible for all \c BehaviorScript components, and calls the methods
+ * on classes derived from \c Script.
*/
class ScriptSystem : public System {
public:
using System::System;
+
+public:
+ //! Call Script::fixed_update() on all active \c BehaviorScript instances
+ void fixed_update() override;
+ //! Call Script::frame_update() on all active \c BehaviorScript instances
+ void frame_update() override;
+
+private:
/**
- * \brief Call Script::update() on all active \c BehaviorScript instances
+ * \brief Call Script `*_update` member function on all active \c BehaviorScript instances
*
- * This routine updates all scripts sequentially using the Script::update()
- * method. It also calls Script::init() if this has not been done before on
- * the \c BehaviorScript instance.
+ * \note This routine also calls Script::init() if this has not been done before on the \c
+ * BehaviorScript instance.
*/
- void update() override;
+ void update(void (Script::*update_function)(duration_t), const duration_t & delta_time);
};
} // namespace crepe
diff --git a/src/crepe/system/System.h b/src/crepe/system/System.h
index 063dfbf..e2ce7eb 100644
--- a/src/crepe/system/System.h
+++ b/src/crepe/system/System.h
@@ -14,10 +14,12 @@ class ComponentManager;
*/
class System {
public:
- /**
- * \brief Process all components this system is responsible for.
- */
- virtual void update() = 0;
+ //! Code that runs in the fixed loop
+ virtual void fixed_update() {};
+ //! Code that runs in the frame loop
+ virtual void frame_update() {};
+ //! Indicates that the update functions of this system should be run
+ bool active = true;
public:
System(const Mediator & m);