diff options
Diffstat (limited to 'src/crepe/api')
46 files changed, 968 insertions, 830 deletions
| diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp new file mode 100644 index 0000000..e148367 --- /dev/null +++ b/src/crepe/api/Asset.cpp @@ -0,0 +1,54 @@ +#include <filesystem> +#include <stdexcept> +#include <whereami.h> + +#include "api/Config.h" + +#include "Asset.h" + +using namespace crepe; +using namespace std; + +Asset::Asset(const string & src) : src(find_asset(src)) {} +Asset::Asset(const char * src) : src(find_asset(src)) {} + +const string & Asset::get_path() const noexcept { return this->src; } + +string Asset::find_asset(const string & src) const { +	auto & cfg = Config::get_instance(); +	string & root_pattern = cfg.asset.root_pattern; + +	// if root_pattern is empty, find_asset must return all paths as-is +	if (root_pattern.empty()) return src; + +	// absolute paths do not need to be resolved, only canonicalized +	filesystem::path path = src; +	if (path.is_absolute()) return filesystem::canonical(path); + +	// find directory matching root_pattern +	filesystem::path root = this->whereami(); +	while (1) { +		if (filesystem::exists(root / root_pattern)) break; +		if (!root.has_parent_path()) +			throw runtime_error(format("Asset: Cannot find root pattern ({})", root_pattern)); +		root = root.parent_path(); +	} + +	// join path to root (base directory) and canonicalize +	return filesystem::canonical(root / path); +} + +string Asset::whereami() const noexcept { +	string path; +	size_t path_length = wai_getExecutablePath(NULL, 0, NULL); +	path.resize(path_length + 1); // wai writes null byte +	wai_getExecutablePath(path.data(), path_length, NULL); +	path.resize(path_length); +	return path; +} + +bool Asset::operator==(const Asset & other) const noexcept { return this->src == other.src; } + +size_t std::hash<const Asset>::operator()(const Asset & asset) const noexcept { +	return std::hash<string>{}(asset.get_path()); +}; diff --git a/src/crepe/api/Asset.h b/src/crepe/api/Asset.h new file mode 100644 index 0000000..bfd0ac7 --- /dev/null +++ b/src/crepe/api/Asset.h @@ -0,0 +1,84 @@ +#pragma once + +#include <string> + +namespace crepe { + +/** + * \brief Asset location helper + * + * This class is used to locate game asset files, and should *always* be used + * instead of reading file paths directly. + */ +class Asset { +public: +	/** +	 * \param src  Unique identifier to asset +	 */ +	Asset(const std::string & src); +	/** +	 * \param src  Unique identifier to asset +	 */ +	Asset(const char * src); + +public: +	/** +	 * \brief Get the path to this asset +	 * \return path to this asset +	 */ +	const std::string & get_path() const noexcept; + +	/** +	 * \brief Comparison operator +	 * \param other Possibly different instance of \c Asset to test equality against +	 * \return True if \c this and \c other are equal +	 */ +	bool operator==(const Asset & other) const noexcept; + +private: +	//! path to asset +	const std::string src; + +private: +	/** +	 * \brief Locate asset path, or throw exception if it cannot be found +	 * +	 * This function resolves asset locations relative to crepe::Config::root_pattern if it is +	 * set and \p src is a relative path. If \p src is an absolute path, it is canonicalized. +	 * This function only returns if the file can be found. +	 * +	 * \param src Arbitrary path to resource file +	 * +	 * \returns \p src if crepe::Config::root_pattern is empty +	 * \returns Canonical path to \p src +	 * +	 * \throws std::runtime_error if root_pattern cannot be found +	 * \throws std::filesystem::filesystem_error if the resolved path does not exist +	 * \throws std::filesystem::filesystem_error if the path cannot be canonicalized +	 */ +	std::string find_asset(const std::string & src) const; +	/** +	 * \returns The path to the current executable +	 */ +	std::string whereami() const noexcept; +}; + +} // namespace crepe + +namespace std { + +//! Hash helper struct +template <> +struct hash<const crepe::Asset> { +	/** +	 * \brief Hash operator for crepe::Asset +	 * +	 * This function hashes a crepe::Asset instance, allowing it to be used as a key in an \c +	 * std::unordered_map. +	 * +	 * \returns Hash value +	 */ +	size_t operator()(const crepe::Asset & asset) const noexcept; +}; + +} // namespace std diff --git a/src/crepe/api/BehaviorScript.h b/src/crepe/api/BehaviorScript.h index 9d85d4c..d556fe5 100644 --- a/src/crepe/api/BehaviorScript.h +++ b/src/crepe/api/BehaviorScript.h @@ -39,11 +39,14 @@ public:  	 * \brief Set the concrete script of this component  	 *  	 * \tparam T Concrete script type (derived from \c crepe::Script) +	 * \tparam Args Arguments for concrete script constructor +	 * +	 * \param args Arguments for concrete script constructor (forwarded using perfect forwarding)  	 *  	 * \returns Reference to BehaviorScript component (`*this`)  	 */ -	template <class T> -	BehaviorScript & set_script(); +	template <class T, typename... Args> +	BehaviorScript & set_script(Args &&... args);  protected:  	//! Script instance diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp index d80321d..bd59337 100644 --- a/src/crepe/api/BehaviorScript.hpp +++ b/src/crepe/api/BehaviorScript.hpp @@ -9,13 +9,17 @@  namespace crepe { -template <class T> -BehaviorScript & BehaviorScript::set_script() { +template <class T, typename... Args> +BehaviorScript & BehaviorScript::set_script(Args &&... args) {  	dbg_trace();  	static_assert(std::is_base_of<Script, T>::value); -	Script * s = new T(); +	Script * s = new T(std::forward<Args>(args)...); +  	s->game_object_id = this->game_object_id; -	s->component_manager_ref = &this->component_manager; +	s->active = this->active; +	s->component_manager = this->component_manager; +	s->event_manager = 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 a185ca6..50c51ed 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -12,9 +12,7 @@ target_sources(crepe PUBLIC  	SaveManager.cpp  	Config.cpp  	Metadata.cpp -	Scene.cpp  	SceneManager.cpp -	Vector2.cpp  	Camera.cpp  	Animator.cpp  	EventManager.cpp @@ -22,7 +20,9 @@ target_sources(crepe PUBLIC  	IMouseListener.cpp  	LoopManager.cpp  	LoopTimer.cpp +	Asset.cpp  	EventHandler.cpp +	Script.cpp  )  target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -36,6 +36,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	Rigidbody.h  	Sprite.h  	Vector2.h +	Vector2.hpp  	Color.h  	Texture.h   	AssetManager.h  @@ -56,4 +57,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES  	IMouseListener.h  	LoopManager.h  	LoopTimer.h +	Asset.h  ) diff --git a/src/crepe/api/Color.cpp b/src/crepe/api/Color.cpp index 9e5f187..29bd77a 100644 --- a/src/crepe/api/Color.cpp +++ b/src/crepe/api/Color.cpp @@ -2,32 +2,11 @@  using namespace crepe; -Color Color::white = Color(255, 255, 255, 0); -Color Color::red = Color(255, 0, 0, 0); -Color Color::green = Color(0, 255, 0, 0); -Color Color::blue = Color(0, 0, 255, 0); -Color Color::black = Color(0, 0, 0, 0); -Color Color::cyan = Color(0, 255, 255, 0); -Color Color::yellow = Color(255, 255, 0, 0); -Color Color::magenta = Color(255, 0, 255, 0); - -Color::Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) { -	this->a = alpha; -	this->r = red; -	this->g = green; -	this->b = blue; -}; - -const Color & Color::get_white() { return Color::white; }; - -const Color & Color::get_red() { return Color::red; }; -const Color & Color::get_green() { return Color::green; }; -const Color & Color::get_blue() { return Color::blue; }; - -const Color & Color::get_black() { return Color::black; }; - -const Color & Color::get_cyan() { return Color::cyan; }; - -const Color & Color::get_yellow() { return Color::yellow; }; - -const Color & Color::get_magenta() { return Color::magenta; }; +const Color Color::WHITE{0xff, 0xff, 0xff}; +const Color Color::RED{0xff, 0x00, 0x00}; +const Color Color::GREEN{0x00, 0xff, 0x00}; +const Color Color::BLUE{0x00, 0x00, 0xff}; +const Color Color::BLACK{0x00, 0x00, 0x00}; +const Color Color::CYAN{0x00, 0xff, 0xff}; +const Color Color::YELLOW{0xff, 0xff, 0x00}; +const Color Color::MAGENTA{0xff, 0x00, 0xff}; diff --git a/src/crepe/api/Color.h b/src/crepe/api/Color.h index aa47bf4..84edb5c 100644 --- a/src/crepe/api/Color.h +++ b/src/crepe/api/Color.h @@ -4,41 +4,20 @@  namespace crepe { -// TODO: make Color a struct w/o constructors/destructors -class Color { - -	// FIXME: can't these colors be defined as a `static constexpr const Color` -	// instead? - -public: -	Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha); -	static const Color & get_white(); -	static const Color & get_red(); -	static const Color & get_green(); -	static const Color & get_blue(); -	static const Color & get_cyan(); -	static const Color & get_magenta(); -	static const Color & get_yellow(); -	static const Color & get_black(); - -private: -	// TODO: why are these private!? -	uint8_t r; -	uint8_t g; -	uint8_t b; -	uint8_t a; - -	static Color white; -	static Color red; -	static Color green; -	static Color blue; -	static Color cyan; -	static Color magenta; -	static Color yellow; -	static Color black; - -private: -	friend class SDLContext; +struct Color { +	uint8_t r = 0x00; +	uint8_t g = 0x00; +	uint8_t b = 0x00; +	uint8_t a = 0xff; + +	static const Color WHITE; +	static const Color RED; +	static const Color GREEN; +	static const Color BLUE; +	static const Color CYAN; +	static const Color MAGENTA; +	static const Color YELLOW; +	static const Color BLACK;  };  } // namespace crepe diff --git a/src/crepe/api/Config.h b/src/crepe/api/Config.h index 3ab877a..0c9d116 100644 --- a/src/crepe/api/Config.h +++ b/src/crepe/api/Config.h @@ -11,19 +11,18 @@ namespace crepe {   * modified *before* execution is handed over from the game programmer to the engine (i.e. the   * main loop is started).   */ -class Config { +class Config final {  public:  	//! Retrieve handle to global Config instance  	static Config & get_instance();  private:  	Config() = default; - -	// singleton -	Config(const Config &) = delete; -	Config(Config &&) = delete; -	Config & operator=(const Config &) = delete; -	Config & operator=(Config &&) = delete; +	~Config() = default; +	Config(const Config &) = default; +	Config(Config &&) = default; +	Config & operator=(const Config &) = default; +	Config & operator=(Config &&) = default;  public:  	//! Logging-related settings @@ -62,6 +61,20 @@ public:  		 */  		double gravity = 1;  	} physics; + +	//! Asset loading options +	struct { +		/** +		 * \brief Pattern to match for Asset base directory +		 * +		 * All non-absolute paths resolved using \c Asset will be made relative to +		 * the first parent directory relative to the calling executable where +		 * appending this pattern results in a path that exists. If this string is +		 * empty, path resolution is disabled, and Asset will return all paths +		 * as-is. +		 */ +		std::string root_pattern = ".crepe-root"; +	} asset;  };  } // namespace crepe diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h index ef6a791..bac8701 100644 --- a/src/crepe/api/Event.h +++ b/src/crepe/api/Event.h @@ -1,23 +1,24 @@  #pragma once +// TODO discussing the location of these events  #include <string>  #include "KeyCodes.h" +namespace crepe { +  /**   * \brief Base class for all event types in the system.   */ -class Event { -public: -}; +class Event {};  /**   * \brief Event triggered when a key is pressed.   */  class KeyPressEvent : public Event {  public: -	//! Number of times the key press is repeated (e.g., for long presses). -	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; @@ -96,11 +97,7 @@ public:  /**   * \brief Event triggered during a collision between objects.   */ -class CollisionEvent : public Event { -public: -	//! Data describing the collision (currently not implemented). -	// Collision collisionData; -}; +class CollisionEvent : public Event {};  /**   * \brief Event triggered when text is submitted, e.g., from a text input. @@ -118,14 +115,4 @@ class ShutDownEvent : public Event {  public:  }; -class MouseScrollEvent : public Event { -public: -	//! X-coordinate of the mouse position at the time of the event. -	int scroll_x = 0; - -	//! Y-coordinate of the mouse position at the time of the event. -	int scroll_y = 0; -	 -	int direction = 0; -}; - +} diff --git a/src/crepe/api/EventHandler.cpp b/src/crepe/api/EventHandler.cpp index 186ec9c..4dc232f 100644 --- a/src/crepe/api/EventHandler.cpp +++ b/src/crepe/api/EventHandler.cpp @@ -2,5 +2,4 @@  using namespace crepe; -// Implementation of IEventHandlerWrapper::exec -bool IEventHandlerWrapper::exec(const Event & e) { return call(e); } +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 db51d04..ef659fd 100644 --- a/src/crepe/api/EventHandler.h +++ b/src/crepe/api/EventHandler.h @@ -13,6 +13,8 @@ namespace crepe {   * 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.   */  template <typename EventType>  using EventHandler = std::function<bool(const EventType & e)>; @@ -22,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: @@ -41,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. @@ -94,20 +87,8 @@ private:       * \return A boolean value indicating whether the event is handled.       */  	bool call(const Event & e) override; - -	/** -     * \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; -  	//! 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 diff --git a/src/crepe/api/EventHandler.hpp b/src/crepe/api/EventHandler.hpp index 9c47da2..050e57e 100644 --- a/src/crepe/api/EventHandler.hpp +++ b/src/crepe/api/EventHandler.hpp @@ -1,3 +1,4 @@ +#pragma once  #include <typeindex> @@ -8,19 +9,12 @@ namespace crepe {  // Implementation of EventHandlerWrapper constructor  template <typename EventType>  EventHandlerWrapper<EventType>::EventHandlerWrapper(const EventHandler<EventType> & handler) -	: m_handler(handler), -	  m_handler_type(m_handler.target_type().name()) {} +	: handler(handler) {}  // Implementation of EventHandlerWrapper::call  template <typename EventType>  bool EventHandlerWrapper<EventType>::call(const Event & e) { -	return m_handler(static_cast<const EventType &>(e)); -} - -// Implementation of EventHandlerWrapper::get_type -template <typename EventType> -std::string EventHandlerWrapper<EventType>::get_type() const { -	return m_handler_type; +	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 b465e89..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,53 +9,38 @@ EventManager & EventManager::get_instance() {  }  void EventManager::dispatch_events() { -	using HandlersMap -		= std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>; -	using HandlersVec = std::vector<std::unique_ptr<IEventHandlerWrapper>>; - -	for (auto 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) { -			auto handlers_it = subscribers_by_event_id.find(event_type); -			if (handlers_it != subscribers_by_event_id.end()) { -				HandlersMap & handlers_map = handlers_it->second; -				auto handlers = handlers_map.find(channel); -				if (handlers != handlers_map.end()) { -					HandlersVec & callbacks = handlers->second; -					for (auto 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 -			auto handlers_it = this->subscribers.find(event_type); -			if (handlers_it != this->subscribers.end()) { -				HandlersVec & handlers = handlers_it->second; -				for (auto 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; -					} -				} -			} -		} +	for (auto & event : this->events_queue) { +		this->handle_event(event.type, event.channel, *event.event.get()); +	} +	this->events_queue.clear(); +} + +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; + +	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 92273df..1a33023 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> @@ -12,98 +10,152 @@  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: +	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 = 0); +	 * \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 = 0); +	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 = 0); +	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 b20b88f..a5f4556 100644 --- a/src/crepe/api/EventManager.hpp +++ b/src/crepe/api/EventManager.hpp @@ -1,110 +1,36 @@ +#pragma once +  #include "EventManager.h"  namespace crepe {  template <typename EventType> -void EventManager::subscribe(EventHandler<EventType> && callback, int channel) { -	using HandlersMap -		= std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>; -	using HandlersVec = std::vector<std::unique_ptr<IEventHandlerWrapper>>; - +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) { -		HandlersMap & handlers_map = this->subscribers_by_event_id[event_type]; -		auto 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 { -		HandlersVec & handlers = this->subscribers[event_type]; -		handlers.emplace_back(std::move(handler)); -	} +	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::queue_event(EventType && event, int channel) { -	std::type_index event_type = std::type_index(typeid(EventType)); - -	auto 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)); +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::trigger_event(const EventType & event, int channel) { -	using HandlersMap -		= std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>; -	using HandlersVec = std::vector<std::unique_ptr<IEventHandlerWrapper>>; - -	std::type_index event_type = std::type_index(typeid(EventType)); - -	if (channel > 0) { -		HandlersMap & handlers_map = this->subscribers_by_event_id[event_type]; -		auto handlers_it = handlers_map.find(channel); - -		if (handlers_it != handlers_map.end()) { -			HandlersVec & handlers = handlers_it->second; -			for (auto it = handlers.begin(); it != handlers.end(); ++it) { -				// stops when callback returns true -				if ((*it)->exec(event)) { -					break; -				} -			} -		} -	} else { -		HandlersVec & handlers = this->subscribers[event_type]; -		for (auto 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) { -	using HandlersMap -		= std::unordered_map<int, std::vector<std::unique_ptr<IEventHandlerWrapper>>>; -	using HandlersVec = std::vector<std::unique_ptr<IEventHandlerWrapper>>; - -	std::type_index event_type(typeid(EventType)); -	std::string handler_name = callback.target_type().name(); - -	if (channel) { -		auto subscriber_list = this->subscribers_by_event_id.find(event_type); -		if (subscriber_list != this->subscribers_by_event_id.end()) { -			HandlersMap & handlers_map = subscriber_list->second; -			auto handlers = handlers_map.find(channel); -			if (handlers != handlers_map.end()) { -				HandlersVec & callbacks = handlers->second; -				for (auto it = callbacks.begin(); it != callbacks.end(); ++it) { -					if ((*it)->get_type() == handler_name) { -						it = callbacks.erase(it); -						return; -					} -				} -			} -		} -	} else { -		auto handlers_it = this->subscribers.find(event_type); -		if (handlers_it != this->subscribers.end()) { -			HandlersVec & handlers = handlers_it->second; -			for (auto 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/GameObject.cpp b/src/crepe/api/GameObject.cpp index 287e81d..3c36a21 100644 --- a/src/crepe/api/GameObject.cpp +++ b/src/crepe/api/GameObject.cpp @@ -9,7 +9,7 @@ using namespace std;  GameObject::GameObject(ComponentManager & component_manager, game_object_id_t id,  					   const std::string & name, const std::string & tag, -					   const Vector2 & position, double rotation, double scale) +					   const vec2 & position, double rotation, double scale)  	: id(id),  	  component_manager(component_manager) { @@ -23,12 +23,10 @@ void GameObject::set_parent(const GameObject & parent) {  	ComponentManager & mgr = this->component_manager;  	// Set parent on own Metadata component -	vector<reference_wrapper<Metadata>> this_metadata -		= mgr.get_components_by_id<Metadata>(this->id); +	RefVector<Metadata> this_metadata = mgr.get_components_by_id<Metadata>(this->id);  	this_metadata.at(0).get().parent = parent.id;  	// Add own id to children list of parent's Metadata component -	vector<reference_wrapper<Metadata>> parent_metadata -		= mgr.get_components_by_id<Metadata>(parent.id); +	RefVector<Metadata> parent_metadata = mgr.get_components_by_id<Metadata>(parent.id);  	parent_metadata.at(0).get().children.push_back(this->id);  } diff --git a/src/crepe/api/GameObject.h b/src/crepe/api/GameObject.h index 34ef8bb..fcb8d9a 100644 --- a/src/crepe/api/GameObject.h +++ b/src/crepe/api/GameObject.h @@ -2,7 +2,6 @@  #include <string> -#include "Vector2.h"  #include "types.h"  namespace crepe { @@ -30,7 +29,7 @@ private:  	 * \param scale The scale of the GameObject  	 */  	GameObject(ComponentManager & component_manager, game_object_id_t id, -			   const std::string & name, const std::string & tag, const Vector2 & position, +			   const std::string & name, const std::string & tag, const vec2 & position,  			   double rotation, double scale);  	//! ComponentManager instances GameObject  	friend class ComponentManager; diff --git a/src/crepe/api/IKeyListener.cpp b/src/crepe/api/IKeyListener.cpp index f5426be..8642655 100644 --- a/src/crepe/api/IKeyListener.cpp +++ b/src/crepe/api/IKeyListener.cpp @@ -2,65 +2,18 @@  using namespace crepe; -// Constructor with default channel -IKeyListener::IKeyListener() -	: channel(0), -	  active(true), -	  event_manager(EventManager::get_instance()) { -	this->subscribe_events(); -} -  // Constructor with specified channel -IKeyListener::IKeyListener(int channel) -	: channel(channel), -	  active(true), -	  event_manager(EventManager::get_instance()) { -	this->subscribe_events(); +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);  }  // Destructor, unsubscribe events -IKeyListener::~IKeyListener() { this->unsubscribe_events(); } - -// Subscribe to key 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); }; - -	event_manager.subscribe<KeyPressEvent>(std::move(this->key_pressed_handler), -										   this->channel); -	event_manager.subscribe<KeyReleaseEvent>(std::move(this->key_released_handler), -											 this->channel); -} - -// Unsubscribe from key events -void IKeyListener::unsubscribe_events() { -	event_manager.unsubscribe<KeyPressEvent>(this->key_pressed_handler, this->channel); -	event_manager.unsubscribe<KeyReleaseEvent>(this->key_released_handler, this->channel); -} - -// Activate key listening -void IKeyListener::activate_keys() { -	if (this->active) { -		return; -	} -	this->active = true; -	this->subscribe_events(); -} - -// Deactivate key listening -void IKeyListener::deactivate_keys() { -	if (!this->active) { -		return; -	} -	this->active = false; -	this->unsubscribe_events(); -} - -// Set a new channel for key events -void IKeyListener::set_channel(int channel) { -	this->unsubscribe_events(); -	this->channel = channel; -	this->subscribe_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 d492387..328a4c2 100644 --- a/src/crepe/api/IKeyListener.h +++ b/src/crepe/api/IKeyListener.h @@ -16,11 +16,11 @@ public:       * \brief Constructs an IKeyListener with a specified channel.       * \param channel The channel ID for event handling.       */ -	IKeyListener(int channel); -	IKeyListener(); +	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;  	/** @@ -37,42 +37,12 @@ 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;  }; diff --git a/src/crepe/api/IMouseListener.cpp b/src/crepe/api/IMouseListener.cpp index 5ee2814..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) -	: event_manager(EventManager::get_instance()), -	  channel(channel) { -	this->subscribe_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);  } -IMouseListener::IMouseListener() : event_manager(EventManager::get_instance()) { -	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); }; - -	// Subscribe event handlers (no need for std::move) -	event_manager.subscribe<MouseClickEvent>(std::move(mouse_click_handler), this->channel); -	event_manager.subscribe<MousePressEvent>(std::move(mouse_press_handler), this->channel); -	event_manager.subscribe<MouseReleaseEvent>(std::move(mouse_release_handler), -											   this->channel); -	event_manager.subscribe<MouseMoveEvent>(std::move(mouse_move_handler), this->channel); -} - -void IMouseListener::unsubscribe_events() { +IMouseListener::~IMouseListener() {  	// Unsubscribe event handlers -	event_manager.unsubscribe<MouseClickEvent>(mouse_click_handler, this->channel); -	event_manager.unsubscribe<MousePressEvent>(mouse_press_handler, this->channel); -	event_manager.unsubscribe<MouseReleaseEvent>(mouse_release_handler, this->channel); -	event_manager.unsubscribe<MouseMoveEvent>(mouse_move_handler, this->channel); -} - -void IMouseListener::activate_mouse() { -	if (this->active) { -		return; -	} -	this->subscribe_events(); -	this->active = true; -} - -void IMouseListener::deactivate_mouse() { -	if (!this->active) { -		return; -	} -	this->unsubscribe_events(); -	this->active = false; -} - -void IMouseListener::set_channel(int channel) { -	this->unsubscribe_events(); -	this->channel = channel; -	this->subscribe_events(); +	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 c315c35..15e1619 100644 --- a/src/crepe/api/IMouseListener.h +++ b/src/crepe/api/IMouseListener.h @@ -12,15 +12,15 @@ namespace crepe {   */  class IMouseListener {  public: -	IMouseListener();  	/**       * \brief Constructs an IMouseListener with a specified channel.       * \param channel The channel ID for event handling.       */ -	IMouseListener(int channel); +	IMouseListener(event_channel_t channel = EventManager::CHANNEL_ALL);  	virtual ~IMouseListener(); -	IMouseListener(const IMouseListener &) = delete;  	IMouseListener & operator=(const IMouseListener &) = delete; +	IMouseListener(const IMouseListener &) = delete; +	IMouseListener & operator=(const IMouseListener &&) = delete;  	IMouseListener(IMouseListener &&) = delete;  	/** @@ -56,46 +56,16 @@ 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;  }; diff --git a/src/crepe/api/KeyCodes.h b/src/crepe/api/KeyCodes.h index feed0b2..9e173e0 100644 --- a/src/crepe/api/KeyCodes.h +++ b/src/crepe/api/KeyCodes.h @@ -1,170 +1,93 @@  #pragma once -//! \enum MouseButton -//! \brief Enumeration for mouse button inputs, including standard and extended buttons. +//! Enumeration for mouse button inputs, including standard and extended buttons.  enum class MouseButton { -	//! No mouse button input. -	NONE = 0, -	//! Left mouse button. -	LEFT_MOUSE = 1, -	//! Right mouse button. -	RIGHT_MOUSE = 2, -	//! Middle mouse button (scroll wheel press). -	MIDDLE_MOUSE = 3, -	//! First extended mouse button. -	X1_MOUSE = 4, -	//! Second extended mouse button. -	X2_MOUSE = 5, -	//! Scroll wheel upward movement. -	SCROLL_UP = 6, -	//! Scroll wheel downward movement. -	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 Keycode -//! \brief Enumeration for keyboard key inputs, including printable characters, function keys, and keypad keys. -enum class Keycode : int { -	//! No key input. -	NONE = 0, -	//! Spacebar. -	SPACE = 32, -	//! Apostrophe ('). -	APOSTROPHE = 39, -	//! Comma (,). -	COMMA = 44, -	//! Minus (-). -	MINUS = 45, -	//! Period (.). -	PERIOD = 46, -	//! Slash (/). -	SLASH = 47, -	//! Digit 0. -	D0 = 48, -	//! Digit 1. -	D1 = 49, -	//! Digit 2. -	D2 = 50, -	//! Digit 3. -	D3 = 51, -	//! Digit 4. -	D4 = 52, -	//! Digit 5. -	D5 = 53, -	//! Digit 6. -	D6 = 54, -	//! Digit 7. -	D7 = 55, -	//! Digit 8. -	D8 = 56, -	//! Digit 9. -	D9 = 57, -	//! Semicolon (;). -	SEMICOLON = 59, -	//! Equal sign (=). -	EQUAL = 61, -	//! Key 'A'. -	A = 65, -	//! Key 'B'. -	B = 66, -	//! Key 'C'. -	C = 67, -	//! Key 'D'. -	D = 68, -	//! Key 'E'. -	E = 69, -	//! Key 'F'. -	F = 70, -	//! Key 'G'. -	G = 71, -	//! Key 'H'. -	H = 72, -	//! Key 'I'. -	I = 73, -	//! Key 'J'. -	J = 74, -	//! Key 'K'. -	K = 75, -	//! Key 'L'. -	L = 76, -	//! Key 'M'. -	M = 77, -	//! Key 'N'. -	N = 78, -	//! Key 'O'. -	O = 79, -	//! Key 'P'. -	P = 80, -	//! Key 'Q'. -	Q = 81, -	//! Key 'R'. -	R = 82, -	//! Key 'S'. -	S = 83, -	//! Key 'T'. -	T = 84, -	//! Key 'U'. -	U = 85, -	//! Key 'V'. -	V = 86, -	//! Key 'W'. -	W = 87, -	//! Key 'X'. -	X = 88, -	//! Key 'Y'. -	Y = 89, -	//! Key 'Z'. -	Z = 90, -	//! Left bracket ([). -	LEFT_BRACKET = 91, -	//! Backslash (\). -	BACKSLASH = 92, -	//! Right bracket (]). -	RIGHT_BRACKET = 93, -	//! Grave accent (`). -	GRAVE_ACCENT = 96, -	//! Non-US key #1. -	WORLD1 = 161, -	//! Non-US key #2. -	WORLD2 = 162, -	//! Escape key. -	ESCAPE = 256, -	//! Enter key. -	ENTER = 257, -	//! Tab key. -	TAB = 258, -	//! Backspace key. -	BACKSPACE = 259, -	//! Insert key. -	INSERT = 260, -	//! Delete key. -	DELETE = 261, -	//! Right arrow key. -	RIGHT = 262, -	//! Left arrow key. -	LEFT = 263, -	//! Down arrow key. -	DOWN = 264, -	//! Up arrow key. -	UP = 265, -	//! Page Up key. -	PAGE_UP = 266, -	//! Page Down key. -	PAGE_DOWN = 267, -	//! Home key. -	HOME = 268, -	//! End key. -	END = 269, -	//! Caps Lock key. -	CAPS_LOCK = 280, -	//! Scroll Lock key. -	SCROLL_LOCK = 281, -	//! Num Lock key. -	NUM_LOCK = 282, -	//! Print Screen key. -	PRINT_SCREEN = 283, -	//! Pause key. -	PAUSE = 284, -	//! Function keys (F1-F25). +//! 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, @@ -190,7 +113,11 @@ enum class Keycode : int {  	F23 = 312,  	F24 = 313,  	F25 = 314, -	//! Keypad digits and operators. +	/// \} +	/** +	 * \name Keypad digits and operators. +	 * \{ +	 */  	KP0 = 320,  	KP1 = 321,  	KP2 = 322, @@ -208,7 +135,11 @@ enum class Keycode : int {  	KP_ADD = 334,  	KP_ENTER = 335,  	KP_EQUAL = 336, -	//! Modifier keys. +	/// \} +	/** +	 * \name Modifier keys. +	 * \{ +	 */  	LEFT_SHIFT = 340,  	LEFT_CONTROL = 341,  	LEFT_ALT = 342, @@ -217,6 +148,6 @@ enum class Keycode : int {  	RIGHT_CONTROL = 345,  	RIGHT_ALT = 346,  	RIGHT_SUPER = 347, -	//! Menu key. -	MENU = 348 +	/// \} +	MENU = 348, //!< Menu key.  }; diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index af306c0..9599943 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -59,7 +59,7 @@ void LoopManager::loop() {  void LoopManager::setup() {  	this->game_running = true;  	LoopTimer::get_instance().start(); -	LoopTimer::get_instance().set_fps(60); +	LoopTimer::get_instance().set_fps(200);  }  void LoopManager::render() { diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index b18c9d1..13e6dac 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -4,19 +4,27 @@  #include "../ComponentManager.h"  #include "../system/System.h" +#include "api/SceneManager.h"  namespace crepe { +/** + * \brief Main game loop manager + * + * This class is responsible for managing the game loop, including initialization and updating. + */  class LoopManager {  public:  	void start(); +	LoopManager(); +  	/** -	 * \brief Set game running variable +	 * \brief Add a new concrete scene to the scene manager  	 * -	 * \param running running (false = game shutdown, true = game running) +	 * \tparam T  Type of concrete scene  	 */ -	void set_running(bool running); -	LoopManager(); +	template <typename T> +	void add_scene();  private:  	/** @@ -59,7 +67,14 @@ private:  	 * This function updates physics and game logic based on LoopTimer's fixed_delta_time.  	 */  	void fixed_update(); -	 + +	/** +	 * \brief Set game running variable +	 * +	 * \param running running (false = game shutdown, true = game running) +	 */ +	void set_running(bool running); +  	/**  	 * \brief Function for executing render-related systems.  	 * @@ -72,6 +87,8 @@ private:  private:  	//! Component manager instance  	ComponentManager component_manager{}; +	//! Scene manager instance +	SceneManager scene_manager{component_manager};  private:  	/** diff --git a/src/crepe/api/LoopManager.hpp b/src/crepe/api/LoopManager.hpp index 0b14fdb..9cf470b 100644 --- a/src/crepe/api/LoopManager.hpp +++ b/src/crepe/api/LoopManager.hpp @@ -11,6 +11,11 @@  namespace crepe {  template <class T> +void LoopManager::add_scene() { +	this->scene_manager.add_scene<T>(); +} + +template <class T>  T & LoopManager::get_system() {  	using namespace std;  	static_assert(is_base_of<System, T>::value, diff --git a/src/crepe/api/LoopTimer.cpp b/src/crepe/api/LoopTimer.cpp index a9800b7..15a0e3a 100644 --- a/src/crepe/api/LoopTimer.cpp +++ b/src/crepe/api/LoopTimer.cpp @@ -47,7 +47,7 @@ double LoopTimer::get_fixed_delta_time() const { return this->fixed_delta_time.c  void LoopTimer::set_fps(int fps) {  	this->fps = fps;  	// target time per frame in seconds -	this->frame_target_time = std::chrono::seconds(1) / fps; +	this->frame_target_time = std::chrono::duration<double>(1.0) / fps;  }  int LoopTimer::get_fps() const { return this->fps; } diff --git a/src/crepe/api/LoopTimer.h b/src/crepe/api/LoopTimer.h index f277d7b..9393439 100644 --- a/src/crepe/api/LoopTimer.h +++ b/src/crepe/api/LoopTimer.h @@ -130,9 +130,9 @@ private:  	//! Delta time for the current frame in seconds  	std::chrono::duration<double> delta_time{0.0};  	//! Target time per frame in seconds -	std::chrono::duration<double> frame_target_time = std::chrono::seconds(1) / fps; +	std::chrono::duration<double> frame_target_time = std::chrono::duration<double>(1.0) / fps;  	//! Fixed delta time for fixed updates in seconds -	std::chrono::duration<double> fixed_delta_time = std::chrono::seconds(1) / 50; +	std::chrono::duration<double> fixed_delta_time = std::chrono::duration<double>(1.0) / 50.0;  	//! Total elapsed game time in seconds  	std::chrono::duration<double> elapsed_time{0.0};  	//! Total elapsed time for fixed updates in seconds diff --git a/src/crepe/api/ParticleEmitter.h b/src/crepe/api/ParticleEmitter.h index 33112e1..b83fd61 100644 --- a/src/crepe/api/ParticleEmitter.h +++ b/src/crepe/api/ParticleEmitter.h @@ -4,7 +4,7 @@  #include "Component.h"  #include "Particle.h" -#include "Vector2.h" +#include "types.h"  namespace crepe { @@ -30,7 +30,7 @@ public:  		//! boundary height (midpoint is emitter location)  		double height = 0.0;  		//! boundary offset from particle emitter location -		Vector2 offset; +		vec2 offset;  		//! reset on exit or stop velocity and set max postion  		bool reset_on_exit = false;  	}; @@ -43,7 +43,7 @@ public:  	 */  	struct Data {  		//! position of the emitter -		Vector2 position; +		vec2 position;  		//! maximum number of particles  		const unsigned int max_particles = 0;  		//! rate of particle emission per update (Lowest value = 0.001 any lower is ignored) @@ -61,7 +61,7 @@ public:  		//! end Lifespan of particle  		double end_lifespan = 0.0;  		//! force over time (physics) -		Vector2 force_over_time; +		vec2 force_over_time;  		//! particle boundary  		Boundary boundary;  		//! collection of particles diff --git a/src/crepe/api/Rigidbody.cpp b/src/crepe/api/Rigidbody.cpp index 6b87695..576ca45 100644 --- a/src/crepe/api/Rigidbody.cpp +++ b/src/crepe/api/Rigidbody.cpp @@ -6,7 +6,7 @@ crepe::Rigidbody::Rigidbody(game_object_id_t id, const Data & data)  	: Component(id),  	  data(data) {} -void crepe::Rigidbody::add_force_linear(const Vector2 & force) { +void crepe::Rigidbody::add_force_linear(const vec2 & force) {  	this->data.linear_velocity += force;  } diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h index 3e5c7a3..3b0588f 100644 --- a/src/crepe/api/Rigidbody.h +++ b/src/crepe/api/Rigidbody.h @@ -2,7 +2,7 @@  #include "../Component.h" -#include "Vector2.h" +#include "types.h"  namespace crepe { @@ -56,11 +56,11 @@ public:  		//! Changes if physics apply  		BodyType body_type = BodyType::DYNAMIC;  		//! linear velocity of object -		Vector2 linear_velocity; +		vec2 linear_velocity;  		//! maximum linear velocity of object -		Vector2 max_linear_velocity; +		vec2 max_linear_velocity;  		//! linear damping of object -		Vector2 linear_damping; +		vec2 linear_damping;  		//! angular velocity of object  		double angular_velocity = 0.0;  		//! max angular velocity of object @@ -90,7 +90,7 @@ public:  	 *   	 * \param force Vector2 that is added to the linear force.  	 */ -	void add_force_linear(const Vector2 & force); +	void add_force_linear(const vec2 & force);  	/**   	 * \brief add a angular force to the Rigidbody.  	 *  diff --git a/src/crepe/api/Scene.cpp b/src/crepe/api/Scene.cpp deleted file mode 100644 index 88aa82d..0000000 --- a/src/crepe/api/Scene.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "Scene.h" - -using namespace crepe; - -Scene::Scene(ComponentManager & mgr, const std::string & name) -	: component_manager(mgr), -	  name(name) {} diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h index 0e516b6..f6fdb2a 100644 --- a/src/crepe/api/Scene.h +++ b/src/crepe/api/Scene.h @@ -2,25 +2,53 @@  #include <string> +#include "../util/OptionalRef.h" +  namespace crepe {  class SceneManager;  class ComponentManager; +/** + * \brief Represents a Scene + *  + * This class represents a Scene. The Scene class is only used as an interface for the game + * programmer. + */  class Scene {  protected: -	Scene(ComponentManager & mgr, const std::string & name); +	// NOTE: This must be the only constructor on Scene, see "Late references" below +	Scene() = default; +	//! SceneManager instances Scene  	friend class SceneManager;  public:  	virtual ~Scene() = default;  public: +	//! Load the scene  	virtual void load_scene() = 0; -	const std::string name; +	/** +	 * \brief Get the scene's name +	 * \return The scene's name +	 */ +	virtual std::string get_name() const = 0;  protected: -	ComponentManager & component_manager; +	/** +	 * \name Late references +	 *  +	 * These references are set by SceneManager immediately after calling the constructor of Scene. +	 * +	 * \note Scene must have a constructor without arguments so the game programmer doesn't need to +	 * manually add `using Scene::Scene` to their concrete scene class, if they want to add a +	 * constructor with arguments (e.g. for passing references to their own concrete Scene classes). +	 * +	 * \{ +	 */ +	//! Reference to the ComponentManager +	OptionalRef<ComponentManager> component_manager; +	//! \}  };  } // namespace crepe diff --git a/src/crepe/api/SceneManager.cpp b/src/crepe/api/SceneManager.cpp index 7fb5cb0..1f783ad 100644 --- a/src/crepe/api/SceneManager.cpp +++ b/src/crepe/api/SceneManager.cpp @@ -18,7 +18,7 @@ void SceneManager::load_next_scene() {  	auto it = find_if(this->scenes.begin(), this->scenes.end(),  					  [&next_scene = this->next_scene](unique_ptr<Scene> & scene) { -						  return scene->name == next_scene; +						  return scene.get()->get_name() == next_scene;  					  });  	// next scene not found diff --git a/src/crepe/api/SceneManager.h b/src/crepe/api/SceneManager.h index e854794..f6f62cd 100644 --- a/src/crepe/api/SceneManager.h +++ b/src/crepe/api/SceneManager.h @@ -1,7 +1,6 @@  #pragma once  #include <memory> -#include <queue>  #include <vector>  #include "Scene.h" @@ -10,8 +9,15 @@ namespace crepe {  class ComponentManager; +/** + * \brief Manages scenes + * + * This class manages scenes. It can add new scenes and load them. It also manages the current scene + * and the next scene. + */  class SceneManager {  public: +	//! \param mgr  Reference to the ComponentManager  	SceneManager(ComponentManager & mgr);  public: @@ -19,10 +25,9 @@ public:  	 * \brief Add a new concrete scene to the scene manager  	 *  	 * \tparam T  Type of concrete scene -	 * \param name  Name of new scene  	 */ -	template <typename T> -	void add_scene(const std::string & name); +	template <typename T, typename... Args> +	void add_scene(Args &&... args);  	/**  	 * \brief Set the next scene  	 * @@ -35,8 +40,11 @@ public:  	void load_next_scene();  private: +	//! Vector of concrete scenes (added by add_scene())  	std::vector<std::unique_ptr<Scene>> scenes; +	//! Next scene to load  	std::string next_scene; +	//! Reference to the ComponentManager  	ComponentManager & component_manager;  }; diff --git a/src/crepe/api/SceneManager.hpp b/src/crepe/api/SceneManager.hpp index 714f690..5c8e417 100644 --- a/src/crepe/api/SceneManager.hpp +++ b/src/crepe/api/SceneManager.hpp @@ -4,17 +4,21 @@  namespace crepe { -template <typename T> -void SceneManager::add_scene(const std::string & name) { +template <typename T, typename... Args> +void SceneManager::add_scene(Args &&... args) {  	using namespace std;  	static_assert(is_base_of<Scene, T>::value, "T must be derived from Scene"); -	Scene * scene = new T(this->component_manager, name); -	this->scenes.emplace_back(unique_ptr<Scene>(scene)); +	Scene * scene = new T(std::forward<Args>(args)...); +	unique_ptr<Scene> unique_scene(scene); + +	unique_scene->component_manager = this->component_manager; + +	this->scenes.emplace_back(std::move(unique_scene));  	// The first scene added, is the one that will be loaded at the beginning  	if (next_scene.empty()) { -		next_scene = name; +		next_scene = scene->get_name();  	}  } diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp new file mode 100644 index 0000000..fcbe4c7 --- /dev/null +++ b/src/crepe/api/Script.cpp @@ -0,0 +1,15 @@ +#include "Script.h" + +using namespace crepe; + +Script::~Script() { +	EventManager & evmgr = this->event_manager; +	for (auto id : this->listeners) { +		evmgr.unsubscribe(id); +	} +} + +template <> +void Script::subscribe(const EventHandler<CollisionEvent> & callback) { +	this->subscribe_internal(callback, this->game_object_id); +} diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 2b70379..a0870cb 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -3,6 +3,9 @@  #include <vector>  #include "../types.h" +#include "../util/OptionalRef.h" + +#include "EventManager.h"  namespace crepe { @@ -16,13 +19,23 @@ class ComponentManager;   * This class is used as a base class for user-defined scripts that can be added to game   * objects using the \c BehaviorScript component.   * - * \note Additional *events* (like Unity's OnDisable and OnEnable) should be implemented as + * \info Additional *events* (like Unity's OnDisable and OnEnable) should be implemented as   * member or lambda methods in derivative user script classes and registered in \c init(). + * + * \warning Concrete scripts are allowed do create a custom constructor, but the utility + * functions should not be called inside the constructor as they rely on late references that + * are only available after the constructor returns. + * + * \see feature_script   */  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 +43,31 @@ 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 with type \c T  	 */  	template <typename T>  	T & get_component() const; @@ -55,34 +75,117 @@ 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  	 *  	 * \returns List of component references  	 */  	template <typename T> -	std::vector<std::reference_wrapper<T>> get_components() const; +	RefVector<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); + +	/** +	 * \brief Subscribe to an event with an explicit channel +	 * \see EventManager::subscribe +	 */ +	template <typename EventType> +	void subscribe(const EventHandler<EventType> & callback, event_channel_t channel); +	/** +	 * \brief Subscribe to an event on EventManager::CHANNEL_ALL +	 * \see EventManager::subscribe +	 */ +	template <typename EventType> +	void subscribe(const EventHandler<EventType> & callback); + +	//! \} + +private: +	/** +	 * \brief Internal subscribe function +	 * +	 * This function exists so certain template specializations of Script::subscribe can be +	 * explicitly deleted, and does the following: +	 * - Wrap the user-provided callback in a check that tests if the parent BehaviorScript +	 *   component is still active +	 * - Store the subscriber handle returned by the event manager so this listener is +	 *   automatically unsubscribed at the end of this Script instance's life +	 * +	 * \tparam EventType concrete Event class +	 * \param callback User-provided callback function +	 * \param channel Event channel (may have been overridden by template specializations) +	 */ +	template <typename EventType> +	void subscribe_internal(const EventHandler<EventType> & callback, event_channel_t channel);  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. +	// NOTE: This must be the only constructor on Script, see "Late references" below  	Script() = default;  	//! Only \c BehaviorScript instantiates Script  	friend class BehaviorScript; +public: +	// std::unique_ptr destroys script +	virtual ~Script(); + +private: +	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. -	game_object_id_t game_object_id = -1; -	ComponentManager * component_manager_ref = nullptr; -	// TODO: use OptionalRef instead of pointer +	/** +	 * \name Late references +	 * +	 * These references are set by BehaviorScript immediately after calling the constructor of +	 * Script. +	 * +	 * \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 if they want to +	 * implement a non-default constructor (e.g. for passing references to their own concrete +	 * Script classes). +	 * +	 * \{ +	 */ +	//! Game object ID of game object parent BehaviorScript is attached to +	game_object_id_t game_object_id; +	//! Reference to parent component +	OptionalRef<bool> active; +	//! Reference to component manager instance +	OptionalRef<ComponentManager> component_manager; +	//! Reference to event manager instance +	OptionalRef<EventManager> event_manager; +	//! \}  private:  	//! Flag to indicate if \c init() has been called already  	bool initialized = false; +	//! List of subscribed events +	std::vector<subscription_t> listeners;  }; +/** + * \brief Subscribe to CollisionEvent for the current GameObject + * + * This is a template specialization for Script::subscribe which automatically sets the event + * channel so the callback handler is only called for CollisionEvent events that apply to the + * current GameObject the parent BehaviorScript is attached to. + */ +template <> +void Script::subscribe(const EventHandler<CollisionEvent> & callback); +template <> +void Script::subscribe(const EventHandler<CollisionEvent> & callback, event_channel_t) +	= delete; +  } // namespace crepe  #include "Script.hpp" diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index a064a90..a2463bf 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -10,7 +10,7 @@ namespace crepe {  template <typename T>  T & Script::get_component() const {  	using namespace std; -	vector<reference_wrapper<T>> all_components = this->get_components<T>(); +	RefVector<T> all_components = this->get_components<T>();  	if (all_components.size() < 1)  		throw runtime_error(  			format("Script: no component found with type = {}", typeid(T).name())); @@ -19,10 +19,39 @@ T & Script::get_component() const {  }  template <typename T> -std::vector<std::reference_wrapper<T>> Script::get_components() const { -	auto & mgr = *this->component_manager_ref; +RefVector<T> Script::get_components() const { +	ComponentManager & mgr = this->component_manager;  	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_internal(const EventHandler<EventType> & callback, +								event_channel_t channel) { +	EventManager & mgr = this->event_manager; +	subscription_t listener = mgr.subscribe<EventType>( +		[this, callback](const EventType & data) -> bool { +			bool & active = this->active; +			if (!active) return false; +			return callback(data); +		}, +		channel); +	this->listeners.push_back(listener); +} + +template <typename EventType> +void Script::subscribe(const EventHandler<EventType> & callback, event_channel_t channel) { +	this->subscribe_internal(callback, channel); +} + +template <typename EventType> +void Script::subscribe(const EventHandler<EventType> & callback) { +	this->subscribe_internal(callback, EventManager::CHANNEL_ALL); +} +  } // namespace crepe diff --git a/src/crepe/api/Sprite.h b/src/crepe/api/Sprite.h index 0192793..74a55d4 100644 --- a/src/crepe/api/Sprite.h +++ b/src/crepe/api/Sprite.h @@ -2,8 +2,9 @@  #include <memory> +#include "../Component.h" +  #include "Color.h" -#include "Component.h"  #include "Texture.h"  namespace crepe { diff --git a/src/crepe/api/Texture.cpp b/src/crepe/api/Texture.cpp index de0d0ea..264d7b1 100644 --- a/src/crepe/api/Texture.cpp +++ b/src/crepe/api/Texture.cpp @@ -9,14 +9,9 @@  using namespace crepe;  using namespace std; -Texture::Texture(unique_ptr<Asset> res) { +Texture::Texture(const Asset & src) {  	dbg_trace(); -	this->load(std::move(res)); -} - -Texture::Texture(const char * src) { -	dbg_trace(); -	this->load(make_unique<Asset>(src)); +	this->load(src);  }  Texture::~Texture() { @@ -24,9 +19,9 @@ Texture::~Texture() {  	this->texture.reset();  } -void Texture::load(unique_ptr<Asset> res) { +void Texture::load(const Asset & res) {  	SDLContext & ctx = SDLContext::get_instance(); -	this->texture = std::move(ctx.texture_from_path(res->get_canonical())); +	this->texture = ctx.texture_from_path(res.get_path());  }  int Texture::get_width() const { @@ -35,5 +30,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/api/Texture.h b/src/crepe/api/Texture.h index 6965223..b4f7d07 100644 --- a/src/crepe/api/Texture.h +++ b/src/crepe/api/Texture.h @@ -25,16 +25,10 @@ class Texture {  public:  	/** -	 * \brief Constructs a Texture from a file path. -	 * \param src Path to the image file to be loaded as a texture. -	 */ -	Texture(const char * src); - -	/**  	 * \brief Constructs a Texture from an Asset resource. -	 * \param res Unique pointer to an Asset resource containing texture data. +	 * \param src Asset with texture data to load.  	 */ -	Texture(std::unique_ptr<Asset> res); +	Texture(const Asset & src);  	/**  	 * \brief Destroys the Texture instance, freeing associated resources. @@ -59,7 +53,7 @@ private:  	 * \brief Loads the texture from an Asset resource.  	 * \param res Unique pointer to an Asset resource to load the texture from.  	 */ -	void load(std::unique_ptr<Asset> res); +	void load(const Asset & res);  private:  	//! The texture of the class from the library diff --git a/src/crepe/api/Transform.cpp b/src/crepe/api/Transform.cpp index cd944bd..a85b792 100644 --- a/src/crepe/api/Transform.cpp +++ b/src/crepe/api/Transform.cpp @@ -4,7 +4,7 @@  using namespace crepe; -Transform::Transform(game_object_id_t id, const Vector2 & point, double rotation, double scale) +Transform::Transform(game_object_id_t id, const vec2 & point, double rotation, double scale)  	: Component(id),  	  position(point),  	  rotation(rotation), diff --git a/src/crepe/api/Transform.h b/src/crepe/api/Transform.h index 18aa293..6243a93 100644 --- a/src/crepe/api/Transform.h +++ b/src/crepe/api/Transform.h @@ -1,8 +1,7 @@  #pragma once -#include "api/Vector2.h" -  #include "Component.h" +#include "types.h"  namespace crepe { @@ -15,7 +14,7 @@ namespace crepe {  class Transform : public Component {  public:  	//! Translation (shift) -	Vector2 position = {0, 0}; +	vec2 position = {0, 0};  	//! Rotation, in degrees  	double rotation = 0;  	//! Multiplication factor @@ -28,7 +27,7 @@ protected:  	 * \param rotation The rotation of the GameObject  	 * \param scale The scale of the GameObject  	 */ -	Transform(game_object_id_t id, const Vector2 & point, double rotation, double scale); +	Transform(game_object_id_t id, const vec2 & point, double rotation, double scale);  	/**  	 * There is always exactly one transform component per entity  	 * \return 1 diff --git a/src/crepe/api/Vector2.cpp b/src/crepe/api/Vector2.cpp deleted file mode 100644 index 30b968e..0000000 --- a/src/crepe/api/Vector2.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "Vector2.h" - -using namespace crepe; - -Vector2 Vector2::operator-(const Vector2 & other) const { return {x - other.x, y - other.y}; } - -Vector2 Vector2::operator+(const Vector2 & other) const { return {x + other.x, y + other.y}; } - -Vector2 Vector2::operator*(double scalar) const { return {x * scalar, y * scalar}; } - -Vector2 & Vector2::operator*=(const Vector2 & other) { -	x *= other.x; -	y *= other.y; -	return *this; -} - -Vector2 & Vector2::operator+=(const Vector2 & other) { -	x += other.x; -	y += other.y; -	return *this; -} - -Vector2 & Vector2::operator+=(double other) { -	x += other; -	y += other; -	return *this; -} - -Vector2 Vector2::operator-() const { return {-x, -y}; } - -bool Vector2::operator==(const Vector2 & other) const { return x == other.x && y == other.y; } - -bool Vector2::operator!=(const Vector2 & other) const { return !(*this == other); } diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h index 2fb6136..c278c87 100644 --- a/src/crepe/api/Vector2.h +++ b/src/crepe/api/Vector2.h @@ -3,38 +3,71 @@  namespace crepe {  //! 2D vector +template <class T>  struct Vector2 {  	//! X component of the vector -	double x = 0; +	T x = 0;  	//! Y component of the vector -	double y = 0; +	T y = 0;  	//! Subtracts another vector from this vector and returns the result. -	Vector2 operator-(const Vector2 & other) const; +	Vector2 operator-(const Vector2<T> & other) const; + +	//! Subtracts a scalar value from both components of this vector and returns the result. +	Vector2 operator-(T scalar) const;  	//! Adds another vector to this vector and returns the result. -	Vector2 operator+(const Vector2 & other) const; +	Vector2 operator+(const Vector2<T> & other) const; + +	//! Adds a scalar value to both components of this vector and returns the result. +	Vector2 operator+(T scalar) const; + +	//! Multiplies this vector by another vector element-wise and returns the result. +	Vector2 operator*(const Vector2<T> & other) const;  	//! Multiplies this vector by a scalar and returns the result. -	Vector2 operator*(double scalar) const; +	Vector2 operator*(T scalar) const; -	//! Multiplies this vector by another vector element-wise and updates this vector. -	Vector2 & operator*=(const Vector2 & other); +	//! Divides this vector by another vector element-wise and returns the result. +	Vector2 operator/(const Vector2<T> & other) const; + +	//! Divides this vector by a scalar and returns the result. +	Vector2 operator/(T scalar) const;  	//! Adds another vector to this vector and updates this vector. -	Vector2 & operator+=(const Vector2 & other); +	Vector2 & operator+=(const Vector2<T> & other);  	//! Adds a scalar value to both components of this vector and updates this vector. -	Vector2 & operator+=(double other); +	Vector2 & operator+=(T other); + +	//! Subtracts another vector from this vector and updates this vector. +	Vector2 & operator-=(const Vector2<T> & other); + +	//! Subtracts a scalar value from both components of this vector and updates this vector. +	Vector2 & operator-=(T other); + +	//! Multiplies this vector by another vector element-wise and updates this vector. +	Vector2 & operator*=(const Vector2<T> & other); + +	//! Multiplies this vector by a scalar and updates this vector. +	Vector2 & operator*=(T other); + +	//! Divides this vector by another vector element-wise and updates this vector. +	Vector2 & operator/=(const Vector2<T> & other); + +	//! Divides this vector by a scalar and updates this vector. +	Vector2 & operator/=(T other);  	//! Returns the negation of this vector.  	Vector2 operator-() const;  	//! Checks if this vector is equal to another vector. -	bool operator==(const Vector2 & other) const; +	bool operator==(const Vector2<T> & other) const;  	//! Checks if this vector is not equal to another vector. -	bool operator!=(const Vector2 & other) const; +	bool operator!=(const Vector2<T> & other) const;  };  } // namespace crepe + +#include "Vector2.hpp" diff --git a/src/crepe/api/Vector2.hpp b/src/crepe/api/Vector2.hpp new file mode 100644 index 0000000..cad15f8 --- /dev/null +++ b/src/crepe/api/Vector2.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include "Vector2.h" + +namespace crepe { + +template <class T> +Vector2<T> Vector2<T>::operator-(const Vector2<T> & other) const { +	return {x - other.x, y - other.y}; +} + +template <class T> +Vector2<T> Vector2<T>::operator-(T scalar) const { +	return {x - scalar, y - scalar}; +} + +template <class T> +Vector2<T> Vector2<T>::operator+(const Vector2<T> & other) const { +	return {x + other.x, y + other.y}; +} + +template <class T> +Vector2<T> Vector2<T>::operator+(T scalar) const { +	return {x + scalar, y + scalar}; +} + +template <class T> +Vector2<T> Vector2<T>::operator*(const Vector2<T> & other) const { +	return {x * other.x, y * other.y}; +} + +template <class T> +Vector2<T> Vector2<T>::operator*(T scalar) const { +	return {x * scalar, y * scalar}; +} + +template <class T> +Vector2<T> Vector2<T>::operator/(const Vector2<T> & other) const { +	return {x / other.x, y / other.y}; +} + +template <class T> +Vector2<T> Vector2<T>::operator/(T scalar) const { +	return {x / scalar, y / scalar}; +} + +template <class T> +Vector2<T> & Vector2<T>::operator+=(const Vector2<T> & other) { +	x += other.x; +	y += other.y; +	return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator+=(T other) { +	x += other; +	y += other; +	return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator-=(const Vector2<T> & other) { +	x -= other.x; +	y -= other.y; +	return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator-=(T other) { +	x -= other; +	y -= other; +	return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator*=(const Vector2<T> & other) { +	x *= other.x; +	y *= other.y; +	return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator*=(T other) { +	x *= other; +	y *= other; +	return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator/=(const Vector2<T> & other) { +	x /= other.x; +	y /= other.y; +	return *this; +} + +template <class T> +Vector2<T> & Vector2<T>::operator/=(T other) { +	x /= other; +	y /= other; +	return *this; +} + +template <class T> +Vector2<T> Vector2<T>::operator-() const { +	return {-x, -y}; +} + +template <class T> +bool Vector2<T>::operator==(const Vector2<T> & other) const { +	return x == other.x && y == other.y; +} + +template <class T> +bool Vector2<T>::operator!=(const Vector2<T> & other) const { +	return !(*this == other); +} + +} // namespace crepe |