aboutsummaryrefslogtreecommitdiff
path: root/src/crepe/api
diff options
context:
space:
mode:
authorMax-001 <80035972+Max-001@users.noreply.github.com>2024-12-11 14:14:43 +0100
committerGitHub <noreply@github.com>2024-12-11 14:14:43 +0100
commit78c4a8772526f40c531b5402b56932b0a41e22e8 (patch)
tree7305c4f88bf177cb8dd704eb9363d3935325804f /src/crepe/api
parentc45b60941b82dec2097d958b396a117b1634eada (diff)
parent0dbbbe4bac4ad4fcb9e88908034e90000056363e (diff)
Merge pull request #68 from lonkaars/max/AI
Max/ai
Diffstat (limited to 'src/crepe/api')
-rw-r--r--src/crepe/api/AI.cpp89
-rw-r--r--src/crepe/api/AI.h128
-rw-r--r--src/crepe/api/CMakeLists.txt2
-rw-r--r--src/crepe/api/LoopManager.cpp3
-rw-r--r--src/crepe/api/LoopManager.h2
-rw-r--r--src/crepe/api/Vector2.h24
-rw-r--r--src/crepe/api/Vector2.hpp48
7 files changed, 295 insertions, 1 deletions
diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp
new file mode 100644
index 0000000..2195249
--- /dev/null
+++ b/src/crepe/api/AI.cpp
@@ -0,0 +1,89 @@
+#include <stdexcept>
+#include <type_traits>
+
+#include "AI.h"
+#include "types.h"
+
+namespace crepe {
+
+AI::AI(game_object_id_t id, float max_force) : Component(id), max_force(max_force) {}
+
+void AI::make_circle_path(float radius, const vec2 & center, float start_angle,
+ bool clockwise) {
+ if (radius <= 0) {
+ throw std::runtime_error("Radius must be greater than 0");
+ }
+
+ // The step size is determined by the radius (step size is in radians)
+ float step = RADIUS_TO_STEP / radius;
+ // Force at least MIN_STEP steps (in case of a small radius)
+ if (step > 2 * M_PI / MIN_STEP) {
+ step = 2 * M_PI / MIN_STEP;
+ }
+ // The path node distance is determined by the step size and the radius
+ this->path_node_distance = radius * step * PATH_NODE_DISTANCE_FACTOR;
+
+ if (clockwise) {
+ for (float i = start_angle; i < 2 * M_PI + start_angle; i += step) {
+ path.push_back(vec2{static_cast<float>(center.x + radius * cos(i)),
+ static_cast<float>(center.y + radius * sin(i))});
+ }
+ } else {
+ for (float i = start_angle; i > start_angle - 2 * M_PI; i -= step) {
+ path.push_back(vec2{static_cast<float>(center.x + radius * cos(i)),
+ static_cast<float>(center.y + radius * sin(i))});
+ }
+ }
+}
+
+void AI::make_oval_path(float radius_x, float radius_y, const vec2 & center, float start_angle,
+ bool clockwise, float rotation) {
+ if (radius_x <= 0 && radius_y <= 0) {
+ throw std::runtime_error("Radius must be greater than 0");
+ }
+
+ float max_radius = std::max(radius_x, radius_y);
+ // The step size is determined by the radius (step size is in radians)
+ float step = RADIUS_TO_STEP / max_radius;
+ // Force at least MIN_STEP steps (in case of a small radius)
+ if (step > 2 * M_PI / MIN_STEP) {
+ step = 2 * M_PI / MIN_STEP;
+ }
+ // The path node distance is determined by the step size and the radius
+ this->path_node_distance = max_radius * step * PATH_NODE_DISTANCE_FACTOR;
+
+ std::function<vec2(vec2, vec2)> rotate_point = [rotation](vec2 point, vec2 center) {
+ float s = sin(rotation);
+ float c = cos(rotation);
+
+ // Translate point back to origin
+ point.x -= center.x;
+ point.y -= center.y;
+
+ // Rotate point
+ float xnew = point.x * c - point.y * s;
+ float ynew = point.x * s + point.y * c;
+
+ // Translate point back
+ point.x = xnew + center.x;
+ point.y = ynew + center.y;
+
+ return point;
+ };
+
+ if (clockwise) {
+ for (float i = start_angle; i < 2 * M_PI + start_angle; i += step) {
+ vec2 point = {static_cast<float>(center.x + radius_x * cos(i)),
+ static_cast<float>(center.y + radius_y * sin(i))};
+ path.push_back(rotate_point(point, center));
+ }
+ } else {
+ for (float i = start_angle; i > start_angle - 2 * M_PI; i -= step) {
+ vec2 point = {static_cast<float>(center.x + radius_x * cos(i)),
+ static_cast<float>(center.y + radius_y * sin(i))};
+ path.push_back(rotate_point(point, center));
+ }
+ }
+}
+
+} // namespace crepe
diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h
new file mode 100644
index 0000000..c780a91
--- /dev/null
+++ b/src/crepe/api/AI.h
@@ -0,0 +1,128 @@
+#pragma once
+
+#include "Component.h"
+#include "types.h"
+
+namespace crepe {
+
+/**
+ * \brief The AI component is used to control the movement of an entity using AI.
+ *
+ * The AI component can be used to control the movement of an entity. The AI component can be used
+ * to implement different behaviors such as seeking, fleeing, arriving, and path following.
+ */
+class AI : public Component {
+public:
+ //! The different types of behaviors that can be used
+ enum BehaviorTypeMask {
+ SEEK = 0x00002,
+ FLEE = 0x00004,
+ ARRIVE = 0x00008,
+ PATH_FOLLOW = 0x00010,
+ };
+
+public:
+ /**
+ * \param id The id of the game object
+ * \param max_force The maximum force that can be applied to the entity
+ */
+ AI(game_object_id_t id, float max_force);
+
+ /**
+ * \brief Check if a behavior is on (aka activated)
+ *
+ * \param behavior The behavior to check
+ * \return true if the behavior is on, false otherwise
+ */
+ bool on(BehaviorTypeMask behavior) const { return (flags & behavior); }
+ //! Turn on the seek behavior
+ void seek_on() { flags |= SEEK; }
+ //! Turn off the seek behavior
+ void seek_off() { flags &= ~SEEK; }
+ //! Turn on the flee behavior
+ void flee_on() { flags |= FLEE; }
+ //! Turn off the flee behavior
+ void flee_off() { flags &= ~FLEE; }
+ //! Turn on the arrive behavior
+ void arrive_on() { flags |= ARRIVE; }
+ //! Turn off the arrive behavior
+ void arrive_off() { flags &= ~ARRIVE; }
+ //! Turn on the path follow behavior
+ void path_follow_on() { flags |= PATH_FOLLOW; }
+ //! Turn off the path follow behavior
+ void path_follow_off() { flags &= ~PATH_FOLLOW; }
+
+ /**
+ * \brief Add a path node (for the path following behavior)
+ *
+ * \note The path is not relative to the entity's position (it is an absolute path)
+ *
+ * \param node The path node to add
+ */
+ void add_path_node(const vec2 & node) { path.push_back(node); }
+ /**
+ * \brief Make a circle path (for the path following behavior)
+ *
+ * \note The path is not relative to the entity's position (it is an absolute path)
+ *
+ * \param radius The radius of the circle (in game units)
+ * \param center The center of the circle (in game units)
+ * \param start_angle The start angle of the circle (in radians)
+ * \param clockwise The direction of the circle
+ */
+ void make_circle_path(float radius, const vec2 & center = {0, 0}, float start_angle = 0,
+ bool clockwise = true);
+ /**
+ * \brief Make an oval path (for the path following behavior)
+ *
+ * \note The path is not relative to the entity's position (it is an absolute path)
+ *
+ * \param radius_x The x radius of the oval (in game units)
+ * \param radius_y The y radius of the oval (in game units)
+ * \param center The center of the oval (in game units)
+ * \param start_angle The start angle of the oval (in radians)
+ * \param clockwise The direction of the oval
+ * \param rotation The rotation of the oval (in radians)
+ */
+ void make_oval_path(float radius_x, float radius_y, const vec2 & center = {0, 0},
+ float start_angle = 0, bool clockwise = true, float rotation = 0);
+
+public:
+ //! The maximum force that can be applied to the entity (higher values will make the entity adjust faster)
+ float max_force;
+
+ //! The target to seek at
+ vec2 seek_target;
+ //! The target to arrive at
+ vec2 arrive_target;
+ //! The target to flee from
+ vec2 flee_target;
+ //! The distance at which the entity will start to flee from the target
+ float square_flee_panic_distance = 200.0f * 200.0f;
+ //! The deceleration rate for the arrive behavior (higher values will make the entity decelerate faster (less overshoot))
+ float arrive_deceleration = 40.0f;
+ //! The path to follow (for the path following behavior)
+ std::vector<vec2> path;
+ //! The distance from the path node at which the entity will move to the next node (automatically set by make_circle_path())
+ float path_node_distance = 400.0f;
+ //! Looping behavior for the path
+ bool path_loop = true;
+
+private:
+ //! The flags for the behaviors
+ int flags = 0;
+ //! The current path index
+ size_t path_index = 0;
+
+ //! The AISystem is the only class that should access the flags and path_index variables
+ friend class AISystem;
+
+ //! The minimum amount of steps for the path following behavior
+ static constexpr int MIN_STEP = 16;
+ //! The radius to step size ratio for the path following behavior
+ static constexpr float RADIUS_TO_STEP = 400.0f;
+ //! The path node distance factor for the path following behavior
+ static constexpr float PATH_NODE_DISTANCE_FACTOR = 0.75f;
+};
+
+} // namespace crepe
diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt
index a163faf..118c7ce 100644
--- a/src/crepe/api/CMakeLists.txt
+++ b/src/crepe/api/CMakeLists.txt
@@ -23,6 +23,7 @@ target_sources(crepe PUBLIC
Script.cpp
Button.cpp
UIObject.cpp
+ AI.cpp
)
target_sources(crepe PUBLIC FILE_SET HEADERS FILES
@@ -55,4 +56,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES
Asset.h
Button.h
UIObject.h
+ AI.h
)
diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp
index 3e9e21c..1f0ba72 100644
--- a/src/crepe/api/LoopManager.cpp
+++ b/src/crepe/api/LoopManager.cpp
@@ -1,3 +1,4 @@
+#include "../system/AISystem.h"
#include "../system/AnimatorSystem.h"
#include "../system/AudioSystem.h"
#include "../system/CollisionSystem.h"
@@ -22,6 +23,7 @@ LoopManager::LoopManager() {
this->load_system<ScriptSystem>();
this->load_system<InputSystem>();
this->load_system<AudioSystem>();
+ this->load_system<AISystem>();
}
void LoopManager::process_input() { this->get_system<InputSystem>().update(); }
@@ -37,6 +39,7 @@ void LoopManager::fixed_update() {
EventManager & ev = this->mediator.event_manager;
ev.dispatch_events();
this->get_system<ScriptSystem>().update();
+ this->get_system<AISystem>().update();
this->get_system<PhysicsSystem>().update();
this->get_system<CollisionSystem>().update();
this->get_system<AudioSystem>().update();
diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h
index 33a61a3..700afe4 100644
--- a/src/crepe/api/LoopManager.h
+++ b/src/crepe/api/LoopManager.h
@@ -5,8 +5,8 @@
#include "../facade/SDLContext.h"
#include "../manager/ComponentManager.h"
#include "../manager/ResourceManager.h"
-#include "../manager/SceneManager.h"
#include "../manager/SaveManager.h"
+#include "../manager/SceneManager.h"
#include "../system/System.h"
#include "LoopTimer.h"
diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h
index c278c87..bf9d124 100644
--- a/src/crepe/api/Vector2.h
+++ b/src/crepe/api/Vector2.h
@@ -66,6 +66,30 @@ struct Vector2 {
//! Checks if this vector is not equal to another vector.
bool operator!=(const Vector2<T> & other) const;
+
+ //! Truncates the vector to a maximum length.
+ void truncate(T max);
+
+ //! Normalizes the vector (resulting in vector with a length of 1).
+ void normalize();
+
+ //! Returns the length of the vector.
+ T length() const;
+
+ //! Returns the squared length of the vector.
+ T length_squared() const;
+
+ //! Returns the dot product (inwendig product) of this vector and another vector.
+ T dot(const Vector2<T> & other) const;
+
+ //! Returns the distance between this vector and another vector.
+ T distance(const Vector2<T> & other) const;
+
+ //! Returns the squared distance between this vector and another vector.
+ T distance_squared(const Vector2<T> & other) const;
+
+ //! Returns the perpendicular vector to this vector.
+ Vector2 perpendicular() const;
};
} // namespace crepe
diff --git a/src/crepe/api/Vector2.hpp b/src/crepe/api/Vector2.hpp
index cad15f8..ff53cb0 100644
--- a/src/crepe/api/Vector2.hpp
+++ b/src/crepe/api/Vector2.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include <cmath>
+
#include "Vector2.h"
namespace crepe {
@@ -115,4 +117,50 @@ bool Vector2<T>::operator!=(const Vector2<T> & other) const {
return !(*this == other);
}
+template <class T>
+void Vector2<T>::truncate(T max) {
+ if (length() > max) {
+ normalize();
+ *this *= max;
+ }
+}
+
+template <class T>
+void Vector2<T>::normalize() {
+ T len = length();
+ if (len > 0) {
+ *this /= len;
+ }
+}
+
+template <class T>
+T Vector2<T>::length() const {
+ return std::sqrt(x * x + y * y);
+}
+
+template <class T>
+T Vector2<T>::length_squared() const {
+ return x * x + y * y;
+}
+
+template <class T>
+T Vector2<T>::dot(const Vector2<T> & other) const {
+ return x * other.x + y * other.y;
+}
+
+template <class T>
+T Vector2<T>::distance(const Vector2<T> & other) const {
+ return (*this - other).length();
+}
+
+template <class T>
+T Vector2<T>::distance_squared(const Vector2<T> & other) const {
+ return (*this - other).length_squared();
+}
+
+template <class T>
+Vector2<T> Vector2<T>::perpendicular() const {
+ return {-y, x};
+}
+
} // namespace crepe