diff options
35 files changed, 1932 insertions, 20 deletions
| diff --git a/asset/texture/green_square.png b/asset/texture/green_square.pngBinary files differ new file mode 100755 index 0000000..7772c87 --- /dev/null +++ b/asset/texture/green_square.png diff --git a/asset/texture/red_square.png b/asset/texture/red_square.pngBinary files differ new file mode 100755 index 0000000..6ffbbec --- /dev/null +++ b/asset/texture/red_square.png diff --git a/lib/sdl2 b/lib/sdl2 -Subproject 9519b9916cd29a14587af0507292f2bd31dd575 +Subproject c98c4fbff6d8f3016a3ce6685bf8f43433c3efc diff --git a/lib/sdl_image b/lib/sdl_image -Subproject abcf63aa71b4e3ac32120fa9870a6500ddcdcc8 +Subproject 56560190a6c5b5740e5afdce747e2a2bfbf16db diff --git a/lib/sdl_ttf b/lib/sdl_ttf -Subproject 4a318f8dfaa1bb6f10e0c5e54052e25d3c7f344 +Subproject a3d0895c1b60c41ff9e85d9203ddd7485c014da diff --git a/mwe/events/include/event.h b/mwe/events/include/event.h index ee1bf52..e1b220b 100644 --- a/mwe/events/include/event.h +++ b/mwe/events/include/event.h @@ -148,7 +148,7 @@ private:  };  class ShutDownEvent : public Event {  public: -	ShutDownEvent() : Event("ShutDownEvent") {}; +	ShutDownEvent() : Event("ShutDownEvent"){};  	REGISTER_EVENT_TYPE(ShutDownEvent) 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(); +} + |