From ac87bfad20e1bcf1fd066a4eda231608fe12f504 Mon Sep 17 00:00:00 2001 From: max-001 Date: Wed, 4 Dec 2024 13:17:08 +0100 Subject: Added AI component --- src/crepe/api/AI.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/crepe/api/AI.cpp (limited to 'src/crepe/api/AI.cpp') diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp new file mode 100644 index 0000000..6b63216 --- /dev/null +++ b/src/crepe/api/AI.cpp @@ -0,0 +1,11 @@ +#include "AI.h" + +namespace crepe { + +AI::AI(game_object_id_t id, double mass, double max_speed, double max_force) + : Component(id), + mass(mass), + max_speed(max_speed), + max_force(max_force) {} + +} // namespace crepe -- cgit v1.2.3 From f9f5600b60d6944dc9a7dd502988703d59d0cd62 Mon Sep 17 00:00:00 2001 From: max-001 Date: Wed, 4 Dec 2024 13:46:41 +0100 Subject: Setup some behaviors --- src/crepe/api/AI.cpp | 2 +- src/crepe/api/AI.h | 47 +++++++++++++++++++++++++++++++++++++++---- src/crepe/system/AISystem.cpp | 32 +++++++++++++++++++++++++++-- src/crepe/system/AISystem.h | 6 ++++++ 4 files changed, 80 insertions(+), 7 deletions(-) (limited to 'src/crepe/api/AI.cpp') diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp index 6b63216..7f820a8 100644 --- a/src/crepe/api/AI.cpp +++ b/src/crepe/api/AI.cpp @@ -2,7 +2,7 @@ namespace crepe { -AI::AI(game_object_id_t id, double mass, double max_speed, double max_force) +AI::AI(game_object_id_t id, float mass, float max_speed, float max_force) : Component(id), mass(mass), max_speed(max_speed), diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h index b755439..faeeba5 100644 --- a/src/crepe/api/AI.h +++ b/src/crepe/api/AI.h @@ -7,12 +7,51 @@ namespace crepe { class AI : public Component { public: - AI(game_object_id_t id, double mass, double max_speed, double max_force); + enum BehaviorType { + NONE = 0x00000, + SEEK = 0x00002, + FLEE = 0x00004, + ARRIVE = 0x00008, + PATH_FOLLOW = 0x00010, + }; public: - double mass; - double max_speed; - double max_force; + AI(game_object_id_t id, float mass, float max_speed, float max_force); + + bool on(BehaviorType behavior) const { return (flags & behavior) == behavior; } + void seek_on() { flags |= SEEK; } + void seek_off() { + if (on(SEEK)) flags ^= SEEK; + } + void flee_on() { flags |= FLEE; } + void flee_off() { + if (on(FLEE)) flags ^= FLEE; + } + void arrive_on() { flags |= ARRIVE; } + void arrive_off() { + if (on(ARRIVE)) flags ^= ARRIVE; + } + void path_follow_on() { flags |= PATH_FOLLOW; } + void path_follow_off() { + if (on(PATH_FOLLOW)) flags ^= PATH_FOLLOW; + } + +public: + float mass; + float max_speed; + float max_force; + + // The target to seek or arrive at + vec2 seek_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 = 100.0f * 100.0f; + // The deceleration rate for the arrive behavior (higher values will make the entity decelerate faster (less overshoot)) + float arrive_deceleration = 2.0f; + +private: + int flags = 0; }; } // namespace crepe diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp index 012f4fa..12da3d9 100644 --- a/src/crepe/system/AISystem.cpp +++ b/src/crepe/system/AISystem.cpp @@ -1,6 +1,34 @@ +#include "../ComponentManager.h" + #include "AISystem.h" -#include using namespace crepe; -void AISystem::update() { std::cout << "AI System update" << std::endl; } +void AISystem::update() { + ComponentManager & mgr = this->component_manager; + RefVector ai_components = mgr.get_components_by_type(); + + for (AI & ai : ai_components) { + vec2 force = this->calculate(ai); + //... + } +} + +vec2 AISystem::calculate(AI & ai) { + vec2 force; + + if (ai.on(AI::BehaviorType::SEEK)) { + // Seek the target + } + if (ai.on(AI::BehaviorType::FLEE)) { + // Flee from the target + } + if (ai.on(AI::BehaviorType::ARRIVE)) { + // Arrive at the target + } + if (ai.on(AI::BehaviorType::PATH_FOLLOW)) { + // Follow the path + } + + return force; +} diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h index 4138e01..eb8d08c 100644 --- a/src/crepe/system/AISystem.h +++ b/src/crepe/system/AISystem.h @@ -1,6 +1,9 @@ #pragma once +#include "api/AI.h" + #include "System.h" +#include "types.h" namespace crepe { @@ -9,6 +12,9 @@ public: using System::System; void update() override; + +private: + vec2 calculate(AI & ai); }; } // namespace crepe -- cgit v1.2.3 From 9c2cafd85ed7aa9a860ba25fbe2bd3ccc2439f29 Mon Sep 17 00:00:00 2001 From: max-001 Date: Fri, 6 Dec 2024 10:39:18 +0100 Subject: Using Rigidbody from now on --- src/crepe/api/AI.cpp | 6 +----- src/crepe/api/AI.h | 7 +------ src/crepe/system/AISystem.cpp | 22 +++++++++++----------- src/example/AITest.cpp | 6 +++++- 4 files changed, 18 insertions(+), 23 deletions(-) (limited to 'src/crepe/api/AI.cpp') diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp index 7f820a8..d785bb5 100644 --- a/src/crepe/api/AI.cpp +++ b/src/crepe/api/AI.cpp @@ -2,10 +2,6 @@ namespace crepe { -AI::AI(game_object_id_t id, float mass, float max_speed, float max_force) - : Component(id), - mass(mass), - max_speed(max_speed), - max_force(max_force) {} +AI::AI(game_object_id_t id, float max_force) : Component(id), max_force(max_force) {} } // namespace crepe diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h index 242ff89..046426d 100644 --- a/src/crepe/api/AI.h +++ b/src/crepe/api/AI.h @@ -16,7 +16,7 @@ public: }; public: - AI(game_object_id_t id, float mass, float max_speed, float max_force); + AI(game_object_id_t id, float max_force); bool on(BehaviorType behavior) const { return (flags & behavior) == behavior; } void seek_on() { flags |= SEEK; } @@ -37,8 +37,6 @@ public: } public: - float mass; - float max_speed; float max_force; // The target to seek or arrive at @@ -51,9 +49,6 @@ public: float arrive_deceleration = 2.0f; private: - vec2 velocity; - friend class AISystem; - int flags = 0; }; diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp index 3c61c78..d496e12 100644 --- a/src/crepe/system/AISystem.cpp +++ b/src/crepe/system/AISystem.cpp @@ -1,4 +1,5 @@ #include "api/LoopTimer.h" +#include "api/Rigidbody.h" #include "api/Transform.h" #include "manager/ComponentManager.h" #include "manager/Mediator.h" @@ -16,16 +17,13 @@ void AISystem::update() { double dt = LoopTimer::get_instance().get_delta_time(); for (AI & ai : ai_components) { + RefVector rigidbodies + = mgr.get_components_by_id(ai.game_object_id); + Rigidbody & rigidbody = rigidbodies.front().get(); + vec2 force = this->calculate(ai); - vec2 acceleration = force / ai.mass; - ai.velocity += acceleration * dt; - ai.velocity.truncate(ai.max_speed); - - // Update the position - RefVector transforms - = mgr.get_components_by_id(ai.game_object_id); - Transform & transform = transforms.front().get(); - transform.position += ai.velocity * dt; + vec2 acceleration = force / rigidbody.data.mass; + rigidbody.data.linear_velocity += acceleration * dt; } } @@ -69,10 +67,12 @@ vec2 AISystem::seek(const AI & ai) { ComponentManager & mgr = mediator.component_manager; RefVector transforms = mgr.get_components_by_id(ai.game_object_id); Transform & transform = transforms.front().get(); + RefVector rigidbodies = mgr.get_components_by_id(ai.game_object_id); + Rigidbody & rigidbody = rigidbodies.front().get(); vec2 desired_velocity = ai.seek_target - transform.position; desired_velocity.normalize(); - desired_velocity *= ai.max_speed; + desired_velocity *= rigidbody.data.max_linear_velocity; - return desired_velocity - ai.velocity; + return desired_velocity - rigidbody.data.linear_velocity; } diff --git a/src/example/AITest.cpp b/src/example/AITest.cpp index 91a529c..2b6a4d6 100644 --- a/src/example/AITest.cpp +++ b/src/example/AITest.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -40,7 +41,10 @@ public: Texture img = Texture("asset/texture/test_ap43.png"); game_object1.add_component(img, Color::MAGENTA, Sprite::FlipSettings{false, false}, 1, 1, 195); - game_object1.add_component(1, 200, 200).seek_on(); + game_object1.add_component(200).seek_on(); + game_object1.add_component(Rigidbody::Data{ + .mass = 1.0f, .max_linear_velocity = {21, 21}, // sqrt(21^2 + 21^2) = 30 + }); game_object2.add_component(Color::WHITE, ivec2{1080, 720}, vec2{1036, 780}, 1.0f); -- cgit v1.2.3 From 6cee1cff083fc50eeedf88537965d3c79e7b790a Mon Sep 17 00:00:00 2001 From: max-001 Date: Mon, 9 Dec 2024 11:28:06 +0100 Subject: Added Doxygen --- src/crepe/api/AI.cpp | 23 ++++++++++++++++++++ src/crepe/api/AI.h | 50 ++++++++++++++++++++++++++++++++++--------- src/crepe/system/AISystem.cpp | 1 + src/crepe/system/AISystem.h | 44 +++++++++++++++++++++++++++++++++++++ src/example/AITest.cpp | 10 ++++----- 5 files changed, 112 insertions(+), 16 deletions(-) (limited to 'src/crepe/api/AI.cpp') 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(center.x + radius * cos(i)), + static_cast(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(center.x + radius * cos(i)), + static_cast(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 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_components = mgr.get_components_by_type(); + //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(img, Color::MAGENTA, Sprite::FlipSettings{false, false}, 1, 1, 195); - AI & ai = game_object1.add_component(300); + AI & ai = game_object1.add_component(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::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().set_script(); -- cgit v1.2.3 From 857e408d6d3a8631b61ebd283b337819783f092c Mon Sep 17 00:00:00 2001 From: max-001 Date: Mon, 9 Dec 2024 14:14:03 +0100 Subject: Implemented oval path --- src/crepe/api/AI.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ src/crepe/api/AI.h | 14 ++++++++++++++ src/example/AITest.cpp | 4 ++-- 3 files changed, 62 insertions(+), 2 deletions(-) (limited to 'src/crepe/api/AI.cpp') diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp index 49f6b92..00e5b37 100644 --- a/src/crepe/api/AI.cpp +++ b/src/crepe/api/AI.cpp @@ -27,4 +27,50 @@ void AI::make_circle_path(float radius, vec2 center, float start_angle, bool clo } } +void AI::make_oval_path(float radius_x, float radius_y, vec2 center, float start_angle, + bool clockwise, float rotation) { + float max_radius = std::max(radius_x, radius_y); + // The step size is determined by the radius (step size is in radians) + float step = 400.0f / max_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 = max_radius * step * 0.75f; + + auto 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(center.x + radius_x * cos(i)), + static_cast(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(center.x + radius_x * cos(i)), + static_cast(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 index 0dccd5f..c95924d 100644 --- a/src/crepe/api/AI.h +++ b/src/crepe/api/AI.h @@ -81,6 +81,20 @@ public: */ void make_circle_path(float radius, 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, 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) diff --git a/src/example/AITest.cpp b/src/example/AITest.cpp index 28537ed..f4efc9f 100644 --- a/src/example/AITest.cpp +++ b/src/example/AITest.cpp @@ -59,8 +59,8 @@ public: // ai.arrive_on(); // ai.flee_on(); ai.path_follow_on(); - ai.make_circle_path(1000, {0, -1000}, 1.5707, true); - ai.make_circle_path(1000, {0, 1000}, 4.7124, false); + ai.make_oval_path(500, 1000, {0, -1000}, 1.5708, true); + ai.make_oval_path(1000, 500, {0, 500}, 4.7124, false); game_object1.add_component(Rigidbody::Data{ .mass = 0.1f, .max_linear_velocity = {40, 40}, -- cgit v1.2.3 From c8d05f759bb1be0ec99e8f23eaa65c80c36ce03d Mon Sep 17 00:00:00 2001 From: max-001 Date: Tue, 10 Dec 2024 08:48:06 +0100 Subject: Implemented feedback --- src/crepe/api/AI.cpp | 15 ++++++++++++-- src/crepe/api/AI.h | 6 +++--- src/crepe/system/AISystem.cpp | 46 ++++++++++++++++++++++--------------------- src/crepe/system/AISystem.h | 18 +++++++++++------ 4 files changed, 52 insertions(+), 33 deletions(-) (limited to 'src/crepe/api/AI.cpp') diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp index 00e5b37..472550b 100644 --- a/src/crepe/api/AI.cpp +++ b/src/crepe/api/AI.cpp @@ -1,10 +1,17 @@ +#include + #include "AI.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, vec2 center, float start_angle, bool clockwise) { +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 = 400.0f / radius; // Force at least 16 steps (in case of a small radius) @@ -27,8 +34,12 @@ void AI::make_circle_path(float radius, vec2 center, float start_angle, bool clo } } -void AI::make_oval_path(float radius_x, float radius_y, vec2 center, float start_angle, +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 = 400.0f / max_radius; diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h index c95924d..de574cd 100644 --- a/src/crepe/api/AI.h +++ b/src/crepe/api/AI.h @@ -68,7 +68,7 @@ public: * * \param node The path node to add */ - void add_path_node(vec2 node) { path.push_back(node); } + void add_path_node(const vec2 & node) { path.push_back(node); } /** * \brief Make a circle path (for the path following behavior) * @@ -79,7 +79,7 @@ public: * \param start_angle The start angle of the circle (in radians) * \param clockwise The direction of the circle */ - void make_circle_path(float radius, vec2 center = {0, 0}, float start_angle = 0, + 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) @@ -93,7 +93,7 @@ public: * \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, vec2 center = {0, 0}, + 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: diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp index 62fa553..4d5039b 100644 --- a/src/crepe/system/AISystem.cpp +++ b/src/crepe/system/AISystem.cpp @@ -2,7 +2,6 @@ #include #include "api/LoopTimer.h" -#include "api/Rigidbody.h" #include "api/Transform.h" #include "manager/ComponentManager.h" #include "manager/Mediator.h" @@ -27,10 +26,17 @@ void AISystem::update() { RefVector rigidbodies = mgr.get_components_by_id(ai.game_object_id); + if (rigidbodies.empty()) { + throw std::runtime_error( + "AI component must be attached to a GameObject with a Rigidbody component"); + } Rigidbody & rigidbody = rigidbodies.front().get(); + if (rigidbody.data.mass <= 0) { + throw std::runtime_error("Mass must be greater than 0"); + } // Calculate the force to apply to the entity - vec2 force = this->calculate(ai); + vec2 force = this->calculate(ai, rigidbody); // Calculate the acceleration (using the above calculated force) vec2 acceleration = force / rigidbody.data.mass; // Finally, update Rigidbody's velocity @@ -38,33 +44,33 @@ void AISystem::update() { } } -vec2 AISystem::calculate(AI & ai) { +vec2 AISystem::calculate(AI & ai, const Rigidbody & rigidbody) { vec2 force; // Run all the behaviors that are on, and stop if the force gets too high if (ai.on(AI::BehaviorType::FLEE)) { - vec2 force_to_add = this->flee(ai); + vec2 force_to_add = this->flee(ai, rigidbody); if (!this->accumulate_force(ai, force, force_to_add)) { return force; } } if (ai.on(AI::BehaviorType::ARRIVE)) { - vec2 force_to_add = this->arrive(ai); + vec2 force_to_add = this->arrive(ai, rigidbody); if (!this->accumulate_force(ai, force, force_to_add)) { return force; } } if (ai.on(AI::BehaviorType::SEEK)) { - vec2 force_to_add = this->seek(ai); + vec2 force_to_add = this->seek(ai, rigidbody); if (!this->accumulate_force(ai, force, force_to_add)) { return force; } } if (ai.on(AI::BehaviorType::PATH_FOLLOW)) { - vec2 force_to_add = this->path_follow(ai); + vec2 force_to_add = this->path_follow(ai, rigidbody); if (!this->accumulate_force(ai, force, force_to_add)) { return force; @@ -74,7 +80,7 @@ vec2 AISystem::calculate(AI & ai) { return force; } -bool AISystem::accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add) { +bool AISystem::accumulate_force(const AI & ai, vec2 & running_total, vec2 force_to_add) { float magnitude = running_total.length(); float magnitude_remaining = ai.max_force - magnitude; @@ -96,13 +102,11 @@ bool AISystem::accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add return true; } -vec2 AISystem::seek(const AI & ai) { +vec2 AISystem::seek(const AI & ai, const Rigidbody & rigidbody) const { const Mediator & mediator = this->mediator; ComponentManager & mgr = mediator.component_manager; RefVector transforms = mgr.get_components_by_id(ai.game_object_id); Transform & transform = transforms.front().get(); - RefVector rigidbodies = mgr.get_components_by_id(ai.game_object_id); - Rigidbody & rigidbody = rigidbodies.front().get(); // Calculate the desired velocity vec2 desired_velocity = ai.seek_target - transform.position; @@ -112,13 +116,11 @@ vec2 AISystem::seek(const AI & ai) { return desired_velocity - rigidbody.data.linear_velocity; } -vec2 AISystem::flee(const AI & ai) { +vec2 AISystem::flee(const AI & ai, const Rigidbody & rigidbody) const { const Mediator & mediator = this->mediator; ComponentManager & mgr = mediator.component_manager; RefVector transforms = mgr.get_components_by_id(ai.game_object_id); Transform & transform = transforms.front().get(); - RefVector rigidbodies = mgr.get_components_by_id(ai.game_object_id); - Rigidbody & rigidbody = rigidbodies.front().get(); // Calculate the desired velocity if the entity is within the panic distance vec2 desired_velocity = transform.position - ai.flee_target; @@ -131,18 +133,20 @@ vec2 AISystem::flee(const AI & ai) { return desired_velocity - rigidbody.data.linear_velocity; } -vec2 AISystem::arrive(const AI & ai) { +vec2 AISystem::arrive(const AI & ai, const Rigidbody & rigidbody) const { const Mediator & mediator = this->mediator; ComponentManager & mgr = mediator.component_manager; RefVector transforms = mgr.get_components_by_id(ai.game_object_id); Transform & transform = transforms.front().get(); - RefVector rigidbodies = mgr.get_components_by_id(ai.game_object_id); - Rigidbody & rigidbody = rigidbodies.front().get(); // Calculate the desired velocity (taking into account the deceleration rate) vec2 to_target = ai.seek_target - transform.position; float distance = to_target.length(); if (distance > 0.0f) { + if (ai.arrive_deceleration <= 0.0f) { + throw std::runtime_error("Deceleration rate must be greater than 0"); + } + float speed = distance / ai.arrive_deceleration; speed = std::min(speed, rigidbody.data.max_linear_velocity.length()); vec2 desired_velocity = to_target * (speed / distance); @@ -153,13 +157,11 @@ vec2 AISystem::arrive(const AI & ai) { return vec2{0, 0}; } -vec2 AISystem::path_follow(AI & ai) { +vec2 AISystem::path_follow(AI & ai, const Rigidbody & rigidbody) { const Mediator & mediator = this->mediator; ComponentManager & mgr = mediator.component_manager; RefVector transforms = mgr.get_components_by_id(ai.game_object_id); Transform & transform = transforms.front().get(); - RefVector rigidbodies = mgr.get_components_by_id(ai.game_object_id); - Rigidbody & rigidbody = rigidbodies.front().get(); if (ai.path.empty()) { return vec2{0, 0}; @@ -182,11 +184,11 @@ vec2 AISystem::path_follow(AI & ai) { } else { // If the path is not looping, arrive at the last node ai.path_index = ai.path.size() - 1; - return this->arrive(ai); + return this->arrive(ai, rigidbody); } } } // Seek the target node - return this->seek(ai); + return this->seek(ai, rigidbody); } diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h index 670d20d..160df01 100644 --- a/src/crepe/system/AISystem.h +++ b/src/crepe/system/AISystem.h @@ -1,6 +1,7 @@ #pragma once #include "api/AI.h" +#include "api/Rigidbody.h" #include "System.h" #include "types.h" @@ -25,8 +26,9 @@ private: * \brief Calculate the total force to apply to the entity * * \param ai The AI component + * \param rigidbody The Rigidbody component */ - vec2 calculate(AI & ai); + vec2 calculate(AI & ai, const Rigidbody & rigidbody); /** * \brief Accumulate the force to apply to the entity * @@ -35,36 +37,40 @@ private: * \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); + bool accumulate_force(const AI & ai, vec2 & running_total, vec2 force_to_add); /** * \brief Calculate the seek force * * \param ai The AI component + * \param rigidbody The Rigidbody component * \return The seek force */ - vec2 seek(const AI & ai); + vec2 seek(const AI & ai, const Rigidbody & rigidbody) const; /** * \brief Calculate the flee force * * \param ai The AI component + * \param rigidbody The Rigidbody component * \return The flee force */ - vec2 flee(const AI & ai); + vec2 flee(const AI & ai, const Rigidbody & rigidbody) const; /** * \brief Calculate the arrive force * * \param ai The AI component + * \param rigidbody The Rigidbody component * \return The arrive force */ - vec2 arrive(const AI & ai); + vec2 arrive(const AI & ai, const Rigidbody & rigidbody) const; /** * \brief Calculate the path follow force * * \param ai The AI component + * \param rigidbody The Rigidbody component * \return The path follow force */ - vec2 path_follow(AI & ai); + vec2 path_follow(AI & ai, const Rigidbody & rigidbody); }; } // namespace crepe -- cgit v1.2.3 From 405bf73b273c7b9a6574d77b4a034d9588c9af1b Mon Sep 17 00:00:00 2001 From: max-001 Date: Wed, 11 Dec 2024 13:18:59 +0100 Subject: Removed magic numbers --- src/crepe/api/AI.cpp | 16 ++++++++-------- src/crepe/api/AI.h | 3 +++ 2 files changed, 11 insertions(+), 8 deletions(-) (limited to 'src/crepe/api/AI.cpp') diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp index 472550b..34e1438 100644 --- a/src/crepe/api/AI.cpp +++ b/src/crepe/api/AI.cpp @@ -13,10 +13,10 @@ void AI::make_circle_path(float radius, const vec2 & center, float start_angle, } // 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; + 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 path_node_distance = radius * step * 0.75f; @@ -42,10 +42,10 @@ void AI::make_oval_path(float radius_x, float radius_y, const vec2 & center, flo float max_radius = std::max(radius_x, radius_y); // The step size is determined by the radius (step size is in radians) - float step = 400.0f / max_radius; - // Force at least 16 steps (in case of a small radius) - if (step > 2 * M_PI / 16) { - step = 2 * M_PI / 16; + 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 path_node_distance = max_radius * step * 0.75f; diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h index 80feda5..fc28451 100644 --- a/src/crepe/api/AI.h +++ b/src/crepe/api/AI.h @@ -116,6 +116,9 @@ private: //! The AISystem is the only class that should access the flags and path_index variables friend class AISystem; + + static constexpr const int MIN_STEP = 16; + static constexpr const float RADIUS_TO_STEP = 400.0f; }; } // namespace crepe -- cgit v1.2.3 From 6f89e67c9b2dfcb182ed2fa9d9a0ea7afa53fbf0 Mon Sep 17 00:00:00 2001 From: max-001 Date: Wed, 11 Dec 2024 14:06:26 +0100 Subject: Clarified 0.75f --- src/crepe/api/AI.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/crepe/api/AI.cpp') diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp index 34e1438..a4d3e98 100644 --- a/src/crepe/api/AI.cpp +++ b/src/crepe/api/AI.cpp @@ -47,8 +47,8 @@ void AI::make_oval_path(float radius_x, float radius_y, const vec2 & center, flo 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 - path_node_distance = max_radius * step * 0.75f; + // The path node distance is determined by the step size times the radius times 75% + this->path_node_distance = max_radius * step * 0.75f; auto rotate_point = [rotation](vec2 point, vec2 center) { float s = sin(rotation); -- cgit v1.2.3 From 0dbbbe4bac4ad4fcb9e88908034e90000056363e Mon Sep 17 00:00:00 2001 From: max-001 Date: Wed, 11 Dec 2024 14:10:43 +0100 Subject: Implemented feedback --- src/crepe/api/AI.cpp | 10 ++++++---- src/crepe/api/AI.h | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) (limited to 'src/crepe/api/AI.cpp') diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp index a4d3e98..2195249 100644 --- a/src/crepe/api/AI.cpp +++ b/src/crepe/api/AI.cpp @@ -1,6 +1,8 @@ #include +#include #include "AI.h" +#include "types.h" namespace crepe { @@ -19,7 +21,7 @@ void AI::make_circle_path(float radius, const vec2 & center, float start_angle, step = 2 * M_PI / MIN_STEP; } // The path node distance is determined by the step size and the radius - path_node_distance = radius * step * 0.75f; + 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) { @@ -47,10 +49,10 @@ void AI::make_oval_path(float radius_x, float radius_y, const vec2 & center, flo if (step > 2 * M_PI / MIN_STEP) { step = 2 * M_PI / MIN_STEP; } - // The path node distance is determined by the step size times the radius times 75% - this->path_node_distance = max_radius * step * 0.75f; + // The path node distance is determined by the step size and the radius + this->path_node_distance = max_radius * step * PATH_NODE_DISTANCE_FACTOR; - auto rotate_point = [rotation](vec2 point, vec2 center) { + std::function rotate_point = [rotation](vec2 point, vec2 center) { float s = sin(rotation); float c = cos(rotation); diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h index 4f7692b..c780a91 100644 --- a/src/crepe/api/AI.h +++ b/src/crepe/api/AI.h @@ -121,6 +121,8 @@ private: 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 -- cgit v1.2.3