aboutsummaryrefslogtreecommitdiff
path: root/src/crepe/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/crepe/api')
-rw-r--r--src/crepe/api/BehaviorScript.h7
-rw-r--r--src/crepe/api/BehaviorScript.hpp12
-rw-r--r--src/crepe/api/CMakeLists.txt1
-rw-r--r--src/crepe/api/Event.h12
-rw-r--r--src/crepe/api/EventManager.h4
-rw-r--r--src/crepe/api/Script.cpp17
-rw-r--r--src/crepe/api/Script.h123
-rw-r--r--src/crepe/api/Script.hpp30
8 files changed, 178 insertions, 28 deletions
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..5b5a418 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();
- s->game_object_id = this->game_object_id;
+ Script * s = new T(std::forward<Args>(args)...);
+
+ s->game_object_id_ref = &this->game_object_id;
+ s->active_ref = &this->active;
s->component_manager_ref = &this->component_manager;
+ s->event_manager_ref = &EventManager::get_instance();
+
this->script = std::unique_ptr<Script>(s);
return *this;
}
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index a185ca6..a63c5ff 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -23,6 +23,7 @@ target_sources(crepe PUBLIC
LoopManager.cpp
LoopTimer.cpp
EventHandler.cpp
+ Script.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
diff --git a/src/crepe/api/Event.h b/src/crepe/api/Event.h
index 06cf7f3..ab4be2b 100644
--- a/src/crepe/api/Event.h
+++ b/src/crepe/api/Event.h
@@ -1,10 +1,12 @@
-// TODO discussing the location of these events
#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.
*/
@@ -91,11 +93,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.
@@ -110,3 +108,5 @@ public:
* \brief Event triggered to indicate the application is shutting down.
*/
class ShutDownEvent : public Event {};
+
+}
diff --git a/src/crepe/api/EventManager.h b/src/crepe/api/EventManager.h
index 348a04d..1a33023 100644
--- a/src/crepe/api/EventManager.h
+++ b/src/crepe/api/EventManager.h
@@ -77,7 +77,7 @@ public:
* \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);
+ void trigger_event(const EventType & event = {}, event_channel_t channel = CHANNEL_ALL);
/**
* \brief Queue an event for later processing.
@@ -89,7 +89,7 @@ public:
* \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);
+ void queue_event(const EventType & event = {}, event_channel_t channel = CHANNEL_ALL);
/**
* \brief Process all queued events.
diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp
new file mode 100644
index 0000000..d3fae07
--- /dev/null
+++ b/src/crepe/api/Script.cpp
@@ -0,0 +1,17 @@
+#include "Script.h"
+
+using namespace crepe;
+
+Script::~Script() {
+ EventManager & evmgr = *this->event_manager_ref;
+ for (auto id : this->listeners) {
+ evmgr.unsubscribe(id);
+ }
+}
+
+template <>
+void Script::subscribe(const EventHandler<CollisionEvent> & callback) {
+ const game_object_id_t & game_object_id = *this->game_object_id_ref;
+ this->subscribe_internal(callback, game_object_id);
+}
+
diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h
index 2b70379..373b253 100644
--- a/src/crepe/api/Script.h
+++ b/src/crepe/api/Script.h
@@ -4,6 +4,8 @@
#include "../types.h"
+#include "EventManager.h"
+
namespace crepe {
class ScriptSystem;
@@ -16,13 +18,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 +38,32 @@ protected:
*/
virtual void init() {}
/**
- * \brief Script update function
+ * \brief Script update function (empty by default)
*
* This function is called during the ScriptSystem::update() routine if the \c BehaviorScript
* component holding this script instance is active.
*/
virtual void update() {}
+ //! \}
+
//! ScriptSystem calls \c init() and \c update()
friend class crepe::ScriptSystem;
protected:
/**
- * \brief Get single component of type \c T on this game object (utility)
+ * \name Utility functions
+ * \{
+ */
+
+ /**
+ * \brief Get single component of type \c T on this game object
*
* \tparam T Type of component
*
* \returns Reference to component
*
- * \throws nullptr if this game object does not have a component matching type \c T
+ * \throws std::runtime_error if this game object does not have a component matching type \c
+ * T
*/
template <typename T>
T & get_component() const;
@@ -55,7 +71,7 @@ protected:
// cause compile-time errors
/**
- * \brief Get all components of type \c T on this game object (utility)
+ * \brief Get all components of type \c T on this game object
*
* \tparam T Type of component
*
@@ -64,25 +80,108 @@ protected:
template <typename T>
std::vector<std::reference_wrapper<T>> get_components() const;
+ /**
+ * \brief Log a message using Log::logf
+ *
+ * \tparam Args Log::logf parameters
+ * \param args Log::logf parameters
+ */
+ template <typename... Args>
+ void logf(Args &&... args);
+
+ /**
+ * \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;
+ /**
+ * \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).
+ *
+ * \todo These should be converted to OptionalRef<> once `loek/util` is merged
+ *
+ * \{
+ */
+ //! Game object ID of game object parent BehaviorScript is attached to
+ const game_object_id_t * game_object_id_ref = nullptr;
+ //! Reference to parent component
+ bool * active_ref = nullptr;
+ //! Reference to component manager instance
ComponentManager * component_manager_ref = nullptr;
- // TODO: use OptionalRef instead of pointer
+ //! Reference to event manager instance
+ EventManager * event_manager_ref = nullptr;
+ //! \}
private:
//! Flag to indicate if \c init() has been called already
bool initialized = false;
+ //! List of subscribed events
+ std::vector<subscription_t> listeners;
};
+/**
+ * \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..8186bd4 100644
--- a/src/crepe/api/Script.hpp
+++ b/src/crepe/api/Script.hpp
@@ -20,9 +20,35 @@ T & Script::get_component() const {
template <typename T>
std::vector<std::reference_wrapper<T>> Script::get_components() const {
- auto & mgr = *this->component_manager_ref;
+ ComponentManager & mgr = *this->component_manager_ref;
- return mgr.get_components_by_id<T>(this->game_object_id);
+ return mgr.get_components_by_id<T>(*this->game_object_id_ref);
+}
+
+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_ref;
+ subscription_t listener = mgr.subscribe<EventType>([this, callback](const EventType & data) -> bool {
+ bool & active = *this->active_ref;
+ 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