aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/crepe/Collider.cpp2
-rw-r--r--src/crepe/Collider.h7
-rw-r--r--src/crepe/api/BoxCollider.cpp7
-rw-r--r--src/crepe/api/BoxCollider.h24
-rw-r--r--src/crepe/api/CMakeLists.txt14
-rw-r--r--src/crepe/api/CircleCollider.cpp6
-rw-r--r--src/crepe/api/CircleCollider.h17
-rw-r--r--src/crepe/api/Event.h59
-rw-r--r--src/crepe/api/EventHandler.cpp2
-rw-r--r--src/crepe/api/EventHandler.h112
-rw-r--r--src/crepe/api/EventManager.cpp81
-rw-r--r--src/crepe/api/EventManager.h122
-rw-r--r--src/crepe/api/EventManager.hpp140
-rw-r--r--src/crepe/api/IKeyListener.cpp51
-rw-r--r--src/crepe/api/IKeyListener.h78
-rw-r--r--src/crepe/api/IMouseListener.cpp64
-rw-r--r--src/crepe/api/IMouseListener.h117
-rw-r--r--src/crepe/api/KeyCodes.h146
-rw-r--r--src/crepe/api/LoopManager.cpp13
-rw-r--r--src/crepe/api/Rigidbody.h10
-rw-r--r--src/crepe/api/Vector2.h3
-rw-r--r--src/crepe/facade/SDLContext.cpp4
-rw-r--r--src/crepe/system/CollisionSystem.cpp361
-rw-r--r--src/crepe/system/CollisionSystem.h186
-rw-r--r--src/example/CMakeLists.txt2
-rw-r--r--src/example/collision.cpp123
-rw-r--r--src/example/events.cpp113
-rw-r--r--src/test/CMakeLists.txt3
-rw-r--r--src/test/CollisionTest.cpp83
29 files changed, 1931 insertions, 19 deletions
diff --git a/src/crepe/Collider.cpp b/src/crepe/Collider.cpp
index bbec488..0706371 100644
--- a/src/crepe/Collider.cpp
+++ b/src/crepe/Collider.cpp
@@ -2,4 +2,4 @@
using namespace crepe;
-Collider::Collider(game_object_id_t id) : Component(id) {}
+Collider::Collider(game_object_id_t id, Vector2 offset) : Component(id), offset(offset) {}
diff --git a/src/crepe/Collider.h b/src/crepe/Collider.h
index 827f83d..e910ae4 100644
--- a/src/crepe/Collider.h
+++ b/src/crepe/Collider.h
@@ -1,14 +1,17 @@
#pragma once
+#include "api/Vector2.h"
+
#include "Component.h"
namespace crepe {
class Collider : public Component {
public:
- Collider(game_object_id_t id);
+ Collider(game_object_id_t id, Vector2 offset);
- int size;
+ //! Offset of the collider relative to rigidbody position
+ Vector2 offset;
};
} // namespace crepe
diff --git a/src/crepe/api/BoxCollider.cpp b/src/crepe/api/BoxCollider.cpp
new file mode 100644
index 0000000..eafbdb2
--- /dev/null
+++ b/src/crepe/api/BoxCollider.cpp
@@ -0,0 +1,7 @@
+#include "BoxCollider.h"
+#include "../Collider.h"
+
+using namespace crepe;
+
+
+BoxCollider::BoxCollider(game_object_id_t game_object_id,Vector2 offset, double width, double height) : Collider(game_object_id,offset), width(width), height(height) {}
diff --git a/src/crepe/api/BoxCollider.h b/src/crepe/api/BoxCollider.h
new file mode 100644
index 0000000..7f51cba
--- /dev/null
+++ b/src/crepe/api/BoxCollider.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include "Vector2.h"
+#include "../Collider.h"
+
+namespace crepe {
+
+/**
+ * \brief A class representing a box-shaped collider.
+ *
+ * This class is used for collision detection with other colliders (e.g., CircleCollider).
+ */
+class BoxCollider : public Collider {
+public:
+ BoxCollider(game_object_id_t game_object_id,Vector2 offset, double width, double height);
+
+ //! Width of box collider
+ double width;
+
+ //! Height of box collider
+ double height;
+};
+
+} // namespace crepe
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index f9b370f..07b3a82 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -17,6 +17,12 @@ target_sources(crepe PUBLIC
Vector2.cpp
Camera.cpp
Animator.cpp
+ BoxCollider.cpp
+ CircleCollider.cpp
+ EventManager.cpp
+ EventHandler.cpp
+ IKeyListener.cpp
+ IMouseListener.cpp
LoopManager.cpp
LoopTimer.cpp
)
@@ -43,6 +49,14 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
SceneManager.hpp
Camera.h
Animator.h
+ BoxCollider.h
+ CircleCollider.h
+ EventManager.h
+ EventManager.hpp
+ EventHandler.h
+ Event.h
+ IKeyListener.h
+ IMouseListener.h
LoopManager.h
LoopTimer.h
)
diff --git a/src/crepe/api/CircleCollider.cpp b/src/crepe/api/CircleCollider.cpp
new file mode 100644
index 0000000..04a4995
--- /dev/null
+++ b/src/crepe/api/CircleCollider.cpp
@@ -0,0 +1,6 @@
+#include "CircleCollider.h"
+
+using namespace crepe;
+
+
+CircleCollider::CircleCollider(game_object_id_t game_object_id,Vector2 offset, int radius) : Collider(game_object_id,offset), radius(radius) {}
diff --git a/src/crepe/api/CircleCollider.h b/src/crepe/api/CircleCollider.h
index e77a592..4e04fa4 100644
--- a/src/crepe/api/CircleCollider.h
+++ b/src/crepe/api/CircleCollider.h
@@ -1,14 +1,23 @@
#pragma once
+
+#include "Vector2.h"
+
#include "../Collider.h"
namespace crepe {
+/**
+ * \brief A class representing a circle-shaped collider.
+ *
+ * This class is used for collision detection with other colliders (e.g., BoxCollider).
+ */
class CircleCollider : public Collider {
public:
- CircleCollider(game_object_id_t game_object_id, int radius)
- : Collider(game_object_id),
- radius(radius) {}
- int radius;
+
+ CircleCollider(game_object_id_t game_object_id,Vector2 offset, int radius);
+
+ //! Radius of the circle collider.
+ double radius;
};
} // namespace crepe
diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h
new file mode 100644
index 0000000..bd6a541
--- /dev/null
+++ b/src/crepe/api/Event.h
@@ -0,0 +1,59 @@
+#pragma once
+#include "KeyCodes.h"
+#include <iostream>
+#include <string>
+#include <typeindex>
+#include "system/CollisionSystem.h"
+
+class Event {
+public:
+};
+
+class KeyPressEvent : public Event {
+public:
+ int repeat = 0;
+ Keycode key = Keycode::NONE;
+};
+
+class KeyReleaseEvent : public Event {
+public:
+ Keycode key = Keycode::NONE;
+};
+
+class MousePressEvent : public Event {
+public:
+ int mouse_x = 0;
+ int mouse_y = 0;
+ MouseButton button = MouseButton::NONE;
+};
+
+class MouseClickEvent : public Event {
+public:
+ int mouse_x = 0;
+ int mouse_y = 0;
+ MouseButton button = MouseButton::NONE;
+};
+class MouseReleaseEvent : public Event {
+public:
+ int mouse_x = 0;
+ int mouse_y = 0;
+ MouseButton button = MouseButton::NONE;
+};
+class MouseMoveEvent : public Event {
+public:
+ int mouse_x = 0;
+ int mouse_y = 0;
+};
+class CollisionEvent : public Event {
+public:
+ crepe::CollisionSystem::CollisionInfo info;
+ CollisionEvent(const crepe::CollisionSystem::CollisionInfo& collisionInfo)
+ : info(collisionInfo) {}
+};
+class TextSubmitEvent : public Event {
+public:
+ std::string text = "";
+};
+class ShutDownEvent : public Event {
+public:
+};
diff --git a/src/crepe/api/EventHandler.cpp b/src/crepe/api/EventHandler.cpp
new file mode 100644
index 0000000..93a116a
--- /dev/null
+++ b/src/crepe/api/EventHandler.cpp
@@ -0,0 +1,2 @@
+#include "EventHandler.h"
+bool IEventHandlerWrapper::exec(const Event & e) { return call(e); }
diff --git a/src/crepe/api/EventHandler.h b/src/crepe/api/EventHandler.h
new file mode 100644
index 0000000..0ab90de
--- /dev/null
+++ b/src/crepe/api/EventHandler.h
@@ -0,0 +1,112 @@
+#pragma once
+#include "Event.h"
+#include <functional>
+#include <iostream>
+#include <typeindex>
+
+/**
+ * \brief A type alias for an event handler function.
+ *
+ * The EventHandler is a std::function that takes an EventType reference and returns a boolean value
+ * indicating whether the event is handled.
+ *
+ * \tparam EventType The type of event this handler will handle.
+ */
+// TODO: typedef
+template <typename EventType>
+using EventHandler = std::function<bool(const EventType & e)>;
+
+/**
+ * \class IEventHandlerWrapper
+ * \brief An abstract base class for event handler wrappers.
+ *
+ * This class provides the interface for handling events. Derived classes must implement the
+ * `call()` method to process events and the `get_type()` method to return the handler's type.
+ */
+class IEventHandlerWrapper {
+public:
+ /**
+ * \brief Virtual destructor for IEventHandlerWrapper.
+ */
+ virtual ~IEventHandlerWrapper() = default;
+
+ /**
+ * \brief Executes the handler with the given event.
+ *
+ * This method calls the `call()` method of the derived class, passing the event to the handler.
+ *
+ * \param e The event to be processed.
+ * \return A boolean value indicating whether the event is handled.
+ */
+ bool exec(const Event & e);
+
+ /**
+ * \brief Get the type of the event handler.
+ *
+ * This method returns the type of the event handler as a string.
+ *
+ * \return A string representing the handler's type.
+ */
+ virtual std::string get_type() const = 0;
+
+private:
+ /**
+ * \brief The method responsible for handling the event.
+ *
+ * This method is implemented by derived classes to process the event.
+ *
+ * \param e The event to be processed.
+ * \return A boolean value indicating whether the event is handled.
+ */
+ virtual bool call(const Event & e) = 0;
+};
+
+/**
+ * \class EventHandlerWrapper
+ * \brief A wrapper for event handler functions.
+ *
+ * This class wraps an event handler function of a specific event type. It implements the
+ * `call()` and `get_type()` methods to allow the handler to be executed and its type to be
+ * queried.
+ *
+ * \tparam EventType The type of event this handler will handle.
+ */
+template <typename EventType>
+class EventHandlerWrapper : public IEventHandlerWrapper {
+public:
+ /**
+ * \brief Constructs an EventHandlerWrapper with a given handler.
+ *
+ * The constructor takes an event handler function and stores it in the wrapper.
+ *
+ * \param handler The event handler function.
+ */
+ explicit EventHandlerWrapper(const EventHandler<EventType> & handler)
+ : m_handler(handler), m_handler_type(m_handler.target_type().name()) {}
+
+private:
+ /**
+ * \brief Calls the stored event handler with the event.
+ *
+ * This method casts the event to the appropriate type and calls the handler.
+ *
+ * \param e The event to be handled.
+ * \return A boolean value indicating whether the event is handled.
+ */
+ bool call(const Event & e) override {
+ return m_handler(static_cast<const EventType &>(e));
+ }
+
+ /**
+ * \brief Returns the type of the handler.
+ *
+ * This method returns a string representing the type of the event handler.
+ *
+ * \return The handler type as a string.
+ */
+ std::string get_type() const override { return m_handler_type; }
+ //! The event handler function.
+ EventHandler<EventType> m_handler;
+ //! The type name of the handler function.
+ const std::string m_handler_type;
+};
diff --git a/src/crepe/api/EventManager.cpp b/src/crepe/api/EventManager.cpp
new file mode 100644
index 0000000..e881d49
--- /dev/null
+++ b/src/crepe/api/EventManager.cpp
@@ -0,0 +1,81 @@
+#include "EventManager.h"
+
+using namespace crepe;
+
+EventManager & EventManager::get_instance() {
+ static EventManager instance;
+ return instance;
+}
+
+void EventManager::dispatch_events() {
+ for (std::vector<std::tuple<std::unique_ptr<Event>, int,
+ std::type_index>>::iterator event_it
+ = this->events_queue.begin();
+ event_it != this->events_queue.end();) {
+ std::unique_ptr<Event> & event = std::get<0>(*event_it);
+ int channel = std::get<1>(*event_it);
+ std::type_index event_type = std::get<2>(*event_it);
+
+ bool event_handled = false;
+
+ if (channel) {
+ std::unordered_map<
+ std::type_index,
+ std::unordered_map<
+ int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>>::
+ iterator handlers_it
+ = subscribers_by_event_id.find(event_type);
+ if (handlers_it != subscribers_by_event_id.end()) {
+ std::unordered_map<
+ int, std::vector<std::unique_ptr<IEventHandlerWrapper>>> &
+ handlers_map
+ = handlers_it->second;
+ std::unordered_map<
+ int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>::
+ iterator handlers
+ = handlers_map.find(channel);
+ if (handlers != handlers_map.end()) {
+ std::vector<std::unique_ptr<IEventHandlerWrapper>> &
+ callbacks
+ = handlers->second;
+ for (std::vector<std::unique_ptr<IEventHandlerWrapper>>::
+ iterator handler_it
+ = callbacks.begin();
+ handler_it != callbacks.end(); ++handler_it) {
+ if ((*handler_it)->exec(*event)) {
+ event_it = events_queue.erase(event_it);
+ event_handled = true;
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ // Handle event for all channels
+ std::unordered_map<
+ std::type_index,
+ std::vector<std::unique_ptr<IEventHandlerWrapper>>>::iterator
+ handlers_it
+ = this->subscribers.find(event_type);
+ if (handlers_it != this->subscribers.end()) {
+ std::vector<std::unique_ptr<IEventHandlerWrapper>> & handlers
+ = handlers_it->second;
+ for (std::vector<std::unique_ptr<IEventHandlerWrapper>>::
+ iterator handler_it
+ = handlers.begin();
+ handler_it != handlers.end(); ++handler_it) {
+ // remove event from queue since and continue when callback returns true
+ if ((*handler_it)->exec(*event)) {
+ event_it = this->events_queue.erase(event_it);
+ event_handled = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!event_handled) {
+ ++event_it;
+ }
+ }
+}
diff --git a/src/crepe/api/EventManager.h b/src/crepe/api/EventManager.h
new file mode 100644
index 0000000..38d2e64
--- /dev/null
+++ b/src/crepe/api/EventManager.h
@@ -0,0 +1,122 @@
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <type_traits>
+#include <typeindex>
+#include <unordered_map>
+#include <vector>
+
+#include "Event.h"
+#include "EventHandler.h"
+
+
+namespace crepe {
+/**
+ * \class EventManager
+ * \brief The EventManager class is responsible for managing the subscription, triggering,
+ * and queueing of events. It handles events and dispatches them to appropriate subscribers.
+ */
+class EventManager {
+public:
+ /**
+ * \brief Deleted copy constructor to prevent copying of the EventManager instance.
+ */
+ EventManager(const EventManager &) = delete;
+
+ /**
+ * \brief Deleted copy assignment operator to prevent assignment of the EventManager instance.
+ */
+ const EventManager & operator=(const EventManager &) = delete;
+
+ /**
+ * \brief Get the singleton instance of the EventManager.
+ *
+ * This method returns the unique instance of the EventManager, creating it on the first call.
+ *
+ * \return Reference to the EventManager instance.
+ */
+ static EventManager & get_instance();
+
+ /**
+ * \brief Subscribe to an event.
+ *
+ * This method allows the registration of a callback for a specific event type and channel.
+ *
+ * \tparam EventType The type of the event to subscribe to.
+ * \param callback The callback function to invoke when the event is triggered.
+ * \param channel The channel number to subscribe to (default is 0).
+ */
+ template <typename EventType>
+ void subscribe(EventHandler<EventType> && callback, int channel = 0);
+
+ /**
+ * \brief Unsubscribe from an event.
+ *
+ * This method removes a previously registered callback from an event.
+ *
+ * \tparam EventType The type of the event to unsubscribe from.
+ * \param callback The callback function to remove from the subscription list.
+ * \param channel The event ID to unsubscribe from.
+ */
+ template <typename EventType>
+ void unsubscribe(const EventHandler<EventType> &, int channel);
+
+ /**
+ * \brief Trigger an event.
+ *
+ * This method invokes the appropriate callback(s) for the specified event.
+ *
+ * \tparam EventType The type of the event to trigger.
+ * \param event The event data to pass to the callback.
+ * \param channel The channel from which to trigger the event (default is 0).
+ */
+ template <typename EventType>
+ void trigger_event(const EventType & event, int channel);
+
+ /**
+ * \brief Queue an event for later processing.
+ *
+ * This method adds an event to the event queue, which will be processed in the
+ * dispatch_events function.
+ *
+ * \tparam EventType The type of the event to queue.
+ * \param event The event to queue.
+ * \param channel The channel number for the event (default is 0).
+ */
+ template <typename EventType>
+ void queue_event(EventType && event, int channel);
+
+ /**
+ * \brief Dispatch all queued events.
+ *
+ * This method processes all events in the event queue and triggers the corresponding
+ * callbacks for each event.
+ */
+ void dispatch_events();
+
+private:
+ /**
+ * \brief Default constructor for the EventManager.
+ *
+ * This constructor is private to enforce the singleton pattern.
+ */
+ EventManager() = default;
+
+ //! The queue of events to be processed.
+ std::vector<std::tuple<std::unique_ptr<Event>, int, std::type_index>>
+ events_queue;
+ //! Registered event handlers.
+ std::unordered_map<std::type_index,
+ std::vector<std::unique_ptr<IEventHandlerWrapper>>>
+ subscribers;
+ //! Event handlers indexed by event ID.
+ std::unordered_map<
+ std::type_index,
+ std::unordered_map<int,
+ std::vector<std::unique_ptr<IEventHandlerWrapper>>>>
+ subscribers_by_event_id;
+};
+
+} // namespace crepe
+#include "EventManager.hpp"
diff --git a/src/crepe/api/EventManager.hpp b/src/crepe/api/EventManager.hpp
new file mode 100644
index 0000000..b509097
--- /dev/null
+++ b/src/crepe/api/EventManager.hpp
@@ -0,0 +1,140 @@
+#include "EventManager.h"
+namespace crepe {
+
+template <typename EventType>
+void EventManager::subscribe(EventHandler<EventType> && callback, int channel) {
+ std::type_index event_type = typeid(EventType);
+ std::unique_ptr<EventHandlerWrapper<EventType>> handler
+ = std::make_unique<EventHandlerWrapper<EventType>>(callback);
+
+ if (channel) {
+ std::unordered_map<int,
+ std::vector<std::unique_ptr<IEventHandlerWrapper>>> &
+ handlers_map
+ = this->subscribers_by_event_id[event_type];
+ std::unordered_map<
+ int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>::iterator
+ handlers
+ = handlers_map.find(channel);
+ if (handlers != handlers_map.end()) {
+ handlers->second.emplace_back(std::move(handler));
+ } else {
+ handlers_map[channel].emplace_back(std::move(handler));
+ }
+ } else {
+ std::vector<std::unique_ptr<IEventHandlerWrapper>> & handlers
+ = this->subscribers[event_type];
+ handlers.emplace_back(std::move(handler));
+ }
+}
+
+template <typename EventType>
+void EventManager::queue_event(EventType && event, int channel) {
+ std::type_index event_type = std::type_index(typeid(EventType));
+
+ std::unique_ptr<EventType> event_ptr
+ = std::make_unique<EventType>(std::forward<EventType>(event));
+
+ std::tuple<std::unique_ptr<Event>, int, std::type_index> tuple(
+ std::move(event_ptr), channel, event_type);
+ this->events_queue.push_back(std::move(tuple));
+}
+
+template <typename EventType>
+void EventManager::trigger_event(const EventType & event, int channel) {
+ std::type_index event_type = std::type_index(typeid(EventType));
+
+ if (channel > 0) {
+ std::unordered_map<int,
+ std::vector<std::unique_ptr<IEventHandlerWrapper>>> &
+ handlers_map
+ = this->subscribers_by_event_id[event_type];
+ std::unordered_map<
+ int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>::iterator
+ handlers_it
+ = handlers_map.find(channel);
+
+ if (handlers_it != handlers_map.end()) {
+ std::vector<std::unique_ptr<IEventHandlerWrapper>> & handlers
+ = handlers_it->second;
+ for (std::vector<std::unique_ptr<IEventHandlerWrapper>>::iterator it
+ = handlers.begin();
+ it != handlers.end();++it) {
+ // stops when callback returns true
+ if((*it)->exec(event)){
+ break;
+ }
+ }
+ }
+ } else {
+ std::vector<std::unique_ptr<IEventHandlerWrapper>> & handlers
+ = this->subscribers[event_type];
+ for (std::vector<std::unique_ptr<IEventHandlerWrapper>>::iterator it
+ = handlers.begin();
+ it != handlers.end();++it) {
+ // stops when callback returns true
+ if((*it)->exec(event)){
+ break;
+ }
+ }
+ }
+}
+
+template <typename EventType>
+void EventManager::unsubscribe(const EventHandler<EventType> & callback,
+ int channel) {
+ std::type_index event_type(typeid(EventType));
+ std::string handler_name = callback.target_type().name();
+
+ if (channel) {
+ std::unordered_map<
+ std::type_index,
+ std::unordered_map<
+ int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>>::
+ iterator subscriber_list
+ = this->subscribers_by_event_id.find(event_type);
+ if (subscriber_list != this->subscribers_by_event_id.end()) {
+ std::unordered_map<
+ int, std::vector<std::unique_ptr<IEventHandlerWrapper>>> &
+ handlers_map
+ = subscriber_list->second;
+ std::unordered_map<
+ int,
+ std::vector<std::unique_ptr<IEventHandlerWrapper>>>::iterator
+ handlers
+ = handlers_map.find(channel);
+ if (handlers != handlers_map.end()) {
+ std::vector<std::unique_ptr<IEventHandlerWrapper>> & callbacks
+ = handlers->second;
+ for (std::vector<
+ std::unique_ptr<IEventHandlerWrapper>>::iterator it
+ = callbacks.begin();
+ it != callbacks.end(); ++it) {
+ if ((*it)->get_type() == handler_name) {
+ it = callbacks.erase(it);
+ return;
+ }
+ }
+ }
+ }
+ } else {
+ std::unordered_map<std::type_index,
+ std::vector<std::unique_ptr<IEventHandlerWrapper>>>::
+ iterator handlers_it
+ = this->subscribers.find(event_type);
+ if (handlers_it != this->subscribers.end()) {
+ std::vector<std::unique_ptr<IEventHandlerWrapper>> & handlers
+ = handlers_it->second;
+ for (std::vector<std::unique_ptr<IEventHandlerWrapper>>::iterator it
+ = handlers.begin();
+ it != handlers.end(); ++it) {
+ if ((*it)->get_type() == handler_name) {
+ it = handlers.erase(it);
+ return;
+ }
+ }
+ }
+ }
+}
+
+}
diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp
new file mode 100644
index 0000000..4fd9855
--- /dev/null
+++ b/src/crepe/api/IKeyListener.cpp
@@ -0,0 +1,51 @@
+#include "IKeyListener.h"
+
+using namespace crepe;
+
+IKeyListener::IKeyListener() {
+ this->channel = channel;
+ this->subscribe_events();
+}
+IKeyListener::IKeyListener(int channel) {
+ this->channel = channel;
+ this->subscribe_events();
+}
+IKeyListener::~IKeyListener() { this->unsubscribe_events(); }
+
+void IKeyListener::subscribe_events() {
+ key_pressed_handler = [this](const KeyPressEvent & event) {
+ return this->on_key_pressed(event);
+ };
+ key_released_handler = [this](const KeyReleaseEvent & event) {
+ return this->on_key_released(event);
+ };
+ EventManager::get_instance().subscribe<KeyPressEvent>(
+ std::move(this->key_pressed_handler), this->channel);
+ EventManager::get_instance().subscribe<KeyReleaseEvent>(
+ std::move(this->key_released_handler), this->channel);
+}
+
+void IKeyListener::unsubscribe_events() {
+ EventManager::get_instance().unsubscribe<KeyPressEvent>(
+ this->key_pressed_handler, channel);
+ EventManager::get_instance().unsubscribe<KeyReleaseEvent>(
+ this->key_released_handler, channel);
+ std::cout << std::endl;
+}
+void IKeyListener::activate_keys() {
+ if (this->active) {
+ return;
+ }
+ this->subscribe_events();
+}
+void IKeyListener::deactivate_keys() {
+ if (!this->active) {
+ return;
+ }
+ this->unsubscribe_events();
+}
+void IKeyListener::set_channel(int channel) {
+ this->unsubscribe_events();
+ this->channel = channel;
+ this->subscribe_events();
+}
diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h
new file mode 100644
index 0000000..839acbf
--- /dev/null
+++ b/src/crepe/api/IKeyListener.h
@@ -0,0 +1,78 @@
+#pragma once
+#include "Event.h"
+#include "EventHandler.h"
+#include "EventManager.h"
+
+/**
+ * \class IKeyListener
+ * \brief Interface for keyboard event handling in the application.
+ */
+class IKeyListener {
+public:
+ /**
+ * \brief Constructs an IKeyListener with a specified channel.
+ * \param channel The channel ID for event handling.
+ */
+ IKeyListener(int channel);
+
+ /**
+ * \brief Default constructor for IKeyListener.
+ */
+ IKeyListener();
+
+ /**
+ * \brief Destructor.
+ */
+ virtual ~IKeyListener();
+
+ /**
+ * \brief Pure virtual function to handle key press events.
+ * \param event The key press event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_key_pressed(const KeyPressEvent & event) = 0;
+
+ /**
+ * \brief Pure virtual function to handle key release events.
+ * \param event The key release event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_key_released(const KeyReleaseEvent & event) = 0;
+
+ /**
+ * \brief Activates key listening.
+ */
+ void activate_keys();
+
+ /**
+ * \brief Deactivates key listening.
+ */
+ void deactivate_keys();
+
+ /**
+ * \brief Sets the channel ID for event handling.
+ * \param channel The channel ID to set.
+ */
+ void set_channel(int channel);
+
+protected:
+ /**
+ * \brief Subscribes to key events.
+ */
+ void subscribe_events();
+
+ /**
+ * \brief Unsubscribes from key events.
+ */
+ void unsubscribe_events();
+
+private:
+ //! Indicates whether key listening is active.
+ bool active = true;
+ //! Channel ID for event handling.
+ int channel = 0;
+ //! Key press event handler.
+ EventHandler<KeyPressEvent> key_pressed_handler;
+ //!< Key release event handler.
+ EventHandler<KeyReleaseEvent> key_released_handler;
+};
diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp
new file mode 100644
index 0000000..489e55b
--- /dev/null
+++ b/src/crepe/api/IMouseListener.cpp
@@ -0,0 +1,64 @@
+#include "IMouseListener.h"
+
+using namespace crepe;
+
+IMouseListener::IMouseListener(int channel) { this->channel = channel; }
+
+IMouseListener::IMouseListener() { this->subscribe_events(); }
+
+IMouseListener::~IMouseListener() { this->unsubscribe_events(); }
+
+void IMouseListener::subscribe_events() {
+ // Define handler lambdas and subscribe them
+ mouse_click_handler = [this](const MouseClickEvent & event) {
+ return this->on_mouse_clicked(event);
+ };
+ mouse_press_handler = [this](const MousePressEvent & event) {
+ return this->on_mouse_pressed(event);
+ };
+ mouse_release_handler = [this](const MouseReleaseEvent & event) {
+ return this->on_mouse_released(event);
+ };
+ mouse_move_handler = [this](const MouseMoveEvent & event) {
+ return this->on_mouse_moved(event);
+ };
+ EventManager::get_instance().subscribe<MouseClickEvent>(
+ std::move(this->mouse_click_handler), this->channel);
+ EventManager::get_instance().subscribe<MousePressEvent>(
+ std::move(this->mouse_press_handler), this->channel);
+ EventManager::get_instance().subscribe<MouseReleaseEvent>(
+ std::move(this->mouse_release_handler), this->channel);
+ EventManager::get_instance().subscribe<MouseMoveEvent>(
+ std::move(this->mouse_move_handler), this->channel);
+}
+// TODO: reference voor singleton
+void IMouseListener::unsubscribe_events() {
+ EventManager::get_instance().unsubscribe<MouseClickEvent>(
+ this->mouse_click_handler, this->channel);
+ EventManager::get_instance().unsubscribe<MousePressEvent>(
+ this->mouse_press_handler, this->channel);
+ EventManager::get_instance().unsubscribe<MouseReleaseEvent>(
+ this->mouse_release_handler, this->channel);
+ EventManager::get_instance().unsubscribe<MouseMoveEvent>(
+ this->mouse_move_handler, this->channel);
+}
+
+void IMouseListener::activate_mouse() {
+ if (this->active) {
+ return;
+ }
+ this->subscribe_events();
+}
+
+void IMouseListener::deactivate_mouse() {
+ if (!this->active) {
+ return;
+ }
+ this->unsubscribe_events();
+}
+
+void IMouseListener::set_channel(int channel) {
+ this->unsubscribe_events();
+ this->channel = channel;
+ this->subscribe_events();
+}
diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h
new file mode 100644
index 0000000..7e92956
--- /dev/null
+++ b/src/crepe/api/IMouseListener.h
@@ -0,0 +1,117 @@
+#pragma once
+
+#include "Event.h"
+#include "EventHandler.h"
+#include "EventManager.h"
+
+/**
+ * \class IMouseListener
+ * \brief Interface for mouse event handling in the application.
+ */
+class IMouseListener {
+public:
+ /**
+ * \brief Default constructor.
+ */
+ IMouseListener();
+
+ /**
+ * \brief Constructs an IMouseListener with a specified channel.
+ * \param channel The channel ID for event handling.
+ */
+ IMouseListener(int channel);
+
+ /**
+ * \brief Destructor.
+ */
+ virtual ~IMouseListener();
+
+ /**
+ * \brief Copy constructor (deleted).
+ */
+ IMouseListener(const IMouseListener &) = delete;
+
+ /**
+ * \brief Copy assignment operator (deleted).
+ */
+ IMouseListener & operator=(const IMouseListener &) = delete;
+
+ /**
+ * \brief Move constructor (deleted).
+ */
+ IMouseListener(IMouseListener &&) = delete;
+
+ /**
+ * \brief Move assignment operator (deleted).
+ */
+ IMouseListener & operator=(IMouseListener &&) = delete;
+
+ /**
+ * \brief Handles a mouse click event.
+ * \param event The mouse click event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_mouse_clicked(const MouseClickEvent & event) = 0;
+
+ /**
+ * \brief Handles a mouse press event.
+ * \param event The mouse press event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_mouse_pressed(const MousePressEvent & event) = 0;
+
+ /**
+ * \brief Handles a mouse release event.
+ * \param event The mouse release event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_mouse_released(const MouseReleaseEvent & event) = 0;
+
+ /**
+ * \brief Handles a mouse move event.
+ * \param event The mouse move event to handle.
+ * \return True if the event was handled, false otherwise.
+ */
+ virtual bool on_mouse_moved(const MouseMoveEvent & event) = 0;
+
+ /**
+ * \brief Activates mouse listening.
+ */
+ void activate_mouse();
+
+ /**
+ * \brief Deactivates mouse listening.
+ */
+ void deactivate_mouse();
+
+ /**
+ * \brief Sets the channel ID for event handling.
+ * \param channel The channel ID to set.
+ */
+ void set_channel(int channel);
+
+protected:
+ /**
+ * \brief Subscribes to mouse events on the specified channel.
+ */
+ void subscribe_events();
+
+ /**
+ * \brief Unsubscribes from mouse events on the specified channel.
+ */
+ void unsubscribe_events();
+
+private:
+ //! Indicates whether mouse listening is active.
+ bool active = true;
+ //! Channel ID for event handling.
+ int channel = 0;
+ //! Mouse click event handler.
+ EventHandler<MouseClickEvent> mouse_click_handler;
+ //! Mouse press event handler.
+ EventHandler<MousePressEvent> mouse_press_handler;
+ //! Mouse release event handler.
+ EventHandler<MouseReleaseEvent> mouse_release_handler;
+ //! Mouse move event handler.
+ EventHandler<MouseMoveEvent> mouse_move_handler;
+};
diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h
new file mode 100644
index 0000000..e5a91fc
--- /dev/null
+++ b/src/crepe/api/KeyCodes.h
@@ -0,0 +1,146 @@
+#pragma once
+
+enum class MouseButton {
+ NONE = 0,
+ LEFT_MOUSE = 1,
+ RIGHT_MOUSE = 2,
+ MIDDLE_MOUSE = 3,
+ X1_MOUSE = 4,
+ X2_MOUSE = 5,
+ SCROLL_UP = 6,
+ SCROLL_DOWN = 7,
+};
+
+enum class Keycode : int {
+ NONE = 0,
+ SPACE = 32,
+ APOSTROPHE = 39, /* ' */
+ COMMA = 44, /* , */
+ MINUS = 45, /* - */
+ PERIOD = 46, /* . */
+ SLASH = 47, /* / */
+
+ D0 = 48, /* 0 */
+ D1 = 49, /* 1 */
+ D2 = 50, /* 2 */
+ D3 = 51, /* 3 */
+ D4 = 52, /* 4 */
+ D5 = 53, /* 5 */
+ D6 = 54, /* 6 */
+ D7 = 55, /* 7 */
+ D8 = 56, /* 8 */
+ D9 = 57, /* 9 */
+
+ SEMICOLON = 59, /* ; */
+ EQUAL = 61, /* = */
+
+ A = 65,
+ B = 66,
+ C = 67,
+ D = 68,
+ E = 69,
+ F = 70,
+ G = 71,
+ H = 72,
+ I = 73,
+ J = 74,
+ K = 75,
+ L = 76,
+ M = 77,
+ N = 78,
+ O = 79,
+ P = 80,
+ Q = 81,
+ R = 82,
+ S = 83,
+ T = 84,
+ U = 85,
+ V = 86,
+ W = 87,
+ X = 88,
+ Y = 89,
+ Z = 90,
+
+ LEFT_BRACKET = 91, /* [ */
+ BACKSLASH = 92, /* \ */
+ RIGHT_BRACKET = 93, /* ] */
+ GRAVE_ACCENT = 96, /* ` */
+
+ WORLD1 = 161, /* non-US #1 */
+ WORLD2 = 162, /* non-US #2 */
+
+ /* Function keys */
+ ESCAPE = 256,
+ ENTER = 257,
+ TAB = 258,
+ BACKSPACE = 259,
+ INSERT = 260,
+ DELETE = 261,
+ RIGHT = 262,
+ LEFT = 263,
+ DOWN = 264,
+ UP = 265,
+ PAGE_UP = 266,
+ PAGE_DOWN = 267,
+ HOME = 268,
+ END = 269,
+ CAPS_LOCK = 280,
+ SCROLL_LOCK = 281,
+ NUM_LOCK = 282,
+ PRINT_SCREEN = 283,
+ PAUSE = 284,
+ F1 = 290,
+ F2 = 291,
+ F3 = 292,
+ F4 = 293,
+ F5 = 294,
+ F6 = 295,
+ F7 = 296,
+ F8 = 297,
+ F9 = 298,
+ F10 = 299,
+ F11 = 300,
+ F12 = 301,
+ F13 = 302,
+ F14 = 303,
+ F15 = 304,
+ F16 = 305,
+ F17 = 306,
+ F18 = 307,
+ F19 = 308,
+ F20 = 309,
+ F21 = 310,
+ F22 = 311,
+ F23 = 312,
+ F24 = 313,
+ F25 = 314,
+
+ /* Keypad */
+ KP0 = 320,
+ KP1 = 321,
+ KP2 = 322,
+ KP3 = 323,
+ KP4 = 324,
+ KP5 = 325,
+ KP6 = 326,
+ KP7 = 327,
+ KP8 = 328,
+ KP9 = 329,
+ KP_DECIMAL = 330,
+ KP_DIVIDE = 331,
+ KP_MULTIPLY = 332,
+ KP_SUBTRACT = 333,
+ KP_ADD = 334,
+ KP_ENTER = 335,
+ KP_EQUAL = 336,
+
+ LEFT_SHIFT = 340,
+ LEFT_CONTROL = 341,
+ LEFT_ALT = 342,
+ LEFT_SUPER = 343,
+ RIGHT_SHIFT = 344,
+ RIGHT_CONTROL = 345,
+ RIGHT_ALT = 346,
+ RIGHT_SUPER = 347,
+ MENU = 348
+};
diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp
index a64366f..a4bc101 100644
--- a/src/crepe/api/LoopManager.cpp
+++ b/src/crepe/api/LoopManager.cpp
@@ -6,6 +6,8 @@
#include "../system/PhysicsSystem.h"
#include "../system/RenderSystem.h"
#include "../system/ScriptSystem.h"
+#include "..//system/PhysicsSystem.h"
+#include "..//system/CollisionSystem.h"
#include "LoopManager.h"
#include "LoopTimer.h"
@@ -32,7 +34,12 @@ void LoopManager::start() {
}
void LoopManager::set_running(bool running) { this->game_running = running; }
-void LoopManager::fixed_update() {}
+void LoopManager::fixed_update() {
+ PhysicsSystem phys;
+ phys.update();
+ CollisionSystem col;
+ col.update();
+}
void LoopManager::loop() {
LoopTimer & timer = LoopTimer::get_instance();
@@ -41,11 +48,11 @@ void LoopManager::loop() {
while (game_running) {
timer.update();
- while (timer.get_lag() >= timer.get_fixed_delta_time()) {
+ //while (timer.get_lag() >= timer.get_fixed_delta_time()) {
this->process_input();
this->fixed_update();
timer.advance_fixed_update();
- }
+ //}
this->update();
this->render();
diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h
index 3e5c7a3..fddbf5c 100644
--- a/src/crepe/api/Rigidbody.h
+++ b/src/crepe/api/Rigidbody.h
@@ -1,5 +1,7 @@
#pragma once
+#include <cmath>
+
#include "../Component.h"
#include "Vector2.h"
@@ -58,13 +60,13 @@ public:
//! linear velocity of object
Vector2 linear_velocity;
//! maximum linear velocity of object
- Vector2 max_linear_velocity;
+ Vector2 max_linear_velocity = {INFINITY ,INFINITY};
//! linear damping of object
Vector2 linear_damping;
//! angular velocity of object
double angular_velocity = 0.0;
//! max angular velocity of object
- double max_angular_velocity = 0.0;
+ double max_angular_velocity = INFINITY;
//! angular damping of object
double angular_damping = 0.0;
//! movements constraints of object
@@ -73,6 +75,10 @@ public:
bool use_gravity = true;
//! if object bounces
bool bounce = false;
+ //! bounce factor of material
+ double elastisity = 0.0;
+ //! offset of all colliders relative to transform position
+ Vector2 offset;
};
public:
diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h
index 2fb6136..438fde6 100644
--- a/src/crepe/api/Vector2.h
+++ b/src/crepe/api/Vector2.h
@@ -35,6 +35,9 @@ struct Vector2 {
//! Checks if this vector is not equal to another vector.
bool operator!=(const Vector2 & other) const;
+
+ //!
+ double dot(const Vector2& other) const;
};
} // namespace crepe
diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp
index 83e91f8..803516e 100644
--- a/src/crepe/facade/SDLContext.cpp
+++ b/src/crepe/facade/SDLContext.cpp
@@ -109,8 +109,8 @@ void SDLContext::draw(const Sprite & sprite, const Transform & transform, const
= (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x)
| (SDL_FLIP_VERTICAL * sprite.flip.flip_y));
- double adjusted_x = (transform.position.x - cam.x) * cam.zoom;
- double adjusted_y = (transform.position.y - cam.y) * cam.zoom;
+ double adjusted_x = (transform.position.x - cam.x -(sprite.sprite_rect.w/2)) * cam.zoom;
+ double adjusted_y = (transform.position.y - cam.y -(sprite.sprite_rect.h/2)) * cam.zoom;
double adjusted_w = sprite.sprite_rect.w * transform.scale * cam.zoom;
double adjusted_h = sprite.sprite_rect.h * transform.scale * cam.zoom;
diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp
index c74ca1d..df0ee41 100644
--- a/src/crepe/system/CollisionSystem.cpp
+++ b/src/crepe/system/CollisionSystem.cpp
@@ -1,5 +1,364 @@
+#include <cmath>
+#include <algorithm>
+#include <cstddef>
+#include <utility>
+#include <variant>
+
+#include "api/Event.h"
+#include "api/EventManager.h"
+#include "api/BoxCollider.h"
+#include "api/CircleCollider.h"
+#include "api/Vector2.h"
+#include "api/Rigidbody.h"
+#include "api/Transform.h"
+
+#include "ComponentManager.h"
#include "CollisionSystem.h"
+#include "Collider.h"
using namespace crepe;
-void CollisionSystem::update() {}
+CollisionSystem::CollisionSystem() {}
+
+void CollisionSystem::update() {
+ // Get collider components and keep them seperate
+ ComponentManager & mgr = ComponentManager::get_instance();
+ std::vector<std::reference_wrapper<BoxCollider>> boxcolliders = mgr.get_components_by_type<BoxCollider>();
+ std::vector<std::reference_wrapper<CircleCollider>> circlecolliders = mgr.get_components_by_type<CircleCollider>();
+
+ // Check between all colliders if there is a collision
+ std::vector<std::pair<CollidedInfoStor,CollidedInfoStor>> collided = check_collisions(boxcolliders,circlecolliders);
+
+ // For both objects call the collision handler
+ for (auto& collision_pair : collided) {
+ collision_handler(collision_pair.first,collision_pair.second);
+ collision_handler(collision_pair.second,collision_pair.first);
+ }
+}
+
+void CollisionSystem::collision_handler(CollidedInfoStor& data1,CollidedInfoStor& data2){
+
+ // Data needed for collision handler info
+ const Collider* collider1 = nullptr;
+ const Collider* collider2 = nullptr;
+ Vector2 move_back;
+
+ // Check collision type and get values for handler
+ if (std::holds_alternative<BoxCollider>(data1.collider)) {
+ if (std::holds_alternative<BoxCollider>(data2.collider)) {
+
+ // Get colliders from variant to be used to determine collision handler info
+ const BoxCollider& box_collider1 = std::get<BoxCollider>(data1.collider);
+ const BoxCollider& box_collider2 = std::get<BoxCollider>(data2.collider);
+ collider1 = &box_collider1;
+ collider2 = &box_collider2;
+
+ // TODO: send with the collider info to this function because this is calculated previously
+ // Get the current position of the collider
+ Vector2 final_position1 = current_position(box_collider1,data1.transform,data1.rigidbody);
+ Vector2 final_position2 = current_position(box_collider2,data2.transform,data2.rigidbody);
+
+ // Determine move_back value for smallest overlap (x or y)
+ move_back = box_box_collision_move_back(box_collider1,box_collider2,final_position1,final_position2);
+
+ }
+ else {
+ // TODO: calcualte Box - Circle collision info
+ const BoxCollider& box_collider = std::get<BoxCollider>(data1.collider);
+ const CircleCollider& circle_collider = std::get<CircleCollider>(data2.collider);
+ collider1 = &box_collider;
+ collider2 = &circle_collider;
+ }
+ }
+ else {
+ if (std::holds_alternative<CircleCollider>(data2.collider)) {
+ // TODO: calcualte Circle - Circle collision info
+ const CircleCollider& circle_collider1 = std::get<CircleCollider>(data1.collider);
+ const CircleCollider& circle_collider2 = std::get<CircleCollider>(data2.collider);
+ collider1 = &circle_collider1;
+ collider2 = &circle_collider2;
+ }
+ else {
+ // TODO: calcualte Circle - Box collision info
+ const CircleCollider& circle_collider = std::get<CircleCollider>(data1.collider);
+ const BoxCollider& box_collider = std::get<BoxCollider>(data2.collider);
+ collider1 = &circle_collider;
+ collider2 = &box_collider;
+ }
+ }
+
+ // One vaue is calculated for moving back. Calculate the other value (x or y) relateive to the move_back value.
+ Direction move_back_direction = Direction::NONE;
+ if(move_back.x != 0 && move_back.y > 0) {
+ move_back_direction = Direction::BOTH;
+ } else if (move_back.x != 0) {
+ move_back_direction = Direction::X_DIRECTION;
+ move_back.y = data1.rigidbody.data.linear_velocity.y * (move_back.x/data1.rigidbody.data.linear_velocity.x);
+ } else if (move_back.y != 0) {
+ move_back_direction = Direction::Y_DIRECTION;
+ move_back.x = data1.rigidbody.data.linear_velocity.x * (move_back.y/data1.rigidbody.data.linear_velocity.y);
+ }
+
+ // collision info
+ crepe::CollisionSystem::CollisionInfo collision_info{
+ .first={ *collider1, data1.transform, data1.rigidbody },
+ .second={ *collider2, data2.transform, data2.rigidbody },
+ .move_back_value = move_back,
+ .move_back_direction = move_back_direction,
+ };
+
+ // Determine if static needs to be called
+ determine_collision_handler(collision_info);
+}
+
+
+Vector2 CollisionSystem::box_box_collision_move_back(const BoxCollider& box_collider1,const BoxCollider& box_collider2,Vector2 final_position1,Vector2 final_position2)
+{
+ Vector2 resolution; // Default resolution vector
+ Vector2 delta = final_position2 - final_position1;
+
+ // Compute half-dimensions of the boxes
+ double half_width1 = box_collider1.width / 2.0;
+ double half_height1 = box_collider1.height / 2.0;
+ double half_width2 = box_collider2.width / 2.0;
+ double half_height2 = box_collider2.height / 2.0;
+
+ // Calculate overlaps along X and Y axes
+ double overlap_x = (half_width1 + half_width2) - std::abs(delta.x);
+ double overlap_y = (half_height1 + half_height2) - std::abs(delta.y);
+
+ // Check if there is a collision
+ if (overlap_x > 0 && overlap_y > 0) {//should always be true check if this can be removed
+ // 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;
+}
+
+void CollisionSystem::determine_collision_handler(CollisionInfo& info){
+ // Check rigidbody type for static
+ if(info.first.rigidbody.data.body_type != Rigidbody::BodyType::STATIC)
+ {
+ // If second body is static perform the static collision handler in this system
+ if(info.second.rigidbody.data.body_type == Rigidbody::BodyType::STATIC){
+ static_collision_handler(info);
+ };
+ // Call collision event for user
+ CollisionEvent data(info);
+ EventManager::get_instance().trigger_event<CollisionEvent>(data, info.first.collider.game_object_id);
+ }
+}
+
+void CollisionSystem::static_collision_handler(CollisionInfo& info){
+ // Move object back using calculate move back value
+ info.first.transform.position += info.move_back_value;
+
+ // If bounce is enabled mirror velocity
+ if(info.first.rigidbody.data.bounce) {
+ if(info.move_back_direction == Direction::BOTH)
+ {
+ info.first.rigidbody.data.linear_velocity.y = -info.first.rigidbody.data.linear_velocity.y * info.first.rigidbody.data.elastisity;
+ info.first.rigidbody.data.linear_velocity.x = -info.first.rigidbody.data.linear_velocity.x * info.first.rigidbody.data.elastisity;
+ }
+ else if(info.move_back_direction == Direction::Y_DIRECTION) {
+ info.first.rigidbody.data.linear_velocity.y = -info.first.rigidbody.data.linear_velocity.y * info.first.rigidbody.data.elastisity;
+ }
+ else if(info.move_back_direction == Direction::X_DIRECTION){
+ info.first.rigidbody.data.linear_velocity.x = -info.first.rigidbody.data.linear_velocity.x * info.first.rigidbody.data.elastisity;
+ }
+ }
+ // Stop movement if bounce is disabled
+ else {
+ info.first.rigidbody.data.linear_velocity = {0,0};
+ }
+}
+
+std::vector<std::pair<CollisionSystem::CollidedInfoStor,CollisionSystem::CollidedInfoStor>> CollisionSystem::check_collisions(const std::vector<std::reference_wrapper<BoxCollider>>& boxcolliders, const std::vector<std::reference_wrapper<CircleCollider>>& circlecolliders) {
+ ComponentManager & mgr = ComponentManager::get_instance();
+ std::vector<std::pair<CollidedInfoStor,CollidedInfoStor>> collisions_ret;
+
+ // 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
+
+ // Check collisions for each collider
+ for (size_t i = 0; i < boxcolliders.size(); ++i) {
+ // Fetch components for the first box collider
+ if(!boxcolliders[i].get().active) continue;
+ int game_object_id_1 = boxcolliders[i].get().game_object_id;
+ Transform& transform1 = mgr.get_components_by_id<Transform>(game_object_id_1).front().get();
+ if(!transform1.active) continue;
+ Rigidbody& rigidbody1 = mgr.get_components_by_id<Rigidbody>(game_object_id_1).front().get();
+ if(!rigidbody1.active) continue;
+
+ // Check CircleCollider vs CircleCollider
+ for (size_t j = i + 1; j < boxcolliders.size(); ++j) {
+ if(!boxcolliders[j].get().active) continue;
+ // Skip self collision
+ int game_object_id_2 = boxcolliders[j].get().game_object_id;
+ if (game_object_id_1 == game_object_id_2) continue;
+
+ // Fetch components for the second box collider
+ Transform & transform2 = mgr.get_components_by_id<Transform>(boxcolliders[j].get().game_object_id).front().get();
+ if(!transform2.active) continue;
+ Rigidbody & rigidbody2 = mgr.get_components_by_id<Rigidbody>(boxcolliders[j].get().game_object_id).front().get();
+ if(!rigidbody2.active) continue;
+ // Check collision
+ if (check_box_box_collision(boxcolliders[i], boxcolliders[j], transform1, transform2, rigidbody1, rigidbody2)) {
+ collisions_ret.emplace_back(std::make_pair(
+ CollidedInfoStor{boxcolliders[i], transform1, rigidbody1},
+ CollidedInfoStor{boxcolliders[j], transform2, rigidbody2}
+ ));
+ }
+ }
+
+ // Check BoxCollider vs CircleCollider
+ for (size_t j = 0; j < circlecolliders.size(); ++j) {
+ if(!circlecolliders[j].get().active) continue;
+ // Skip self collision
+ int game_object_id_2 = circlecolliders[j].get().game_object_id;
+ if (game_object_id_1 == game_object_id_2) continue;
+
+ // Fetch components for the second collider (circle)
+ Transform & transform2 = mgr.get_components_by_id<Transform>(circlecolliders[j].get().game_object_id).front().get();
+ if(!transform2.active) continue;
+ Rigidbody & rigidbody2 = mgr.get_components_by_id<Rigidbody>(circlecolliders[j].get().game_object_id).front().get();
+ if(!rigidbody2.active) continue;
+
+ // Check collision
+ if (check_box_circle_collision(boxcolliders[i], circlecolliders[j], transform1, transform2, rigidbody1, rigidbody2)) {
+
+ collisions_ret.emplace_back(std::make_pair(
+ CollidedInfoStor{boxcolliders[i], transform1, rigidbody1},
+ CollidedInfoStor{circlecolliders[j], transform2, rigidbody2}
+ ));
+ }
+ }
+ }
+ // Check CircleCollider vs CircleCollider
+ for (size_t i = 0; i < circlecolliders.size(); ++i) {
+ if(!circlecolliders[i].get().active) continue;
+ // Fetch components for the first circle collider
+ int game_object_id_1 = circlecolliders[i].get().game_object_id;
+ Transform & transform1 = mgr.get_components_by_id<Transform>(circlecolliders[i].get().game_object_id).front().get();
+ if(!transform1.active) continue;
+ Rigidbody & rigidbody1 = mgr.get_components_by_id<Rigidbody>(circlecolliders[i].get().game_object_id).front().get();
+ if(!rigidbody1.active) continue;
+
+ for (size_t j = i + 1; j < circlecolliders.size(); ++j) {
+ if(!circlecolliders[j].get().active) continue;
+ // Skip self collision
+ int game_object_id_2 = circlecolliders[j].get().game_object_id;
+ if (game_object_id_1 == game_object_id_2) continue;
+
+ // Fetch components for the second circle collider
+ Transform & transform2 = mgr.get_components_by_id<Transform>(circlecolliders[j].get().game_object_id).front().get();
+ if(!transform2.active) continue;
+ Rigidbody & rigidbody2 = mgr.get_components_by_id<Rigidbody>(circlecolliders[j].get().game_object_id).front().get();
+ if(!rigidbody2.active) continue;
+
+ // Check collision
+ if (check_circle_circle_collision(circlecolliders[i], circlecolliders[j], transform1, transform2, rigidbody1, rigidbody2)) {
+ collisions_ret.emplace_back(std::make_pair(
+ CollidedInfoStor{circlecolliders[i], transform1, rigidbody1},
+ CollidedInfoStor{circlecolliders[j], transform2, rigidbody2}
+ ));
+ }
+ }
+ }
+ return collisions_ret;
+}
+
+bool CollisionSystem::check_box_box_collision(const BoxCollider& box1, const BoxCollider& box2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2)
+{
+ // Get current positions of colliders
+ Vector2 final_position1 = current_position(box1,transform1,rigidbody1);
+ Vector2 final_position2 = current_position(box2,transform2,rigidbody2);
+
+ // Calculate half-extents (half width and half height)
+ double half_width1 = box1.width / 2.0;
+ double half_height1 = box1.height / 2.0;
+ double half_width2 = box2.width / 2.0;
+ double half_height2 = box2.height / 2.0;
+
+ // Check if the boxes overlap along the X and Y axes
+ return !(final_position1.x + half_width1 <= final_position2.x - half_width2 || // box1 is left of box2
+ final_position1.x - half_width1 >= final_position2.x + half_width2 || // box1 is right of box2
+ final_position1.y + half_height1 <= final_position2.y - half_height2 || // box1 is above box2
+ final_position1.y - half_height1 >= final_position2.y + half_height2); // box1 is below box2
+}
+
+bool CollisionSystem::check_box_circle_collision(const BoxCollider& box1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) {
+ // Get current positions of colliders
+ Vector2 final_position1 = current_position(box1, transform1, rigidbody1);
+ Vector2 final_position2 = current_position(circle2, transform2, rigidbody2);
+
+ // Calculate box half-extents
+ double half_width = box1.width / 2.0;
+ double half_height = box1.height / 2.0;
+
+ // Find the closest point on the box to the circle's center
+ double closest_x = std::max(final_position1.x - half_width, std::min(final_position2.x, final_position1.x + half_width));
+ double 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
+ double distance_x = final_position2.x - closest_x;
+ double distance_y = final_position2.y - closest_y;
+ double 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::check_circle_circle_collision(const CircleCollider& circle1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2) {
+ // Get current positions of colliders
+ Vector2 final_position1 = current_position(circle1,transform1,rigidbody1);
+ Vector2 final_position2 = current_position(circle2,transform2,rigidbody2);
+
+ double distance_x = final_position1.x - final_position2.x;
+ double distance_y = final_position1.y - final_position2.y;
+ double distance_squared = distance_x * distance_x + distance_y * distance_y;
+
+ // Calculate the sum of the radii
+ double 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;
+}
+
+Vector2 CollisionSystem::current_position(const Collider& collider, const Transform& transform, const Rigidbody& rigidbody) {
+ // Function to convert degrees to radians
+ auto degrees_to_radians = [](double degrees) {
+ return degrees * (M_PI / 180.0);
+ };
+
+ // Get the rotation in radians
+ double radians1 = degrees_to_radians(transform.rotation);
+
+ // Calculate total offset with scale
+ Vector2 total_offset = (rigidbody.data.offset + collider.offset) * transform.scale;
+
+ // Rotate
+ double rotated_total_offset_x1 = total_offset.x * cos(radians1) - total_offset.y * sin(radians1);
+ double rotated_total_offset_y1 = total_offset.x * sin(radians1) + total_offset.y * cos(radians1);
+
+ // Final positions considering scaling and rotation
+ return(transform.position + Vector2(rotated_total_offset_x1, rotated_total_offset_y1));
+
+}
diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h
index c1a70d8..f3242b1 100644
--- a/src/crepe/system/CollisionSystem.h
+++ b/src/crepe/system/CollisionSystem.h
@@ -1,13 +1,191 @@
#pragma once
-#include "System.h"
+#include <vector>
+#include <variant>
+
+#include "api/Rigidbody.h"
+#include "api/Transform.h"
+#include "api/BoxCollider.h"
+#include "api/CircleCollider.h"
+#include "api/Vector2.h"
+
+#include "Collider.h"
namespace crepe {
-class CollisionSystem : public System {
+
+//! A system responsible for detecting and handling collisions between colliders.
+class CollisionSystem {
+private:
+
+ //! A variant type that can hold either a BoxCollider or a CircleCollider.
+ using collider_stor = std::variant<BoxCollider, CircleCollider>;
+
+ /**
+ * \brief A structure to store the collision data of a single collider.
+ *
+ * This structure stores the collider type, its associated transform, and its rigidbody.
+ */
+ struct CollidedInfoStor {
+ //! Store either BoxCollider or CircleCollider
+ collider_stor collider;
+ Transform& transform;
+ Rigidbody& rigidbody;
+ };
+
+ //! Enum representing movement directions during collision resolution.
+ enum class Direction {
+ NONE,
+ X_DIRECTION,
+ Y_DIRECTION,
+ BOTH
+ };
+
+public:
+ /**
+ * \brief A structure representing the collision information between two colliders.
+ *
+ * This structure contains both colliders, their associated transforms and rigidbodies,
+ * as well as the movement vector to resolve the collision.
+ */
+ struct ColliderInfo {
+ const Collider& collider;
+ Transform& transform;
+ Rigidbody& rigidbody;
+ };
+ /**
+ * \brief A structure representing detailed collision information between two colliders.
+ *
+ * This includes the movement data required to resolve the collision.
+ */
+ struct CollisionInfo{
+ ColliderInfo first;
+ ColliderInfo second;
+ Vector2 move_back_value;
+ Direction move_back_direction = Direction::NONE;
+ };
+
public:
- using System::System;
- void update() override;
+
+ CollisionSystem();
+
+ //! Updates the collision system by checking for collisions between colliders and handling them.
+ void update();
+
+private:
+ /**
+ * \brief Handles a collision between two colliders.
+ *
+ * This function calculates the necessary response to resolve the collision, including
+ * moving the objects back to prevent overlap and applying any velocity changes.
+ *
+ * \param data1 The collision data for the first collider.
+ * \param data2 The collision data for the second collider.
+ */
+ void collision_handler(CollidedInfoStor& data1,CollidedInfoStor& data2);
+
+ /**
+ * \brief Resolves the movement of two box colliders that are colliding.
+ *
+ * This function calculates the smallest overlap (along the X or Y axis) and determines
+ * the move-back vector to prevent overlap.
+ *
+ * \param box_collider1 The first box collider.
+ * \param box_collider2 The second box collider.
+ * \param final_position1 The final position of the first box collider.
+ * \param final_position2 The final position of the second box collider.
+ * \return The move-back vector to resolve the collision.
+ */
+ Vector2 box_box_collision_move_back(const BoxCollider& box_collider1,const BoxCollider& box_collider2,Vector2 position1,Vector2 position2);
+
+ /**
+ * \brief Determines the appropriate collision handler based on the rigidbody types of the colliding objects.
+ *
+ * This function checks if the second object is static, and if so, it calls the static collision handler.
+ * Otherwise, it triggers a collision event.
+ *
+ * \param info The collision information containing the colliders, transforms, rigidbodies, and move-back data.
+ */
+ void determine_collision_handler(CollisionInfo& info);
+
+ /**
+ * \brief Handles the collision with a static object.
+ *
+ * This function resolves the collision by moving the object back and applying any bounce or stop behavior.
+ *
+ * \param info The collision information containing the colliders, transforms, rigidbodies, and move-back data.
+ */
+ void static_collision_handler(CollisionInfo& info);
+private: //detection
+
+ /**
+ * \brief Checks for collisions between box colliders and circle colliders
+ * and returns the collision pairs that need to be resolved.
+ *
+ * \param boxcolliders A vector of references to all box colliders.
+ * \param circlecolliders A vector of references to all circle colliders.
+ * \return A vector of pairs containing collision information for the detected collisions.
+ */
+ std::vector<std::pair<CollidedInfoStor,CollidedInfoStor>> check_collisions(const std::vector<std::reference_wrapper<BoxCollider>>& boxcolliders, const std::vector<std::reference_wrapper<CircleCollider>>& circlecolliders);
+
+ /**
+ * \brief Checks for a collision between two box colliders.
+ *
+ * This function checks if two box colliders overlap based on their positions and dimensions.
+ *
+ * \param box1 The first box collider.
+ * \param box2 The second box collider.
+ * \param transform1 The transform component of the first box collider.
+ * \param transform2 The transform component of the second box collider.
+ * \param rigidbody1 The rigidbody component of the first box collider.
+ * \param rigidbody2 The rigidbody component of the second box collider.
+ * \return True if the two box colliders overlap, otherwise false.
+ */
+ bool check_box_box_collision(const BoxCollider& box1, const BoxCollider& box2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2);
+
+ /**
+ * \brief Checks for a collision between a box collider and a circle collider.
+ *
+ * This function checks if a box collider overlaps with a circle collider based on their positions
+ * and dimensions.
+ *
+ * \param box1 The box collider.
+ * \param circle2 The circle collider.
+ * \param transform1 The transform component of the box collider.
+ * \param transform2 The transform component of the circle collider.
+ * \param rigidbody1 The rigidbody component of the box collider.
+ * \param rigidbody2 The rigidbody component of the circle collider.
+ * \return True if the box collider and circle collider overlap, otherwise false.
+ */
+ bool check_box_circle_collision(const BoxCollider& box1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2);
+
+ /**
+ * \brief Checks for a collision between two circle colliders.
+ *
+ * This function checks if two circle colliders overlap based on their positions and radii.
+ *
+ * \param circle1 The first circle collider.
+ * \param circle2 The second circle collider.
+ * \param transform1 The transform component of the first circle collider.
+ * \param transform2 The transform component of the second circle collider.
+ * \param rigidbody1 The rigidbody component of the first circle collider.
+ * \param rigidbody2 The rigidbody component of the second circle collider.
+ * \return True if the two circle colliders overlap, otherwise false.
+ */
+ bool check_circle_circle_collision(const CircleCollider& circle1, const CircleCollider& circle2, const Transform& transform1, const Transform& transform2, const Rigidbody& rigidbody1, const Rigidbody& rigidbody2);
+
+ /**
+ * \brief Gets the current position of a collider by combining its transform and rigidbody data.
+ *
+ * This function calculates the current position of the collider by considering its transform and
+ * rigidbody velocity.
+ *
+ * \param collider The collider whose position is being determined.
+ * \param transform The transform component associated with the collider.
+ * \param rigidbody The rigidbody component associated with the collider.
+ * \return The current position of the collider as a Vector2.
+ */
+ Vector2 current_position(const Collider& collider, const Transform& transform, const Rigidbody& rigidbody);
};
} // namespace crepe
diff --git a/src/example/CMakeLists.txt b/src/example/CMakeLists.txt
index 3a5b543..d2ea926 100644
--- a/src/example/CMakeLists.txt
+++ b/src/example/CMakeLists.txt
@@ -28,6 +28,8 @@ add_example(proxy)
add_example(db)
add_example(ecs)
add_example(scene_manager)
+add_example(collision)
+add_example(events)
add_example(particles)
add_example(gameloop)
diff --git a/src/example/collision.cpp b/src/example/collision.cpp
new file mode 100644
index 0000000..f51380e
--- /dev/null
+++ b/src/example/collision.cpp
@@ -0,0 +1,123 @@
+#include "api/BoxCollider.h"
+#include "system/CollisionSystem.h"
+#include <crepe/system/ScriptSystem.h>
+#include <crepe/Component.h>
+#include <crepe/ComponentManager.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/BoxCollider.h>
+#include <crepe/api/Transform.h>
+#include <crepe/system/PhysicsSystem.h>
+#include <crepe/system/RenderSystem.h>
+#include <crepe/util/log.h>
+
+#include <crepe/api/Script.h>
+#include <crepe/api/AssetManager.h>
+#include <crepe/api/Color.h>
+#include <crepe/api/Sprite.h>
+#include <crepe/api/Texture.h>
+#include <crepe/api/Transform.h>
+#include <crepe/api/Vector2.h>
+#include <crepe/api/Event.h>
+#include <crepe/api/EventManager.h>
+#include <crepe/api/LoopManager.h>
+
+#include <chrono>
+#include <memory>
+
+using namespace crepe;
+using namespace std;
+
+class MyScript : public Script {
+ static bool oncollision(const CollisionEvent& test) {
+ std::cout << "test collision: " << test.info.first.collider.game_object_id << std::endl;
+ return true;
+ }
+ void init() {
+ EventManager::get_instance().subscribe<CollisionEvent>(oncollision, this->parent->game_object_id);
+ }
+ void update() {
+ // Retrieve component from the same GameObject this script is on
+
+ }
+
+
+};
+
+int main(int argc, char * argv[]) {
+ //setup
+ LoopManager gameloop;
+ Color color(0, 0, 0, 0);
+
+ double screen_size_width = 640;
+ double screen_size_height = 480;
+ double world_collider = 1000;
+ //define playable world
+ GameObject World(0, "Name", "Tag", Vector2{screen_size_width/2, screen_size_height/2}, 0, 1);
+ World.add_component<Rigidbody>(Rigidbody::Data{
+ .mass = 0,
+ .gravity_scale = 0,
+ .body_type = Rigidbody::BodyType::STATIC,
+ .constraints = {0, 0, 0},
+ .use_gravity = false,
+ .bounce = false,
+ .offset = {0,0}
+ });
+ World.add_component<BoxCollider>(Vector2{0, 0-(screen_size_height/2+world_collider/2)}, world_collider, world_collider);; // Top
+ World.add_component<BoxCollider>(Vector2{0, screen_size_height/2+world_collider/2}, world_collider, world_collider); // Bottom
+ World.add_component<BoxCollider>(Vector2{0-(screen_size_width/2+world_collider/2), 0}, world_collider, world_collider); // Left
+ World.add_component<BoxCollider>(Vector2{screen_size_width/2+world_collider/2, 0}, world_collider, world_collider); // right
+
+
+ GameObject game_object1(1, "Name", "Tag", Vector2{screen_size_width/2, screen_size_height/2}, 0, 1);
+ game_object1.add_component<Rigidbody>(Rigidbody::Data{
+ .mass = 1,
+ .gravity_scale = 0.01,
+ .body_type = Rigidbody::BodyType::DYNAMIC,
+ .linear_velocity = {1,0},
+ .constraints = {0, 0, 0},
+ .use_gravity = true,
+ .bounce = true,
+ .elastisity = 1,
+ .offset = {0,0},
+ });
+ game_object1.add_component<BoxCollider>(Vector2{0, 0}, 20, 20);
+ game_object1.add_component<BehaviorScript>().set_script<MyScript>();
+ game_object1.add_component<Sprite>(
+ make_shared<Texture>("/home/jaro/crepe/asset/texture/green_square.png"), color,
+ FlipSettings{true, true});
+ game_object1.add_component<Camera>(Color::get_white());
+
+
+ // GameObject game_object2(2, "Name", "Tag", Vector2{20, 470}, 0, 1);
+ // game_object2.add_component<Rigidbody>(Rigidbody::Data{
+ // .mass = 1,
+ // .gravity_scale = 1,
+ // .body_type = Rigidbody::BodyType::DYNAMIC,
+ // .linear_velocity = {0,0},
+ // .constraints = {0, 0, 0},
+ // .use_gravity = false,
+ // .bounce = false,
+ // .offset = {0,0},
+ // });
+ // game_object2.add_component<BoxCollider>(Vector2{0, 0}, 0, 0);
+ // game_object2.add_component<BehaviorScript>().set_script<MyScript>();
+ // game_object2.add_component<Sprite>(
+ // make_shared<Texture>("/home/jaro/crepe/asset/texture/red_square.png"), color,
+ // FlipSettings{true, true});
+
+
+ crepe::ScriptSystem sys;
+ // Update all scripts. This should result in MyScript::update being called
+ sys.update();
+
+ gameloop.start();
+ // auto & render = crepe::RenderSystem::get_instance();
+ // auto start = std::chrono::steady_clock::now();
+ // while (std::chrono::steady_clock::now() - start < std::chrono::seconds(5)) {
+ // render.update();
+ // }
+
+
+ return 0;
+}
diff --git a/src/example/events.cpp b/src/example/events.cpp
new file mode 100644
index 0000000..ed519ff
--- /dev/null
+++ b/src/example/events.cpp
@@ -0,0 +1,113 @@
+#include <iostream>
+
+#include <crepe/ComponentManager.h>
+#include <crepe/system/ScriptSystem.h>
+#include <crepe/util/log.h>
+
+#include <crepe/api/BehaviorScript.h>
+#include <crepe/api/Config.h>
+#include <crepe/api/Event.h>
+#include <crepe/api/EventManager.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/IKeyListener.h>
+#include <crepe/api/IMouseListener.h>
+#include <crepe/api/KeyCodes.h>
+#include <crepe/api/Script.h>
+#include <crepe/api/Transform.h>
+
+using namespace crepe;
+using namespace std;
+
+class MyScript : public Script, public IKeyListener, public IMouseListener {
+ void update() {
+ // Retrieve component from the same GameObject this script is on
+ Transform & test = get_component<Transform>();
+ dbg_logf("Transform(%.2f, %.2f)", test.position.x, test.position.y);
+ }
+
+ bool on_key_pressed(const KeyPressEvent & event) override {
+ std::cout << "KeyPressed function" << std::endl;
+ this->deactivate_keys();
+ return false;
+ }
+ bool on_key_released(const KeyReleaseEvent & event) override {
+ std::cout << "KeyRelease function" << std::endl;
+ return false;
+ }
+ bool on_mouse_clicked(const MouseClickEvent & event) override {
+ std::cout << "MouseClick function" << std::endl;
+ return false;
+ }
+ bool on_mouse_pressed(const MousePressEvent & event) override {
+ std::cout << "MousePress function" << std::endl;
+ return false;
+ }
+ bool on_mouse_released(const MouseReleaseEvent & event) override {
+ std::cout << "MouseRelease function" << std::endl;
+ return false;
+ }
+ bool on_mouse_moved(const MouseMoveEvent & event) override {
+ std::cout << "MouseMove function" << std::endl;
+ return false;
+ }
+};
+class TestKeyListener : public IKeyListener {
+public:
+ bool on_key_pressed(const KeyPressEvent & event) override {
+ std::cout << "TestKeyListener: Key Pressed - Code: "
+ << static_cast<int>(event.key) << std::endl;
+ return true; // Return true if the listener should remain active
+ }
+ bool on_key_released(const KeyReleaseEvent & event) override {
+ std::cout << "TestKeyListener: Key Released - Code: "
+ << static_cast<int>(event.key) << std::endl;
+ return true;
+ }
+};
+int main() {
+ // two events to trigger
+ KeyPressEvent key_press;
+ key_press.key = Keycode::A;
+ key_press.repeat = 0;
+ MouseClickEvent click_event;
+ click_event.button = MouseButton::LEFT_MOUSE;
+ click_event.mouse_x = 100;
+ click_event.mouse_y = 200;
+ // queue events to test queue
+ EventManager::get_instance().queue_event<KeyPressEvent>(
+ std::move(key_press), 0);
+ EventManager::get_instance().queue_event<MouseClickEvent>(
+ std::move(click_event), 0);
+ {
+ TestKeyListener test_listener;
+ test_listener.set_channel(1);
+ auto obj = GameObject(0, "name", "tag", Vector2{1.2, 3.4}, 0, 1);
+ obj.add_component<BehaviorScript>().set_script<MyScript>();
+
+ ScriptSystem sys;
+ sys.update();
+
+ // Trigger the events while `testListener` is in scope
+ EventManager::get_instance().trigger_event<KeyPressEvent>(key_press, 1);
+ EventManager::get_instance().trigger_event(MouseClickEvent{
+ .mouse_x = 100,
+ .mouse_y = 100,
+ .button = MouseButton::LEFT_MOUSE,
+ },1);
+ }
+ // custom lambda event handler
+ EventHandler<KeyPressEvent> event_handler = [](const KeyPressEvent & e) {
+ std::cout << "lambda test" << std::endl;
+ return false;
+ };
+ EventManager::get_instance().subscribe<KeyPressEvent>(
+ std::move(event_handler), 0);
+ // testing trigger with testListener not in scope (unsubscribed)
+ EventManager::get_instance().trigger_event<KeyPressEvent>(key_press, 0);
+ EventManager::get_instance().trigger_event<MouseClickEvent>(click_event, 0);
+ // dispatching queued events
+ EventManager::get_instance().dispatch_events();
+
+ EventManager::get_instance().unsubscribe<KeyPressEvent>(event_handler, 0);
+ return EXIT_SUCCESS;
+}
diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt
index 49c8151..a41d097 100644
--- a/src/test/CMakeLists.txt
+++ b/src/test/CMakeLists.txt
@@ -1,4 +1,7 @@
target_sources(test_main PUBLIC
+ dummy.cpp
+ # audio.cpp
+ CollisionTest.cpp
main.cpp
PhysicsTest.cpp
ScriptTest.cpp
diff --git a/src/test/CollisionTest.cpp b/src/test/CollisionTest.cpp
new file mode 100644
index 0000000..3e43479
--- /dev/null
+++ b/src/test/CollisionTest.cpp
@@ -0,0 +1,83 @@
+#include "api/BoxCollider.h"
+#include "api/CircleCollider.h"
+#include "api/Vector2.h"
+#include <crepe/ComponentManager.h>
+#include <crepe/api/Config.h>
+#include <crepe/api/GameObject.h>
+#include <crepe/api/Rigidbody.h>
+#include <crepe/api/Transform.h>
+#include <crepe/system/CollisionSystem.h>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace std::chrono_literals;
+using namespace crepe;
+
+class CollisionTest : public ::testing::Test {
+protected:
+ GameObject * game_object1;
+ GameObject * game_object2;
+ CollisionSystem collision_system;
+ void SetUp() override {
+ ComponentManager & mgr = ComponentManager::get_instance();
+ mgr.delete_all_components();
+ std::vector<std::reference_wrapper<Transform>> transforms
+ = mgr.get_components_by_id<Transform>(0);
+
+ // ob 1
+ game_object1 = new GameObject(0, "", "", Vector2{0, 0}, 0, 0);
+ game_object1->add_component<Rigidbody>(Rigidbody::Data{
+ .mass = 1,
+ .gravity_scale = 1,
+ .body_type = Rigidbody::BodyType::DYNAMIC,
+ .max_linear_velocity = Vector2{10, 10},
+ .max_angular_velocity = 10,
+ .constraints = {0, 0, 0},
+ .use_gravity = false,
+ .bounce = false,
+ });
+
+ game_object1->add_component<BoxCollider>(Vector2{0,0},10,10);
+
+
+ //ob 2
+ game_object2 = new GameObject(1, "", "", Vector2{50, 50}, 0, 0);
+ game_object2->add_component<Rigidbody>(Rigidbody::Data{
+ .mass = 1,
+ .gravity_scale = 1,
+ .body_type = Rigidbody::BodyType::DYNAMIC,
+ .max_linear_velocity = Vector2{10, 10},
+ .max_angular_velocity = 10,
+ .constraints = {0, 0, 0},
+ .use_gravity = false,
+ .bounce = false,
+ });
+ game_object2->add_component<CircleCollider>(Vector2{0,0},5);
+ }
+};
+
+TEST_F(CollisionTest, box_box_collision) {
+ Config::get_instance().physics.gravity = 1;
+ ComponentManager & mgr = ComponentManager::get_instance();
+ std::vector<std::reference_wrapper<Transform>> transforms
+ = mgr.get_components_by_id<Transform>(0);
+ Transform & transform = transforms.front().get();
+ ASSERT_FALSE(transforms.empty());
+ transform.position = {39,50};
+ collision_system.update();
+ transform.position = {40,50};
+ collision_system.update();
+ transform.position = {50,39};
+ collision_system.update();
+ transform.position = {50,40};
+ collision_system.update();
+ transform.position = {50,60};
+ collision_system.update();
+ transform.position = {50,61};
+ collision_system.update();
+ transform.position = {60,50};
+ collision_system.update();
+ transform.position = {61,50};
+ collision_system.update();
+}
+