diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/crepe/api/AI.cpp | 23 | ||||
-rw-r--r-- | src/crepe/api/AI.h | 50 | ||||
-rw-r--r-- | src/crepe/system/AISystem.cpp | 1 | ||||
-rw-r--r-- | src/crepe/system/AISystem.h | 44 | ||||
-rw-r--r-- | src/example/AITest.cpp | 10 |
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>(); |