diff options
author | Loek Le Blansch <loek@pipeframe.xyz> | 2024-11-22 15:10:49 +0100 |
---|---|---|
committer | Loek Le Blansch <loek@pipeframe.xyz> | 2024-11-22 15:10:49 +0100 |
commit | d038f192c7dcb453c9fc19082cd1b642c8f70fc8 (patch) | |
tree | bcb539657cd8b35ed742f19e5673c777ab39610c /src/crepe/api | |
parent | c3c3476f1d82aa83d8f8dc706488475dc2cf1e55 (diff) | |
parent | 4117d1d287f1d87efd0577d56819520e981a7f1c (diff) |
merge with `master`loek/audio
Diffstat (limited to 'src/crepe/api')
41 files changed, 1276 insertions, 184 deletions
diff --git a/src/crepe/api/Asset.cpp b/src/crepe/api/Asset.cpp index 5271cf7..e148367 100644 --- a/src/crepe/api/Asset.cpp +++ b/src/crepe/api/Asset.cpp @@ -2,34 +2,33 @@ #include <stdexcept> #include <whereami.h> -#include "Asset.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)) { } +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(); - auto & root_pattern = cfg.asset.root_pattern; + 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); + 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 (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(); @@ -48,11 +47,8 @@ string Asset::whereami() const noexcept { return path; } -bool Asset::operator==(const Asset & other) const noexcept { - return this->src == other.src; -} +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 index 685dd3a..bfd0ac7 100644 --- a/src/crepe/api/Asset.h +++ b/src/crepe/api/Asset.h @@ -33,13 +33,29 @@ public: * \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; + 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 @@ -52,7 +68,8 @@ private: namespace std { //! Hash helper struct -template<> struct hash<const crepe::Asset> { +template <> +struct hash<const crepe::Asset> { /** * \brief Hash operator for crepe::Asset * @@ -64,5 +81,4 @@ template<> struct hash<const crepe::Asset> { 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 cca0e8b..7a3ab48 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -14,12 +14,16 @@ target_sources(crepe PUBLIC Metadata.cpp Scene.cpp SceneManager.cpp - Vector2.cpp Camera.cpp Animator.cpp + EventManager.cpp + IKeyListener.cpp + IMouseListener.cpp LoopManager.cpp LoopTimer.cpp Asset.cpp + EventHandler.cpp + Script.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -33,6 +37,7 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Rigidbody.h Sprite.h Vector2.h + Vector2.hpp Color.h Texture.h ResourceManager.h @@ -43,6 +48,13 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES SceneManager.hpp Camera.h Animator.h + EventManager.h + EventManager.hpp + EventHandler.h + EventHandler.hpp + Event.h + IKeyListener.h + 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/Event.h b/src/crepe/api/Event.h new file mode 100644 index 0000000..b267e3e --- /dev/null +++ b/src/crepe/api/Event.h @@ -0,0 +1,112 @@ +#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 {}; + +/** + * \brief Event triggered when a key is pressed. + */ +class KeyPressEvent : public Event { +public: + //! 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 {}; + +/** + * \brief Event triggered when text is submitted, e.g., from a text input. + */ +class TextSubmitEvent : public Event { +public: + //! The submitted text. + std::string text = ""; +}; + +/** + * \brief Event triggered to indicate the application is shutting down. + */ +class ShutDownEvent : public Event {}; + +} // namespace crepe diff --git a/src/crepe/api/EventHandler.cpp b/src/crepe/api/EventHandler.cpp new file mode 100644 index 0000000..4dc232f --- /dev/null +++ b/src/crepe/api/EventHandler.cpp @@ -0,0 +1,5 @@ +#include "EventHandler.h" + +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 new file mode 100644 index 0000000..ef659fd --- /dev/null +++ b/src/crepe/api/EventHandler.h @@ -0,0 +1,96 @@ +#pragma once + +#include <functional> +#include <string> + +#include "Event.h" + +namespace crepe { +/** + * \brief A type alias for an event handler function. + * + * The EventHandler is a std::function that takes an EventType reference and returns a boolean value + * indicating whether the event is handled. + * + * \tparam EventType The type of event this handler will handle. + * + * 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)>; + +/** + * \class IEventHandlerWrapper + * \brief An abstract base class for event handler wrappers. + * + * This class provides the interface for handling events. Derived classes must implement the + * `call()` method to process events + */ +class IEventHandlerWrapper { +public: + /** + * \brief Virtual destructor for IEventHandlerWrapper. + */ + virtual ~IEventHandlerWrapper() = default; + + /** + * \brief Executes the handler with the given event. + * + * This method calls the `call()` method of the derived class, passing the event to the handler. + * + * \param e The event to be processed. + * \return A boolean value indicating whether the event is handled. + */ + bool exec(const Event & e); + +private: + /** + * \brief The method responsible for handling the event. + * + * This method is implemented by derived classes to process the event. + * + * \param e The event to be processed. + * \return A boolean value indicating whether the event is handled. + */ + virtual bool call(const Event & e) = 0; +}; + +/** + * \class EventHandlerWrapper + * \brief A wrapper for event handler functions. + * + * This class wraps an event handler function of a specific event type. It implements the + * `call()` and `get_type()` methods to allow the handler to be executed and its type to be + * queried. + * + * \tparam EventType The type of event this handler will handle. + */ +template <typename EventType> +class EventHandlerWrapper : public IEventHandlerWrapper { +public: + /** + * \brief Constructs an EventHandlerWrapper with a given handler. + * + * The constructor takes an event handler function and stores it in the wrapper. + * + * \param handler The event handler function. + */ + explicit EventHandlerWrapper(const EventHandler<EventType> & handler); + +private: + /** + * \brief Calls the stored event handler with the event. + * + * This method casts the event to the appropriate type and calls the handler. + * + * \param e The event to be handled. + * \return A boolean value indicating whether the event is handled. + */ + bool call(const Event & e) override; + //! The event handler function. + 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..050e57e --- /dev/null +++ b/src/crepe/api/EventHandler.hpp @@ -0,0 +1,20 @@ +#pragma once + +#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 new file mode 100644 index 0000000..20f0dd3 --- /dev/null +++ b/src/crepe/api/EventManager.cpp @@ -0,0 +1,46 @@ +#include "EventManager.h" + +using namespace crepe; +using namespace std; + +EventManager & EventManager::get_instance() { + static EventManager instance; + return instance; +} + +void EventManager::dispatch_events() { + 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(); +} + +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 new file mode 100644 index 0000000..1a33023 --- /dev/null +++ b/src/crepe/api/EventManager.h @@ -0,0 +1,161 @@ +#pragma once + +#include <memory> +#include <typeindex> +#include <unordered_map> +#include <vector> + +#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 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 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 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> + subscription_t subscribe(const EventHandler<EventType> & callback, + event_channel_t channel = CHANNEL_ALL); + + /** + * \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 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 = {}, event_channel_t channel = CHANNEL_ALL); + + /** + * \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(const EventType & event = {}, event_channel_t channel = CHANNEL_ALL); + + /** + * \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. + * + * Constructor is private to enforce the singleton pattern. + */ + EventManager() = default; + + /** + * \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 new file mode 100644 index 0000000..a5f4556 --- /dev/null +++ b/src/crepe/api/EventManager.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "EventManager.h" + +namespace crepe { + +template <typename EventType> +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); + 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(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, 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 new file mode 100644 index 0000000..8642655 --- /dev/null +++ b/src/crepe/api/IKeyListener.cpp @@ -0,0 +1,19 @@ +#include "IKeyListener.h" + +using namespace crepe; + +// 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); +} + +// 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 new file mode 100644 index 0000000..328a4c2 --- /dev/null +++ b/src/crepe/api/IKeyListener.h @@ -0,0 +1,49 @@ +#pragma once + +#include "Event.h" +#include "EventHandler.h" +#include "EventManager.h" + +namespace crepe { + +/** + * \class IKeyListener + * \brief Interface for keyboard event handling in the application. + */ +class IKeyListener { +public: + /** + * \brief Constructs an IKeyListener with a specified channel. + * \param channel The channel ID for event handling. + */ + IKeyListener(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. + * \param event The key press event to handle. + * \return True if the event was handled, false otherwise. + */ + virtual bool on_key_pressed(const KeyPressEvent & event) = 0; + + /** + * \brief Pure virtual function to handle key release events. + * \param event The key release event to handle. + * \return True if the event was handled, false otherwise. + */ + virtual bool on_key_released(const KeyReleaseEvent & event) = 0; + +private: + //! 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 new file mode 100644 index 0000000..989aeb3 --- /dev/null +++ b/src/crepe/api/IMouseListener.cpp @@ -0,0 +1,29 @@ +#include "IMouseListener.h" + +using namespace crepe; + +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() { + // 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 new file mode 100644 index 0000000..15e1619 --- /dev/null +++ b/src/crepe/api/IMouseListener.h @@ -0,0 +1,72 @@ +#pragma once + +#include "Event.h" +#include "EventHandler.h" +#include "EventManager.h" + +namespace crepe { + +/** + * \class IMouseListener + * \brief Interface for mouse event handling in the application. + */ +class IMouseListener { +public: + /** + * \brief Constructs an IMouseListener with a specified channel. + * \param channel The channel ID for event handling. + */ + IMouseListener(event_channel_t channel = EventManager::CHANNEL_ALL); + virtual ~IMouseListener(); + IMouseListener & operator=(const IMouseListener &) = delete; + IMouseListener(const IMouseListener &) = delete; + IMouseListener & operator=(const IMouseListener &&) = delete; + IMouseListener(IMouseListener &&) = delete; + + /** + * \brief Move assignment operator (deleted). + */ + IMouseListener & operator=(IMouseListener &&) = delete; + + /** + * \brief Handles a mouse click event. + * \param event The mouse click event to handle. + * \return True if the event was handled, false otherwise. + */ + virtual bool on_mouse_clicked(const MouseClickEvent & event) = 0; + + /** + * \brief Handles a mouse press event. + * \param event The mouse press event to handle. + * \return True if the event was handled, false otherwise. + */ + virtual bool on_mouse_pressed(const MousePressEvent & event) = 0; + + /** + * \brief Handles a mouse release event. + * \param event The mouse release event to handle. + * \return True if the event was handled, false otherwise. + */ + virtual bool on_mouse_released(const MouseReleaseEvent & event) = 0; + + /** + * \brief Handles a mouse move event. + * \param event The mouse move event to handle. + * \return True if the event was handled, false otherwise. + */ + virtual bool on_mouse_moved(const MouseMoveEvent & event) = 0; + +private: + //! 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 new file mode 100644 index 0000000..9e173e0 --- /dev/null +++ b/src/crepe/api/KeyCodes.h @@ -0,0 +1,153 @@ +#pragma once + +//! Enumeration for mouse button inputs, including standard and extended buttons. +enum class MouseButton { + 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. +}; + +//! 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, + F4 = 293, + F5 = 294, + F6 = 295, + F7 = 296, + F8 = 297, + F9 = 298, + F10 = 299, + F11 = 300, + F12 = 301, + F13 = 302, + F14 = 303, + F15 = 304, + F16 = 305, + F17 = 306, + F18 = 307, + F19 = 308, + F20 = 309, + F21 = 310, + F22 = 311, + F23 = 312, + F24 = 313, + F25 = 314, + /// \} + /** + * \name Keypad digits and operators. + * \{ + */ + KP0 = 320, + KP1 = 321, + KP2 = 322, + KP3 = 323, + KP4 = 324, + KP5 = 325, + KP6 = 326, + KP7 = 327, + KP8 = 328, + KP9 = 329, + KP_DECIMAL = 330, + KP_DIVIDE = 331, + KP_MULTIPLY = 332, + KP_SUBTRACT = 333, + KP_ADD = 334, + KP_ENTER = 335, + KP_EQUAL = 336, + /// \} + /** + * \name Modifier keys. + * \{ + */ + LEFT_SHIFT = 340, + LEFT_CONTROL = 341, + LEFT_ALT = 342, + LEFT_SUPER = 343, + RIGHT_SHIFT = 344, + RIGHT_CONTROL = 345, + RIGHT_ALT = 346, + RIGHT_SUPER = 347, + /// \} + MENU = 348, //!< Menu key. +}; diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index f6904be..13e6dac 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -4,14 +4,28 @@ #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 Add a new concrete scene to the scene manager + * + * \tparam T Type of concrete scene + */ + template <typename T> + void add_scene(); + private: /** * \brief Setup function for one-time initialization. @@ -53,12 +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. * @@ -71,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/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 index 88aa82d..849945e 100644 --- a/src/crepe/api/Scene.cpp +++ b/src/crepe/api/Scene.cpp @@ -2,6 +2,4 @@ using namespace crepe; -Scene::Scene(ComponentManager & mgr, const std::string & name) - : component_manager(mgr), - name(name) {} +Scene::Scene(ComponentManager & mgr) : component_manager(mgr) {} diff --git a/src/crepe/api/Scene.h b/src/crepe/api/Scene.h index 0e516b6..869bf6f 100644 --- a/src/crepe/api/Scene.h +++ b/src/crepe/api/Scene.h @@ -7,19 +7,36 @@ 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); + //TODO: Use Loek's custom reference class to set ComponentManger via SceneManager instead of via constructor + /** + * \param mgr Reference to the ComponentManager + */ + Scene(ComponentManager & mgr); + //! 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: + //! Reference to the ComponentManager ComponentManager & component_manager; }; 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..45ba668 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); + void add_scene(); /** * \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..94e5946 100644 --- a/src/crepe/api/SceneManager.hpp +++ b/src/crepe/api/SceneManager.hpp @@ -5,16 +5,16 @@ namespace crepe { template <typename T> -void SceneManager::add_scene(const std::string & name) { +void SceneManager::add_scene() { 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); + Scene * scene = new T(this->component_manager); this->scenes.emplace_back(unique_ptr<Scene>(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..f0b9c73 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,19 @@ 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(). + * + * \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 +39,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,34 +72,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 6a1e4d8..9be9421 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/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 |