aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/crepe/api/BehaviorScript.hpp5
-rw-r--r--src/crepe/api/Event.h6
-rw-r--r--src/crepe/api/EventManager.h4
-rw-r--r--src/crepe/api/Script.cpp4
-rw-r--r--src/crepe/api/Script.h47
-rw-r--r--src/crepe/api/Script.hpp8
-rw-r--r--src/doc/layout.xml2
-rw-r--r--src/test/ScriptTest.cpp60
8 files changed, 120 insertions, 16 deletions
diff --git a/src/crepe/api/BehaviorScript.hpp b/src/crepe/api/BehaviorScript.hpp
index dd197b3..5b5a418 100644
--- a/src/crepe/api/BehaviorScript.hpp
+++ b/src/crepe/api/BehaviorScript.hpp
@@ -14,9 +14,12 @@ BehaviorScript & BehaviorScript::set_script(Args &&... args) {
dbg_trace();
static_assert(std::is_base_of<Script, T>::value);
Script * s = new T(std::forward<Args>(args)...);
- s->game_object_id = this->game_object_id;
+
+ 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/Event.h b/src/crepe/api/Event.h
index 9923a05..c1432e1 100644
--- a/src/crepe/api/Event.h
+++ b/src/crepe/api/Event.h
@@ -1,5 +1,5 @@
-// TODO discussing the location of these events
#pragma once
+// TODO discussing the location of these events
#include <string>
@@ -7,6 +7,8 @@
#include "KeyCodes.h"
+namespace crepe {
+
/**
* \brief Base class for all event types in the system.
*/
@@ -113,3 +115,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
index bac5ab2..d3fae07 100644
--- a/src/crepe/api/Script.cpp
+++ b/src/crepe/api/Script.cpp
@@ -11,5 +11,7 @@ Script::~Script() {
template <>
void Script::subscribe(const EventHandler<CollisionEvent> & callback) {
- this->subscribe_internal(callback, this->game_object_id);
+ 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 2d5e7a6..373b253 100644
--- a/src/crepe/api/Script.h
+++ b/src/crepe/api/Script.h
@@ -18,8 +18,10 @@ 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:
@@ -88,24 +90,40 @@ protected:
void logf(Args &&... args);
/**
- * \brief Subscribe to an event
- *
+ * \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;
@@ -113,6 +131,7 @@ public:
// std::unique_ptr destroys script
virtual ~Script();
+private:
Script(const Script &) = delete;
Script(Script &&) = delete;
Script & operator=(const Script &) = delete;
@@ -125,10 +144,19 @@ private:
* 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
- game_object_id_t game_object_id = -1;
+ 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;
//! Reference to event manager instance
@@ -142,6 +170,13 @@ private:
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 <>
diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp
index 96e4a36..8186bd4 100644
--- a/src/crepe/api/Script.hpp
+++ b/src/crepe/api/Script.hpp
@@ -22,7 +22,7 @@ template <typename T>
std::vector<std::reference_wrapper<T>> Script::get_components() const {
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>
@@ -33,7 +33,11 @@ void Script::logf(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>(callback, channel);
+ 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);
}
diff --git a/src/doc/layout.xml b/src/doc/layout.xml
index 2244fa7..7f514d4 100644
--- a/src/doc/layout.xml
+++ b/src/doc/layout.xml
@@ -42,6 +42,7 @@
</navindex>
<class>
<briefdescription visible="yes"/>
+ <detaileddescription title=""/>
<includes visible="$SHOW_HEADERFILE"/>
<inheritancegraph visible="yes"/>
<collaborationgraph visible="yes"/>
@@ -79,7 +80,6 @@
<related title="" subtitle=""/>
<membergroups visible="yes"/>
</memberdecl>
- <detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
diff --git a/src/test/ScriptTest.cpp b/src/test/ScriptTest.cpp
index 19fef6d..875c422 100644
--- a/src/test/ScriptTest.cpp
+++ b/src/test/ScriptTest.cpp
@@ -9,26 +9,44 @@
#include <crepe/api/GameObject.h>
#include <crepe/api/Script.h>
#include <crepe/api/Vector2.h>
+#include <crepe/api/Event.h>
+#include <crepe/api/EventManager.h>
#include <crepe/system/ScriptSystem.h>
using namespace std;
using namespace crepe;
using namespace testing;
+class MyEvent : public Event { };
+
class ScriptTest : public Test {
public:
ComponentManager component_manager{};
ScriptSystem system{component_manager};
+ EventManager & evmgr = EventManager::get_instance();
class MyScript : public Script {
// NOTE: default (private) visibility of init and update shouldn't cause
// issues!
- void init() { this->init_count++; }
- void update() { this->update_count++; }
+ void init() {
+ this->init_count++;
+
+ subscribe<MyEvent>([this](const MyEvent &) {
+ this->event_count++;
+ return true;
+ });
+
+ // init should never be called more than once
+ EXPECT_LE(this->init_count, 1);
+ }
+ void update() {
+ this->update_count++;
+ }
public:
unsigned init_count = 0;
unsigned update_count = 0;
+ unsigned event_count = 0;
};
BehaviorScript * behaviorscript_ref = nullptr;
@@ -46,21 +64,58 @@ public:
this->script_ref = (MyScript *) this->behaviorscript_ref->script.get();
ASSERT_NE(this->script_ref, nullptr);
+
+ // sanity
+ ASSERT_EQ(script_ref->init_count, 0);
+ ASSERT_EQ(script_ref->update_count, 0);
+ ASSERT_EQ(script_ref->event_count, 0);
}
};
TEST_F(ScriptTest, Default) {
EXPECT_EQ(0, this->script_ref->init_count);
EXPECT_EQ(0, this->script_ref->update_count);
+ EXPECT_EQ(0, this->script_ref->event_count);
}
TEST_F(ScriptTest, UpdateOnce) {
+ this->system.update();
+ EXPECT_EQ(1, this->script_ref->init_count);
+ EXPECT_EQ(1, this->script_ref->update_count);
+ EXPECT_EQ(0, this->script_ref->event_count);
+}
+
+TEST_F(ScriptTest, UpdateInactive) {
+ this->behaviorscript_ref->active = false;
+ this->system.update();
EXPECT_EQ(0, this->script_ref->init_count);
EXPECT_EQ(0, this->script_ref->update_count);
+ EXPECT_EQ(0, this->script_ref->event_count);
+
+ this->behaviorscript_ref->active = true;
+ this->system.update();
+ EXPECT_EQ(1, this->script_ref->init_count);
+ EXPECT_EQ(1, this->script_ref->update_count);
+ EXPECT_EQ(0, this->script_ref->event_count);
+}
+TEST_F(ScriptTest, EventInactive) {
this->system.update();
+ this->behaviorscript_ref->active = false;
EXPECT_EQ(1, this->script_ref->init_count);
EXPECT_EQ(1, this->script_ref->update_count);
+ EXPECT_EQ(0, this->script_ref->event_count);
+
+ this->evmgr.trigger_event<MyEvent>();
+ EXPECT_EQ(1, this->script_ref->init_count);
+ EXPECT_EQ(1, this->script_ref->update_count);
+ EXPECT_EQ(0, this->script_ref->event_count);
+
+ this->behaviorscript_ref->active = true;
+ this->evmgr.trigger_event<MyEvent>();
+ EXPECT_EQ(1, this->script_ref->init_count);
+ EXPECT_EQ(1, this->script_ref->update_count);
+ EXPECT_EQ(1, this->script_ref->event_count);
}
TEST_F(ScriptTest, ListScripts) {
@@ -70,3 +125,4 @@ TEST_F(ScriptTest, ListScripts) {
}
ASSERT_EQ(1, script_count);
}
+