aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/crepe/api/AI.cpp23
-rw-r--r--src/crepe/api/AI.h50
-rw-r--r--src/crepe/system/AISystem.cpp1
-rw-r--r--src/crepe/system/AISystem.h44
-rw-r--r--src/example/AITest.cpp10
5 files changed, 112 insertions, 16 deletions
diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp
index d785bb5..49f6b92 100644
--- a/src/crepe/api/AI.cpp
+++ b/src/crepe/api/AI.cpp
@@ -4,4 +4,27 @@ 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, vec2 center, float start_angle, bool clockwise) {
+ // The step size is determined by the radius (step size is in radians)
+ float step = 400.0f / radius;
+ // Force at least 16 steps (in case of a small radius)
+ if (step > 2 * M_PI / 16) {
+ step = 2 * M_PI / 16;
+ }
+ // The path node distance is determined by the step size and the radius
+ path_node_distance = radius * step * 0.75f;
+
+ 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))});
+ }
+ }
+}
+
} // namespace crepe
diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h
index 35b8998..9f5c0a8 100644
--- a/src/crepe/api/AI.h
+++ b/src/crepe/api/AI.h
@@ -5,8 +5,15 @@
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 BehaviorType {
NONE = 0x00000,
SEEK = 0x00002,
@@ -16,53 +23,76 @@ public:
};
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/activated
+ *
+ * \param behavior The behavior to check
+ * \return true if the behavior is on, false otherwise
+ */
bool on(BehaviorType behavior) const { return (flags & behavior) == behavior; }
+ //! Turn on the seek behavior
void seek_on() { flags |= SEEK; }
+ //! Turn off the seek behavior
void seek_off() {
if (on(SEEK)) flags ^= SEEK;
}
+ //! Turn on the flee behavior
void flee_on() { flags |= FLEE; }
+ //! Turn off the flee behavior
void flee_off() {
if (on(FLEE)) flags ^= FLEE;
}
+ //! Turn on the arrive behavior
void arrive_on() { flags |= ARRIVE; }
+ //! Turn off the arrive behavior
void arrive_off() {
if (on(ARRIVE)) 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() {
if (on(PATH_FOLLOW)) flags ^= PATH_FOLLOW;
}
+ //! Add a path node to the path
void add_path_node(vec2 node) { path.push_back(node); }
+ //! Create a circle path
+ void make_circle_path(float radius, vec2 center = {0, 0}, float start_angle = 0,
+ bool clockwise = true);
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 or arrive at
+ //! The target to seek or arrive at
vec2 seek_target;
- // The target to flee from
+ //! The target to flee from
vec2 flee_target;
- // The distance at which the entity will start to flee from the 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))
+ //! 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
+ //! The path to follow
std::vector<vec2> path;
- // The distance from the path node at which the entity will move to the next node
+ //! The distance from the path node at which the entity will move to the next node
float path_node_distance = 400.0f;
- // Looping behavior for the path
+ //! Looping behavior for the path
bool path_loop = true;
private:
- // The flags for the behaviors
+ //! The flags for the behaviors
int flags = 0;
- // The current path index
+ //! The current path index
size_t path_index = 0;
- // The AISystem is the only class that can access the private members of AI
+ //! The AISystem is the only class that should access the flags and path_index variables
friend class AISystem;
};
diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp
index 55dc14c..7b801c3 100644
--- a/src/crepe/system/AISystem.cpp
+++ b/src/crepe/system/AISystem.cpp
@@ -17,6 +17,7 @@ void AISystem::update() {
ComponentManager & mgr = mediator.component_manager;
RefVector<AI> ai_components = mgr.get_components_by_type<AI>();
+ //TODO: Use fixed loop dt (this is not available at master at the moment)
double dt = LoopTimer::get_instance().get_delta_time();
for (AI & ai : ai_components) {
diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h
index 27861d9..670d20d 100644
--- a/src/crepe/system/AISystem.h
+++ b/src/crepe/system/AISystem.h
@@ -7,19 +7,63 @@
namespace crepe {
+/**
+ * \brief The AISystem is used to control the movement of entities using AI.
+ *
+ * The AISystem is used to control the movement of entities using AI. The AISystem can be used to
+ * implement different behaviors such as seeking, fleeing, arriving, and path following.
+ */
class AISystem : public System {
public:
using System::System;
+ //! Update the AI system
void update() override;
private:
+ /**
+ * \brief Calculate the total force to apply to the entity
+ *
+ * \param ai The AI component
+ */
vec2 calculate(AI & ai);
+ /**
+ * \brief Accumulate the force to apply to the entity
+ *
+ * \param ai The AI component
+ * \param running_total The running total of the force
+ * \param force_to_add The force to add
+ * \return true if the force was added, false otherwise
+ */
bool accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add);
+ /**
+ * \brief Calculate the seek force
+ *
+ * \param ai The AI component
+ * \return The seek force
+ */
vec2 seek(const AI & ai);
+ /**
+ * \brief Calculate the flee force
+ *
+ * \param ai The AI component
+ * \return The flee force
+ */
vec2 flee(const AI & ai);
+ /**
+ * \brief Calculate the arrive force
+ *
+ * \param ai The AI component
+ * \return The arrive force
+ */
vec2 arrive(const AI & ai);
+ /**
+ * \brief Calculate the path follow force
+ *
+ * \param ai The AI component
+ * \return The path follow force
+ */
vec2 path_follow(AI & ai);
};
diff --git a/src/example/AITest.cpp b/src/example/AITest.cpp
index d12a99a..72e06cf 100644
--- a/src/example/AITest.cpp
+++ b/src/example/AITest.cpp
@@ -52,16 +52,14 @@ public:
Texture img = Texture("asset/texture/test_ap43.png");
game_object1.add_component<Sprite>(img, Color::MAGENTA,
Sprite::FlipSettings{false, false}, 1, 1, 195);
- AI & ai = game_object1.add_component<AI>(300);
+ AI & ai = game_object1.add_component<AI>(3000);
// ai.arrive_on();
// ai.flee_on();
ai.path_follow_on();
- ai.add_path_node(vec2{1200, 1200});
- ai.add_path_node(vec2{-1200, 1200});
- ai.add_path_node(vec2{1200, -1200});
- ai.add_path_node(vec2{-1200, -1200});
+ ai.make_circle_path(1000, {0, -1000}, 1.5707, true);
+ ai.make_circle_path(1000, {0, 1000}, 4.7124, false);
game_object1.add_component<Rigidbody>(Rigidbody::Data{
- .mass = 0.5f, .max_linear_velocity = {50, 50}, // sqrt(21^2 + 21^2) = 30
+ .mass = 0.5f, .max_linear_velocity = {40, 40}, // sqrt(21^2 + 21^2) = 30
});
game_object1.add_component<BehaviorScript>().set_script<Script1>();