diff options
Diffstat (limited to 'src/crepe')
29 files changed, 707 insertions, 696 deletions
| diff --git a/src/crepe/Component.h b/src/crepe/Component.h index 5279fb3..06ab1ef 100644 --- a/src/crepe/Component.h +++ b/src/crepe/Component.h @@ -27,6 +27,12 @@ protected:  	//! Only the ComponentManager can create components  	friend class ComponentManager; +	// TODO: this breaks the CollisionSystem but should be uncommented: +	// Component(const Component &) = delete; +	// Component(Component &&) = delete; +	// virtual Component & operator=(const Component &) = delete; +	// virtual Component & operator=(Component &&) = delete; +  public:  	virtual ~Component() = default; diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp index 6bd123d..dd197b3 100644 --- a/src/crepe/api/BehaviorScript.hpp +++ b/src/crepe/api/BehaviorScript.hpp @@ -16,6 +16,7 @@ BehaviorScript & BehaviorScript::set_script(Args &&... args) {  	Script * s = new T(std::forward<Args>(args)...);  	s->game_object_id = this->game_object_id;  	s->component_manager_ref = &this->component_manager; +	s->event_manager_ref = &EventManager::get_instance();  	this->script = std::unique_ptr<Script>(s);  	return *this;  } diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 07b3a82..cc3e5e2 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -20,11 +20,12 @@ target_sources(crepe PUBLIC  	BoxCollider.cpp  	CircleCollider.cpp  	EventManager.cpp -	EventHandler.cpp  	IKeyListener.cpp  	IMouseListener.cpp  	LoopManager.cpp  	LoopTimer.cpp +	EventHandler.cpp +	Script.cpp  )  target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -54,6 +55,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	EventManager.h  	EventManager.hpp  	EventHandler.h +	EventHandler.hpp  	Event.h  	IKeyListener.h  	IMouseListener.h diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h index bd6a541..9923a05 100644 --- a/src/crepe/api/Event.h +++ b/src/crepe/api/Event.h @@ -1,59 +1,115 @@ +// TODO discussing the location of these events  #pragma once -#include "KeyCodes.h" -#include <iostream> +  #include <string> -#include <typeindex> -#include "system/CollisionSystem.h" -class Event { -public: -}; +#include "../system/CollisionSystem.h" + +#include "KeyCodes.h" +/** + * \brief Base class for all event types in the system. + */ +class Event {}; + +/** + * \brief Event triggered when a key is pressed. + */  class KeyPressEvent : public Event {  public: -	int repeat = 0; +	//! false if first time press, true if key is repeated +	bool repeat = false; + +	//! The key that was pressed.  	Keycode key = Keycode::NONE;  }; +/** + * \brief Event triggered when a key is released. + */  class KeyReleaseEvent : public Event {  public: +	//! The key that was released.  	Keycode key = Keycode::NONE;  }; +/** + * \brief Event triggered when a mouse button is pressed. + */  class MousePressEvent : public Event {  public: +	//! X-coordinate of the mouse position at the time of the event.  	int mouse_x = 0; + +	//! Y-coordinate of the mouse position at the time of the event.  	int mouse_y = 0; + +	//! The mouse button that was pressed.  	MouseButton button = MouseButton::NONE;  }; +/** + * \brief Event triggered when a mouse button is clicked (press and release). + */  class MouseClickEvent : public Event {  public: +	//! X-coordinate of the mouse position at the time of the event.  	int mouse_x = 0; + +	//! Y-coordinate of the mouse position at the time of the event.  	int mouse_y = 0; + +	//! The mouse button that was clicked.  	MouseButton button = MouseButton::NONE;  }; + +/** + * \brief Event triggered when a mouse button is released. + */  class MouseReleaseEvent : public Event {  public: +	//! X-coordinate of the mouse position at the time of the event.  	int mouse_x = 0; + +	//! Y-coordinate of the mouse position at the time of the event.  	int mouse_y = 0; + +	//! The mouse button that was released.  	MouseButton button = MouseButton::NONE;  }; + +/** + * \brief Event triggered when the mouse is moved. + */  class MouseMoveEvent : public Event {  public: +	//! X-coordinate of the mouse position at the time of the event.  	int mouse_x = 0; + +	//! Y-coordinate of the mouse position at the time of the event.  	int mouse_y = 0;  }; + +/** + * \brief Event triggered during a collision between objects. + */  class CollisionEvent : public Event {  public:  	crepe::CollisionSystem::CollisionInfo info;  	CollisionEvent(const crepe::CollisionSystem::CollisionInfo& collisionInfo)          : info(collisionInfo) {}  }; + +/** + * \brief Event triggered when text is submitted, e.g., from a text input. + */  class TextSubmitEvent : public Event {  public: +	//! The submitted text.  	std::string text = "";  }; -class ShutDownEvent : public Event { -public: -}; + +/** + * \brief Event triggered to indicate the application is shutting down. + */ +class ShutDownEvent : public Event {}; diff --git a/src/crepe/api/EventHandler.cpp b/src/crepe/api/EventHandler.cpp index 93a116a..4dc232f 100644 --- a/src/crepe/api/EventHandler.cpp +++ b/src/crepe/api/EventHandler.cpp @@ -1,2 +1,5 @@  #include "EventHandler.h" -bool IEventHandlerWrapper::exec(const Event & e) { return call(e); } + +using namespace crepe; + +bool IEventHandlerWrapper::exec(const Event & e) { return this->call(e); } diff --git a/src/crepe/api/EventHandler.h b/src/crepe/api/EventHandler.h index 0ab90de..ef659fd 100644 --- a/src/crepe/api/EventHandler.h +++ b/src/crepe/api/EventHandler.h @@ -1,9 +1,11 @@  #pragma once -#include "Event.h" +  #include <functional> -#include <iostream> -#include <typeindex> +#include <string> +#include "Event.h" + +namespace crepe {  /**   * \brief A type alias for an event handler function.   *  @@ -11,8 +13,9 @@   * indicating whether the event is handled.   *    * \tparam EventType The type of event this handler will handle. + *  + * Returning \c false from an event handler results in the event being propogated to other listeners for the same event type, while returning \c true stops propogation altogether.   */ -// TODO: typedef  template <typename EventType>  using EventHandler = std::function<bool(const EventType & e)>; @@ -21,7 +24,7 @@ using EventHandler = std::function<bool(const EventType & e)>;   * \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. + * `call()` method to process events   */  class IEventHandlerWrapper {  public: @@ -40,15 +43,6 @@ public:       */  	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. @@ -81,8 +75,7 @@ public:       *        * \param handler The event handler function.       */ -	explicit EventHandlerWrapper(const EventHandler<EventType> & handler) -		: m_handler(handler), m_handler_type(m_handler.target_type().name()) {} +	explicit EventHandlerWrapper(const EventHandler<EventType> & handler);  private:  	/** @@ -93,20 +86,11 @@ private:       * \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; } +	bool call(const Event & e) override;  	//! The event handler function. -	EventHandler<EventType> m_handler; -	//! The type name of the handler function. -	const std::string m_handler_type; +	EventHandler<EventType> handler;  }; + +} // namespace crepe + +#include "EventHandler.hpp" diff --git a/src/crepe/api/EventHandler.hpp b/src/crepe/api/EventHandler.hpp new file mode 100644 index 0000000..391dcca --- /dev/null +++ b/src/crepe/api/EventHandler.hpp @@ -0,0 +1,18 @@ +#include <typeindex> + +#include "EventHandler.h" + +namespace crepe { + +// Implementation of EventHandlerWrapper constructor +template <typename EventType> +EventHandlerWrapper<EventType>::EventHandlerWrapper(const EventHandler<EventType> & handler) +	: handler(handler) {} + +// Implementation of EventHandlerWrapper::call +template <typename EventType> +bool EventHandlerWrapper<EventType>::call(const Event & e) { +	return this->handler(static_cast<const EventType &>(e)); +} + +} //namespace crepe diff --git a/src/crepe/api/EventManager.cpp b/src/crepe/api/EventManager.cpp index e881d49..20f0dd3 100644 --- a/src/crepe/api/EventManager.cpp +++ b/src/crepe/api/EventManager.cpp @@ -1,6 +1,7 @@  #include "EventManager.h"  using namespace crepe; +using namespace std;  EventManager & EventManager::get_instance() {  	static EventManager instance; @@ -8,74 +9,38 @@ EventManager & EventManager::get_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); +	for (auto & event : this->events_queue) { +		this->handle_event(event.type, event.channel, *event.event.get()); +	} +	this->events_queue.clear(); +} -		bool event_handled = false; +void EventManager::handle_event(type_index type, event_channel_t channel, const Event & data) { +	auto handlers_it = this->subscribers.find(type); +	if (handlers_it == this->subscribers.end()) return; -		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; -					} -				} -			} -		} +	vector<CallbackEntry> & handlers = handlers_it->second; +	for (auto & handler : handlers) { +		bool check_channel = handler.channel != CHANNEL_ALL || channel != CHANNEL_ALL; +		if (check_channel && handler.channel != channel) continue; + +		bool handled = handler.callback->exec(data); +		if (handled) return; +	} +} + +void EventManager::clear() { +	this->subscribers.clear(); +	this->events_queue.clear(); +} -		if (!event_handled) { -			++event_it; +void EventManager::unsubscribe(subscription_t id) { +	for (auto & [event_type, handlers] : this->subscribers) { +		for (auto it = handlers.begin(); it != handlers.end(); it++) { +			// find listener with subscription id +			if ((*it).id != id) continue; +			it = handlers.erase(it); +			return;  		}  	}  } diff --git a/src/crepe/api/EventManager.h b/src/crepe/api/EventManager.h index 38d2e64..348a04d 100644 --- a/src/crepe/api/EventManager.h +++ b/src/crepe/api/EventManager.h @@ -1,8 +1,6 @@  #pragma once -#include <functional>  #include <memory> -#include <type_traits>  #include <typeindex>  #include <unordered_map>  #include <vector> @@ -10,113 +8,154 @@  #include "Event.h"  #include "EventHandler.h" -  namespace crepe { + +//! Event listener unique ID +typedef size_t subscription_t; + +/** + * \brief Event channel + * + * Events can be sent to a specific channel, which prevents listeners on other channels from + * being called. The default channel is EventManager::CHANNEL_ALL, which calls all listeners. + */ +typedef size_t event_channel_t; +  /**   * \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. + * \brief Manages event subscriptions, triggers, and queues, enabling decoupled event handling. + *  + * The `EventManager` acts as a centralized event system. It allows for registering callbacks + * for specific event types, triggering events synchronously, queueing events for later + * processing, and managing subscriptions via unique identifiers.   */  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; +	static constexpr const event_channel_t CHANNEL_ALL = -1;  	/** -     * \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. -     */ +	 * \brief Get the singleton instance of the EventManager. +	 *  +	 * This method returns the unique instance of the EventManager, creating it if it +	 * doesn't already exist. Ensures only one instance is active in the program. +	 *  +	 * \return Reference to the singleton instance of the EventManager. +	 */  	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). -     */ +	 * \brief Subscribe to a specific event type. +	 *  +	 * Registers a callback for a given event type and optional channel. Each callback +	 * is assigned a unique subscription ID that can be used for later unsubscription. +	 *  +	 * \tparam EventType The type of the event to subscribe to. +	 * \param callback The callback function to be invoked when the event is triggered. +	 * \param channel The channel number to subscribe to (default is CHANNEL_ALL, which listens to all channels). +	 * \return A unique subscription ID associated with the registered callback. +	 */  	template <typename EventType> -	void subscribe(EventHandler<EventType> && callback, int channel = 0); +	subscription_t subscribe(const EventHandler<EventType> & callback, +							 event_channel_t channel = CHANNEL_ALL);  	/** -     * \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 Unsubscribe a previously registered callback. +	 *  +	 * Removes a callback from the subscription list based on its unique subscription ID. +	 *  +	 * \param event_id The unique subscription ID of the callback to remove. +	 */ +	void unsubscribe(subscription_t event_id);  	/** -     * \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). -     */ +	 * \brief Trigger an event immediately. +	 *  +	 * Synchronously invokes all registered callbacks for the given event type on the specified channel. +	 *  +	 * \tparam EventType The type of the event to trigger. +	 * \param event The event instance to pass to the callbacks. +	 * \param channel The channel to trigger the event on (default is CHANNEL_ALL, which triggers on all channels). +	 */  	template <typename EventType> -	void trigger_event(const EventType & event, int channel); +	void trigger_event(const EventType & event, event_channel_t channel = CHANNEL_ALL);  	/** -     * \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). -     */ +	 * \brief Queue an event for later processing. +	 *  +	 * Adds an event to the event queue to be processed during the next call to `dispatch_events`. +	 *  +	 * \tparam EventType The type of the event to queue. +	 * \param event The event instance to queue. +	 * \param channel The channel to associate with the event (default is CHANNEL_ALL). +	 */  	template <typename EventType> -	void queue_event(EventType && event, int channel); +	void queue_event(const EventType & event, event_channel_t channel = CHANNEL_ALL);  	/** -     * \brief Dispatch all queued events. -     *  -     * This method processes all events in the event queue and triggers the corresponding  -     * callbacks for each event. -     */ +	 * \brief Process all queued events. +	 *  +	 * Iterates through the event queue and triggers callbacks for each queued event. +	 * Events are removed from the queue once processed. +	 */  	void dispatch_events(); +	/** +	 * \brief Clear all subscriptions. +	 *  +	 * Removes all registered event handlers and clears the subscription list. +	 */ +	void clear(); +  private:  	/** -     * \brief Default constructor for the EventManager. -     *  -     * This constructor is private to enforce the singleton pattern. -     */ +	 * \brief Default constructor for the EventManager. +	 *  +	 * 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; +	/** +	 * \struct QueueEntry +	 * \brief Represents an entry in the event queue. +	 */ +	struct QueueEntry { +		std::unique_ptr<Event> event; ///< The event instance. +		event_channel_t channel = CHANNEL_ALL; ///< The channel associated with the event. +		std::type_index type; ///< The type of the event. +	}; + +	/** +	 * \brief Internal event handler +	 * +	 * This function processes a single event, and is used to process events both during +	 * EventManager::dispatch_events and inside EventManager::trigger_event +	 * +	 * \param type \c typeid of concrete Event class +	 * \param channel Event channel +	 * \param data Event data +	 */ +	void handle_event(std::type_index type, event_channel_t channel, const Event & data); + +	/** +	 * \struct CallbackEntry +	 * \brief Represents a registered event handler callback. +	 */ +	struct CallbackEntry { +		std::unique_ptr<IEventHandlerWrapper> callback; ///< The callback function wrapper. +		event_channel_t channel = CHANNEL_ALL; ///< The channel this callback listens to. +		subscription_t id = -1; ///< Unique subscription ID. +	}; + +	//! The queue of events to be processed during dispatch. +	std::vector<QueueEntry> events_queue; + +	//! A map of event type to registered callbacks. +	std::unordered_map<std::type_index, std::vector<CallbackEntry>> subscribers; + +	//! Counter to generate unique subscription IDs. +	subscription_t subscription_counter = 0;  };  } // namespace crepe +  #include "EventManager.hpp" diff --git a/src/crepe/api/EventManager.hpp b/src/crepe/api/EventManager.hpp index b509097..a5f4556 100644 --- a/src/crepe/api/EventManager.hpp +++ b/src/crepe/api/EventManager.hpp @@ -1,140 +1,36 @@ +#pragma once +  #include "EventManager.h" +  namespace crepe {  template <typename EventType> -void EventManager::subscribe(EventHandler<EventType> && callback, int channel) { +subscription_t EventManager::subscribe(const EventHandler<EventType> & callback, +									   event_channel_t channel) { +	subscription_counter++;  	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)); +	std::vector<CallbackEntry> & handlers = this->subscribers[event_type]; +	handlers.emplace_back(CallbackEntry{ +		.callback = std::move(handler), .channel = channel, .id = subscription_counter}); +	return subscription_counter;  }  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; -				} -		} -	} +void EventManager::queue_event(const EventType & event, event_channel_t channel) { +	static_assert(std::is_base_of<Event, EventType>::value, +				  "EventType must derive from Event"); +	this->events_queue.push_back(QueueEntry{ +		.event = std::make_unique<EventType>(event), +		.channel = channel, +		.type = typeid(EventType), +	});  }  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; -				} -			} -		} -	} +void EventManager::trigger_event(const EventType & event, event_channel_t channel) { +	this->handle_event(typeid(EventType), channel, event);  } -} +} // namespace crepe diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp index 4fd9855..8642655 100644 --- a/src/crepe/api/IKeyListener.cpp +++ b/src/crepe/api/IKeyListener.cpp @@ -2,50 +2,18 @@  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); +// Constructor with specified channel +IKeyListener::IKeyListener(event_channel_t channel) +	: event_manager(EventManager::get_instance()) { +	this->press_id = event_manager.subscribe<KeyPressEvent>( +		[this](const KeyPressEvent & event) { return this->on_key_pressed(event); }, channel); +	this->release_id = event_manager.subscribe<KeyReleaseEvent>( +		[this](const KeyReleaseEvent & event) { return this->on_key_released(event); }, +		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(); +// Destructor, unsubscribe events +IKeyListener::~IKeyListener() { +	event_manager.unsubscribe(this->press_id); +	event_manager.unsubscribe(this->release_id);  } diff --git a/src/crepe/api/IKeyListener.h b/src/crepe/api/IKeyListener.h index 839acbf..328a4c2 100644 --- a/src/crepe/api/IKeyListener.h +++ b/src/crepe/api/IKeyListener.h @@ -1,8 +1,11 @@  #pragma once +  #include "Event.h"  #include "EventHandler.h"  #include "EventManager.h" +namespace crepe { +  /**   * \class IKeyListener   * \brief Interface for keyboard event handling in the application. @@ -13,17 +16,12 @@ 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. -     */ +	IKeyListener(event_channel_t channel = EventManager::CHANNEL_ALL);  	virtual ~IKeyListener(); +	IKeyListener(const IKeyListener &) = delete; +	IKeyListener & operator=(const IKeyListener &) = delete; +	IKeyListener & operator=(IKeyListener &&) = delete; +	IKeyListener(IKeyListener &&) = delete;  	/**       * \brief Pure virtual function to handle key press events. @@ -39,40 +37,13 @@ public:       */  	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; +	//! Key press event id +	subscription_t press_id = -1; +	//! Key release event id +	subscription_t release_id = -1; +	//! EventManager reference +	EventManager & event_manager;  }; + +} // namespace crepe diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp index 489e55b..989aeb3 100644 --- a/src/crepe/api/IMouseListener.cpp +++ b/src/crepe/api/IMouseListener.cpp @@ -2,63 +2,28 @@  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(); +IMouseListener::IMouseListener(event_channel_t channel) +	: event_manager(EventManager::get_instance()) { +	this->click_id = event_manager.subscribe<MouseClickEvent>( +		[this](const MouseClickEvent & event) { return this->on_mouse_clicked(event); }, +		channel); + +	this->press_id = event_manager.subscribe<MousePressEvent>( +		[this](const MousePressEvent & event) { return this->on_mouse_pressed(event); }, +		channel); + +	this->release_id = event_manager.subscribe<MouseReleaseEvent>( +		[this](const MouseReleaseEvent & event) { return this->on_mouse_released(event); }, +		channel); + +	this->move_id = event_manager.subscribe<MouseMoveEvent>( +		[this](const MouseMoveEvent & event) { return this->on_mouse_moved(event); }, channel);  } -void IMouseListener::set_channel(int channel) { -	this->unsubscribe_events(); -	this->channel = channel; -	this->subscribe_events(); +IMouseListener::~IMouseListener() { +	// Unsubscribe event handlers +	event_manager.unsubscribe(this->click_id); +	event_manager.unsubscribe(this->press_id); +	event_manager.unsubscribe(this->release_id); +	event_manager.unsubscribe(this->move_id);  } diff --git a/src/crepe/api/IMouseListener.h b/src/crepe/api/IMouseListener.h index 7e92956..15e1619 100644 --- a/src/crepe/api/IMouseListener.h +++ b/src/crepe/api/IMouseListener.h @@ -4,6 +4,8 @@  #include "EventHandler.h"  #include "EventManager.h" +namespace crepe { +  /**   * \class IMouseListener   * \brief Interface for mouse event handling in the application. @@ -11,34 +13,14 @@  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. -     */ +	IMouseListener(event_channel_t channel = EventManager::CHANNEL_ALL);  	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(const IMouseListener &) = delete; +	IMouseListener & operator=(const IMouseListener &&) = delete;  	IMouseListener(IMouseListener &&) = delete;  	/** @@ -74,44 +56,17 @@ public:       */  	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; +	//! Mouse click event id +	subscription_t click_id = -1; +	//! Mouse press event id +	subscription_t press_id = -1; +	//! Mouse release event id +	subscription_t release_id = -1; +	//! Mouse move event id +	subscription_t move_id = -1; +	//! EventManager reference +	EventManager & event_manager;  }; + +} //namespace crepe diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h index e5a91fc..9e173e0 100644 --- a/src/crepe/api/KeyCodes.h +++ b/src/crepe/api/KeyCodes.h @@ -1,94 +1,93 @@  #pragma once +//! Enumeration for mouse button inputs, including standard and extended buttons.  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, +	NONE = 0, //!< No mouse button input. +	LEFT_MOUSE = 1, //!< Left mouse button. +	RIGHT_MOUSE = 2, //!< Right mouse button. +	MIDDLE_MOUSE = 3, //!< Middle mouse button (scroll wheel press). +	X1_MOUSE = 4, //!< First extended mouse button. +	X2_MOUSE = 5, //!< Second extended mouse button. +	SCROLL_UP = 6, //!< Scroll wheel upward movement. +	SCROLL_DOWN = 7, //!< Scroll wheel downward movement.  }; -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, +//! Enumeration for keyboard key inputs, including printable characters, function keys, and keypad keys. +enum class Keycode { +	NONE = 0, //!< No key input. +	SPACE = 32, //!< Spacebar. +	APOSTROPHE = 39, //!< Apostrophe ('). +	COMMA = 44, //!< Comma (,). +	MINUS = 45, //!< Minus (-). +	PERIOD = 46, //!< Period (.). +	SLASH = 47, //!< Slash (/). +	D0 = 48, //!< Digit 0. +	D1 = 49, //!< Digit 1. +	D2 = 50, //!< Digit 2. +	D3 = 51, //!< Digit 3. +	D4 = 52, //!< Digit 4. +	D5 = 53, //!< Digit 5. +	D6 = 54, //!< Digit 6. +	D7 = 55, //!< Digit 7. +	D8 = 56, //!< Digit 8. +	D9 = 57, //!< Digit 9. +	SEMICOLON = 59, //!< Semicolon (;). +	EQUAL = 61, //!< Equal sign (=). +	A = 65, //!< Key 'A'. +	B = 66, //!< Key 'B'. +	C = 67, //!< Key 'C'. +	D = 68, //!< Key 'D'. +	E = 69, //!< Key 'E'. +	F = 70, //!< Key 'F'. +	G = 71, //!< Key 'G'. +	H = 72, //!< Key 'H'. +	I = 73, //!< Key 'I'. +	J = 74, //!< Key 'J'. +	K = 75, //!< Key 'K'. +	L = 76, //!< Key 'L'. +	M = 77, //!< Key 'M'. +	N = 78, //!< Key 'N'. +	O = 79, //!< Key 'O'. +	P = 80, //!< Key 'P'. +	Q = 81, //!< Key 'Q'. +	R = 82, //!< Key 'R'. +	S = 83, //!< Key 'S'. +	T = 84, //!< Key 'T'. +	U = 85, //!< Key 'U'. +	V = 86, //!< Key 'V'. +	W = 87, //!< Key 'W'. +	X = 88, //!< Key 'X'. +	Y = 89, //!< Key 'Y'. +	Z = 90, //!< Key 'Z'. +	LEFT_BRACKET = 91, //!< Left bracket ([). +	BACKSLASH = 92, //!< Backslash (\). +	RIGHT_BRACKET = 93, //!< Right bracket (]). +	GRAVE_ACCENT = 96, //!< Grave accent (`). +	WORLD1 = 161, //!< Non-US key #1. +	WORLD2 = 162, //!< Non-US key #2. +	ESCAPE = 256, //!< Escape key. +	ENTER = 257, //!< Enter key. +	TAB = 258, //!< Tab key. +	BACKSPACE = 259, //!< Backspace key. +	INSERT = 260, //!< Insert key. +	DELETE = 261, //!< Delete key. +	RIGHT = 262, //!< Right arrow key. +	LEFT = 263, //!< Left arrow key. +	DOWN = 264, //!< Down arrow key. +	UP = 265, //!< Up arrow key. +	PAGE_UP = 266, //!< Page Up key. +	PAGE_DOWN = 267, //!< Page Down key. +	HOME = 268, //!< Home key. +	END = 269, //!< End key. +	CAPS_LOCK = 280, //!< Caps Lock key. +	SCROLL_LOCK = 281, //!< Scroll Lock key. +	NUM_LOCK = 282, //!< Num Lock key. +	PRINT_SCREEN = 283, //!< Print Screen key. +	PAUSE = 284, //!< Pause key. +	/** +	 * \name Function keys (F1-F25). +	 * \{ +	 */  	F1 = 290,  	F2 = 291,  	F3 = 292, @@ -114,8 +113,11 @@ enum class Keycode : int {  	F23 = 312,  	F24 = 313,  	F25 = 314, - -	/* Keypad */ +	/// \} +	/** +	 * \name Keypad digits and operators. +	 * \{ +	 */  	KP0 = 320,  	KP1 = 321,  	KP2 = 322, @@ -133,7 +135,11 @@ enum class Keycode : int {  	KP_ADD = 334,  	KP_ENTER = 335,  	KP_EQUAL = 336, - +	/// \} +	/** +	 * \name Modifier keys. +	 * \{ +	 */  	LEFT_SHIFT = 340,  	LEFT_CONTROL = 341,  	LEFT_ALT = 342, @@ -142,5 +148,6 @@ enum class Keycode : int {  	RIGHT_CONTROL = 345,  	RIGHT_ALT = 346,  	RIGHT_SUPER = 347, -	MENU = 348 +	/// \} +	MENU = 348, //!< Menu key.  }; diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp new file mode 100644 index 0000000..9ebb074 --- /dev/null +++ b/src/crepe/api/Script.cpp @@ -0,0 +1,11 @@ +#include "Script.h" + +using namespace crepe; + +Script::~Script() { +	EventManager & evmgr = *this->event_manager_ref; +	for (auto id : this->listeners) { +		evmgr.unsubscribe(id); +	} +} + diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 939d142..4d98e4d 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -4,6 +4,8 @@  #include "../types.h" +#include "EventManager.h" +  namespace crepe {  class ScriptSystem; @@ -22,7 +24,11 @@ class ComponentManager;  class Script {  protected:  	/** -	 * \brief Script initialization function +	 * \name Interface functions +	 * \{ +	 */ +	/** +	 * \brief Script initialization function (empty by default)  	 *  	 * This function is called during the ScriptSystem::update() routine *before*  	 * Script::update() if it (a) has not yet been called and (b) the \c BehaviorScript component @@ -30,24 +36,32 @@ protected:  	 */  	virtual void init() {}  	/** -	 * \brief Script update function +	 * \brief Script update function (empty by default)  	 *  	 * This function is called during the ScriptSystem::update() routine if the \c BehaviorScript  	 * component holding this script instance is active.  	 */  	virtual void update() {} +	//! \} +  	//! ScriptSystem calls \c init() and \c update()  	friend class crepe::ScriptSystem;  protected:  	/** -	 * \brief Get single component of type \c T on this game object (utility) +	 * \name Utility functions +	 * \{ +	 */ + +	/** +	 * \brief Get single component of type \c T on this game object  	 *  	 * \tparam T Type of component  	 *  	 * \returns Reference to component  	 * -	 * \throws nullptr if this game object does not have a component matching type \c T +	 * \throws std::runtime_error if this game object does not have a component matching type \c +	 * T  	 */  	template <typename T>  	T & get_component() const; @@ -55,7 +69,7 @@ protected:  	// cause compile-time errors  	/** -	 * \brief Get all components of type \c T on this game object (utility) +	 * \brief Get all components of type \c T on this game object  	 *  	 * \tparam T Type of component  	 * @@ -63,31 +77,65 @@ protected:  	 */  	template <typename T>  	std::vector<std::reference_wrapper<T>> get_components() const; -	 + +	/** +	 * \brief Log a message using Log::logf +	 * +	 * \tparam Args Log::logf parameters +	 * \param args  Log::logf parameters +	 */ +	template <typename... Args> +	void logf(Args &&... args); + +	game_object_id_t get_game_object_id() const { return this->game_object_id; }; +  	/** -	 * \brief Gets game object id this script is attached to +	 * \brief Subscribe to an event  	 * -	 * \returns game object id +	 * \see EventManager::subscribe  	 */ -	game_object_id_t get_game_object_id() const {return this->game_object_id;}; -	 +	template <typename EventType> +	void subscribe(const EventHandler<EventType> & callback, event_channel_t channel = EventManager::CHANNEL_ALL); + +	//! \} +  protected:  	// NOTE: Script must have a constructor without arguments so the game programmer doesn't need  	// to manually add `using Script::Script` to their concrete script class.  	Script() = default;  	//! Only \c BehaviorScript instantiates Script  	friend class BehaviorScript; +public: +	// std::unique_ptr destroys script +	virtual ~Script(); + +	Script(const Script &) = delete; +	Script(Script &&) = delete; +	Script & operator=(const Script &) = delete; +	Script & operator=(Script &&) = delete;  private: -	// These references are set by BehaviorScript immediately after calling the constructor of -	// Script. +	/** +	 * \name Late references +	 * +	 * These references are set by BehaviorScript immediately after calling the constructor of +	 * Script. +	 * +	 * \{ +	 */ +	//! Game object ID of game object parent BehaviorScript is attached to  	game_object_id_t game_object_id = -1; +	//! Reference to component manager instance  	ComponentManager * component_manager_ref = nullptr; -	// TODO: use OptionalRef instead of pointer +	//! Reference to event manager instance +	EventManager * event_manager_ref = nullptr; +	//! \}  private:  	//! Flag to indicate if \c init() has been called already  	bool initialized = false; +	//! List of subscribed events +	std::vector<subscription_t> listeners;  };  } // namespace crepe diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index a064a90..42c8f0b 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -20,9 +20,21 @@ T & Script::get_component() const {  template <typename T>  std::vector<std::reference_wrapper<T>> Script::get_components() const { -	auto & mgr = *this->component_manager_ref; +	ComponentManager & mgr = *this->component_manager_ref;  	return mgr.get_components_by_id<T>(this->game_object_id);  } +template <typename... Args> +void Script::logf(Args &&... args) { +	Log::logf(std::forward<Args>(args)...); +} + +template <typename EventType> +void Script::subscribe(const EventHandler<EventType> & callback, event_channel_t channel) { +	EventManager & mgr = *this->event_manager_ref; +	subscription_t listener = mgr.subscribe<EventType>(callback, channel); +	this->listeners.push_back(listener); +} +  } // namespace crepe diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp index de0d0ea..734a5bb 100644 --- a/src/crepe/api/Texture.cpp +++ b/src/crepe/api/Texture.cpp @@ -35,5 +35,5 @@ int Texture::get_width() const {  }  int Texture::get_height() const {  	if (this->texture == nullptr) return 0; -	return SDLContext::get_instance().get_width(*this); +	return SDLContext::get_instance().get_height(*this);  } diff --git a/src/crepe/facade/DB.cpp b/src/crepe/facade/DB.cpp index d5d19dc..95cf606 100644 --- a/src/crepe/facade/DB.cpp +++ b/src/crepe/facade/DB.cpp @@ -18,8 +18,8 @@ DB::DB(const string & path) {  	this->db = {db, [](libdb::DB * db) { db->close(db, 0); }};  	// load or create database file -	ret = this->db->open(this->db.get(), NULL, path.c_str(), NULL, libdb::DB_BTREE, DB_CREATE, -						 0); +	const char * file = path.empty() ? NULL : path.c_str(); +	ret = this->db->open(this->db.get(), NULL, file, NULL, libdb::DB_BTREE, DB_CREATE, 0);  	if (ret != 0) throw runtime_error(format("db->open: {}", libdb::db_strerror(ret)));  	// create cursor diff --git a/src/crepe/facade/DB.h b/src/crepe/facade/DB.h index 629b0eb..115c0f1 100644 --- a/src/crepe/facade/DB.h +++ b/src/crepe/facade/DB.h @@ -22,8 +22,10 @@ class DB {  public:  	/**  	 * \param path  The path of the database (created if nonexistant) +	 * +	 * \note If \p path is empty, the database is entirely in-memory  	 */ -	DB(const std::string & path); +	DB(const std::string & path = "");  	virtual ~DB() = default;  public: diff --git a/src/crepe/facade/SDLContext.cpp b/src/crepe/facade/SDLContext.cpp index 803516e..00523a6 100644 --- a/src/crepe/facade/SDLContext.cpp +++ b/src/crepe/facade/SDLContext.cpp @@ -1,5 +1,6 @@  #include <SDL2/SDL.h>  #include <SDL2/SDL_image.h> +#include <SDL2/SDL_keycode.h>  #include <SDL2/SDL_rect.h>  #include <SDL2/SDL_render.h>  #include <SDL2/SDL_surface.h> @@ -7,13 +8,15 @@  #include <cmath>  #include <cstddef>  #include <functional> -#include <iostream>  #include <memory> +#include <stdexcept>  #include <string> +#include "../api/Camera.h"  #include "../api/Sprite.h"  #include "../api/Texture.h"  #include "../api/Transform.h" +#include "../api/Vector2.h"  #include "../util/Log.h"  #include "SDLContext.h" @@ -30,29 +33,22 @@ SDLContext::SDLContext() {  	dbg_trace();  	// FIXME: read window defaults from config manager -	if (SDL_Init(SDL_INIT_VIDEO) < 0) { -		// FIXME: throw exception -		std::cerr << "SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl; -		return; +	if (SDL_Init(SDL_INIT_VIDEO) != 0) { +		throw runtime_error(format("SDLContext: SDL_Init error: {}", SDL_GetError()));  	}  	SDL_Window * tmp_window  		= SDL_CreateWindow("Crepe Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,  						   this->viewport.w, this->viewport.h, 0);  	if (!tmp_window) { -		// FIXME: throw exception -		std::cerr << "Window could not be created! SDL_Error: " << SDL_GetError() << std::endl; -		return; +		throw runtime_error(format("SDLContext: SDL_Window error: {}", SDL_GetError()));  	}  	this->game_window = {tmp_window, [](SDL_Window * window) { SDL_DestroyWindow(window); }};  	SDL_Renderer * tmp_renderer  		= SDL_CreateRenderer(this->game_window.get(), -1, SDL_RENDERER_ACCELERATED);  	if (!tmp_renderer) { -		// FIXME: throw exception -		std::cerr << "Renderer could not be created! SDL_Error: " << SDL_GetError() -				  << std::endl; -		SDL_DestroyWindow(this->game_window.get()); -		return; +		throw runtime_error( +			format("SDLContext: SDL_CreateRenderer error: {}", SDL_GetError()));  	}  	this->game_renderer @@ -60,9 +56,7 @@ SDLContext::SDLContext() {  	int img_flags = IMG_INIT_PNG;  	if (!(IMG_Init(img_flags) & img_flags)) { -		// FIXME: throw exception -		std::cout << "SDL_image could not initialize! SDL_image Error: " << IMG_GetError() -				  << std::endl; +		throw runtime_error("SDLContext: SDL_image could not initialize!");  	}  } @@ -78,7 +72,6 @@ SDLContext::~SDLContext() {  	IMG_Quit();  	SDL_Quit();  } -  void SDLContext::handle_events(bool & running) {  	//TODO: wouter i need events  	/* @@ -103,40 +96,63 @@ void SDLContext::handle_events(bool & running) {  void SDLContext::clear_screen() { SDL_RenderClear(this->game_renderer.get()); }  void SDLContext::present_screen() { SDL_RenderPresent(this->game_renderer.get()); } -void SDLContext::draw(const Sprite & sprite, const Transform & transform, const Camera & cam) { - -	SDL_RendererFlip render_flip -		= (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x) -							  | (SDL_FLIP_VERTICAL * sprite.flip.flip_y)); - -	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; - -	SDL_Rect srcrect = { +SDL_Rect SDLContext::get_src_rect(const Sprite & sprite) const { +	return SDL_Rect{  		.x = sprite.sprite_rect.x,  		.y = sprite.sprite_rect.y,  		.w = sprite.sprite_rect.w,  		.h = sprite.sprite_rect.h,  	}; +} +SDL_Rect SDLContext::get_dst_rect(const Sprite & sprite, const Vector2 & pos, +								  const double & scale, const Camera & cam) const { -	SDL_Rect dstrect = { +	double adjusted_x = (pos.x - cam.x) * cam.zoom; +	double adjusted_y = (pos.y - cam.y) * cam.zoom; +	double adjusted_w = sprite.sprite_rect.w * scale * cam.zoom; +	double adjusted_h = sprite.sprite_rect.h * scale * cam.zoom; + +	return SDL_Rect{  		.x = static_cast<int>(adjusted_x),  		.y = static_cast<int>(adjusted_y),  		.w = static_cast<int>(adjusted_w),  		.h = static_cast<int>(adjusted_h),  	}; +} + +void SDLContext::draw_particle(const Sprite & sprite, const Vector2 & pos, +							   const double & angle, const double & scale, +							   const Camera & camera) { + +	SDL_RendererFlip render_flip +		= (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x) +							  | (SDL_FLIP_VERTICAL * sprite.flip.flip_y)); + +	SDL_Rect srcrect = this->get_src_rect(sprite); +	SDL_Rect dstrect = this->get_dst_rect(sprite, pos, scale, camera); + +	SDL_RenderCopyEx(this->game_renderer.get(), sprite.sprite_image->texture.get(), &srcrect, +					 &dstrect, angle, NULL, render_flip); +} + +void SDLContext::draw(const Sprite & sprite, const Transform & transform, const Camera & cam) { + +	SDL_RendererFlip render_flip +		= (SDL_RendererFlip) ((SDL_FLIP_HORIZONTAL * sprite.flip.flip_x) +							  | (SDL_FLIP_VERTICAL * sprite.flip.flip_y)); + +	SDL_Rect srcrect = this->get_src_rect(sprite); +	SDL_Rect dstrect = this->get_dst_rect(sprite, transform.position, transform.scale, cam);  	SDL_RenderCopyEx(this->game_renderer.get(), sprite.sprite_image->texture.get(), &srcrect,  					 &dstrect, transform.rotation, NULL, render_flip);  } -void SDLContext::camera(const Camera & cam) { +void SDLContext::set_camera(const Camera & cam) {  	this->viewport.w = static_cast<int>(cam.aspect_width);  	this->viewport.h = static_cast<int>(cam.aspect_height); -	this->viewport.x = static_cast<int>(cam.x) - (SCREEN_WIDTH / 2); -	this->viewport.y = static_cast<int>(cam.y) - (SCREEN_HEIGHT / 2); +	this->viewport.x = static_cast<int>(cam.x) - (this->viewport.w / 2); +	this->viewport.y = static_cast<int>(cam.y) - (this->viewport.h / 2);  	SDL_SetRenderDrawColor(this->game_renderer.get(), cam.bg_color.r, cam.bg_color.g,  						   cam.bg_color.b, cam.bg_color.a); @@ -148,9 +164,8 @@ std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>>  SDLContext::texture_from_path(const std::string & path) {  	SDL_Surface * tmp = IMG_Load(path.c_str()); -	if (tmp == nullptr) { -		tmp = IMG_Load("../asset/texture/ERROR.png"); -	} +	if (tmp == nullptr) +		throw runtime_error(format("SDLContext: IMG_Load error: {}", SDL_GetError()));  	std::unique_ptr<SDL_Surface, std::function<void(SDL_Surface *)>> img_surface;  	img_surface = {tmp, [](SDL_Surface * surface) { SDL_FreeSurface(surface); }}; @@ -159,7 +174,7 @@ SDLContext::texture_from_path(const std::string & path) {  		= SDL_CreateTextureFromSurface(this->game_renderer.get(), img_surface.get());  	if (tmp_texture == nullptr) { -		throw runtime_error(format("Texture cannot be load from {}", path)); +		throw runtime_error(format("SDLContext: Texture cannot be load from {}", path));  	}  	std::unique_ptr<SDL_Texture, std::function<void(SDL_Texture *)>> img_texture; diff --git a/src/crepe/facade/SDLContext.h b/src/crepe/facade/SDLContext.h index 652a83e..841ffc9 100644 --- a/src/crepe/facade/SDLContext.h +++ b/src/crepe/facade/SDLContext.h @@ -1,8 +1,10 @@  #pragma once  #include <SDL2/SDL_keycode.h> +#include <SDL2/SDL_rect.h>  #include <SDL2/SDL_render.h>  #include <SDL2/SDL_video.h> +#include <cmath>  #include <functional>  #include <memory>  #include <string> @@ -10,10 +12,7 @@  #include "../api/Sprite.h"  #include "../api/Transform.h"  #include "api/Camera.h" - -// FIXME: this needs to be removed -const int SCREEN_WIDTH = 640; -const int SCREEN_HEIGHT = 480; +#include "api/Vector2.h"  namespace crepe { @@ -121,6 +120,9 @@ private:  	 */  	void draw(const Sprite & sprite, const Transform & transform, const Camera & camera); +	void draw_particle(const Sprite & sprite, const Vector2 & pos, const double & angle, +					   const double & scale, const Camera & camera); +  	//! Clears the screen, preparing for a new frame.  	void clear_screen(); @@ -128,10 +130,31 @@ private:  	void present_screen();  	/** -	 * \brief Sets the current camera for rendering. +	 * \brief sets the background of the camera (will be adjusted in future PR)  	 * \param camera Reference to the Camera object.  	 */ -	void camera(const Camera & camera); +	void set_camera(const Camera & camera); + +private: +	/** +	 * \brief calculates the sqaure size of the image +	 * +	 * \param sprite Reference to the sprite to calculate the rectangle +	 * \return sdl rectangle to draw a src image +	 */ +	SDL_Rect get_src_rect(const Sprite & sprite) const; +	/** +	 * \brief calculates the sqaure size of the image for an destination +	 * +	 * \param sprite Reference to the sprite to calculate the rectangle +	 * \param pos the pos in pixel positions +	 * \param scale the multiplier to increase of decrease for the specified sprite  +	 * \param cam Reference to the current camera in the scene to calculate the position based +	 * on the camera  +	 * \return sdl rectangle to draw a dst image to draw on the screen +	 */ +	SDL_Rect get_dst_rect(const Sprite & sprite, const Vector2 & pos, const double & scale, +						  const Camera & cam) const;  private:  	//! sdl Window diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp index 2132b0d..1a30e3f 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -220,8 +220,8 @@ std::vector<std::pair<CollisionSystem::CollidedInfoStor,CollisionSystem::Collide  			// 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} +					CollidedInfoStor{boxcolliders[i], transform1, rigidbody1},  +					CollidedInfoStor{boxcolliders[j], transform2, rigidbody2}  				));  			}  		} diff --git a/src/crepe/system/RenderSystem.cpp b/src/crepe/system/RenderSystem.cpp index 96c5f27..e379771 100644 --- a/src/crepe/system/RenderSystem.cpp +++ b/src/crepe/system/RenderSystem.cpp @@ -1,12 +1,16 @@  #include <algorithm>  #include <cassert> +#include <cmath>  #include <functional> +#include <iostream>  #include <stdexcept>  #include <vector>  #include "../ComponentManager.h" +#include "../api/ParticleEmitter.h"  #include "../api/Sprite.h"  #include "../api/Transform.h" +#include "../api/Vector2.h"  #include "../facade/SDLContext.h"  #include "RenderSystem.h" @@ -25,7 +29,8 @@ void RenderSystem::update_camera() {  	if (cameras.size() == 0) throw std::runtime_error("No cameras in current scene");  	for (Camera & cam : cameras) { -		this->context.camera(cam); +		if (!cam.active) continue; +		this->context.set_camera(cam);  		this->curr_cam_ref = &cam;  	}  } @@ -38,7 +43,7 @@ bool sorting_comparison(const Sprite & a, const Sprite & b) {  }  std::vector<std::reference_wrapper<Sprite>> -RenderSystem::sort(std::vector<std::reference_wrapper<Sprite>> & objs) { +RenderSystem::sort(std::vector<std::reference_wrapper<Sprite>> & objs) const {  	std::vector<std::reference_wrapper<Sprite>> sorted_objs(objs);  	std::sort(sorted_objs.begin(), sorted_objs.end(), sorting_comparison); @@ -46,20 +51,54 @@ RenderSystem::sort(std::vector<std::reference_wrapper<Sprite>> & objs) {  	return sorted_objs;  } -void RenderSystem::render_sprites() { +void RenderSystem::update() { +	this->clear_screen(); +	this->update_camera(); +	this->render(); +	this->present_screen(); +} + +bool RenderSystem::render_particle(const Sprite & sprite, const double & scale) { + +	ComponentManager & mgr = this->component_manager; + +	vector<reference_wrapper<ParticleEmitter>> emitters +		= mgr.get_components_by_id<ParticleEmitter>(sprite.game_object_id); + +	bool rendering_particles = false; + +	for (const ParticleEmitter & em : emitters) { +		if (!(&em.data.sprite == &sprite)) continue; +		rendering_particles = true; +		if (!em.active) continue; + +		for (const Particle & p : em.data.particles) { +			if (!p.active) continue; +			this->context.draw_particle(sprite, p.position, p.angle, scale, +										*this->curr_cam_ref); +		} +	} +	return rendering_particles; +} +void RenderSystem::render_normal(const Sprite & sprite, const Transform & tm) { +	this->context.draw(sprite, tm, *this->curr_cam_ref); +} + +void RenderSystem::render() { +  	ComponentManager & mgr = this->component_manager;  	vector<reference_wrapper<Sprite>> sprites = mgr.get_components_by_type<Sprite>();  	vector<reference_wrapper<Sprite>> sorted_sprites = this->sort(sprites);  	for (const Sprite & sprite : sorted_sprites) { -		auto transforms = mgr.get_components_by_id<Transform>(sprite.game_object_id); -		this->context.draw(sprite, transforms[0], *this->curr_cam_ref); -	} -} +		if (!sprite.active) continue; +		const Transform & transform +			= mgr.get_components_by_id<Transform>(sprite.game_object_id).front().get(); -void RenderSystem::update() { -	this->clear_screen(); -	this->update_camera(); -	this->render_sprites(); -	this->present_screen(); +		bool rendered_particles = this->render_particle(sprite, transform.scale); + +		if (rendered_particles) continue; + +		this->render_normal(sprite, transform); +	}  } diff --git a/src/crepe/system/RenderSystem.h b/src/crepe/system/RenderSystem.h index 57b9c73..d25a6e3 100644 --- a/src/crepe/system/RenderSystem.h +++ b/src/crepe/system/RenderSystem.h @@ -6,6 +6,7 @@  #include "facade/SDLContext.h"  #include "System.h" +#include <cmath>  namespace crepe { @@ -38,8 +39,25 @@ private:  	//! Updates the active camera used for rendering.  	void update_camera(); -	//! Renders all active sprites to the screen. -	void render_sprites(); +	//! Renders the whole screen +	void render(); + +	/** +	 * \brief Renders all the particles on the screen from a given sprite. +	 * +	 * \param sprite renders the particles with given texture +	 * \param tm the Transform component for scale +	 * \return true if particles have been rendered +	 */ +	bool render_particle(const Sprite & sprite, const double & scale); + +	/** +	 * \brief renders a sprite with a Transform component on the screen  +	 * +	 * \param sprite  the sprite component that holds all the data +	 * \param tm the Transform component that holds the position,rotation and scale  +	 */ +	void render_normal(const Sprite & sprite, const Transform & tm);  	/**  	 * \brief sort a vector sprite objects with @@ -48,11 +66,10 @@ private:  	 * \return returns a sorted reference vector  	 */  	std::vector<std::reference_wrapper<Sprite>> -	sort(std::vector<std::reference_wrapper<Sprite>> & objs); +	sort(std::vector<std::reference_wrapper<Sprite>> & objs) const;  	/**  	 * \todo Include color handling for sprites. -	 * \todo Implement particle emitter rendering with sprites.  	 * \todo Add text rendering using SDL_ttf for text components.  	 * \todo Implement a text component and a button component.  	 * \todo Ensure each sprite is checked for active status before rendering. diff --git a/src/crepe/util/Log.h b/src/crepe/util/Log.h index d55b11e..fc0bb3a 100644 --- a/src/crepe/util/Log.h +++ b/src/crepe/util/Log.h @@ -34,11 +34,11 @@ class Log {  public:  	//! Log message severity  	enum Level { -		TRACE, //< Include (internal) function calls -		DEBUG, //< Include dbg_logf output -		INFO, //< General-purpose messages -		WARNING, //< Non-fatal errors -		ERROR, //< Fatal errors +		TRACE, //!< Include (internal) function calls +		DEBUG, //!< Include dbg_logf output +		INFO, //!< General-purpose messages +		WARNING, //!< Non-fatal errors +		ERROR, //!< Fatal errors  	};  	/** diff --git a/src/crepe/util/Proxy.h b/src/crepe/util/Proxy.h index b34f7c6..789144a 100644 --- a/src/crepe/util/Proxy.h +++ b/src/crepe/util/Proxy.h @@ -16,6 +16,8 @@ template <typename T>  class Proxy {  public:  	//! Set operator +	Proxy & operator=(Proxy &); +	//! Set operator  	Proxy & operator=(const T &);  	//! Get operator  	operator const T &(); diff --git a/src/crepe/util/Proxy.hpp b/src/crepe/util/Proxy.hpp index b9923db..ef2b69f 100644 --- a/src/crepe/util/Proxy.hpp +++ b/src/crepe/util/Proxy.hpp @@ -14,6 +14,12 @@ Proxy<T> & Proxy<T>::operator=(const T & val) {  }  template <typename T> +Proxy<T> & Proxy<T>::operator=(Proxy & proxy) { +	this->broker.set(T(proxy)); +	return *this; +} + +template <typename T>  Proxy<T>::operator const T &() {  	return this->broker.get();  } |