diff options
Diffstat (limited to 'src/crepe/system')
-rw-r--r-- | src/crepe/system/AISystem.cpp | 170 | ||||
-rw-r--r-- | src/crepe/system/AISystem.h | 26 | ||||
-rw-r--r-- | src/crepe/system/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/crepe/system/CollisionSystem.cpp | 1 |
4 files changed, 198 insertions, 1 deletions
diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp new file mode 100644 index 0000000..55dc14c --- /dev/null +++ b/src/crepe/system/AISystem.cpp @@ -0,0 +1,170 @@ +#include <algorithm> +#include <cmath> + +#include "api/LoopTimer.h" +#include "api/Rigidbody.h" +#include "api/Transform.h" +#include "manager/ComponentManager.h" +#include "manager/Mediator.h" + +#include "AISystem.h" +#include "types.h" + +using namespace crepe; + +void AISystem::update() { + const Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + RefVector<AI> ai_components = mgr.get_components_by_type<AI>(); + + double dt = LoopTimer::get_instance().get_delta_time(); + + for (AI & ai : ai_components) { + RefVector<Rigidbody> rigidbodies + = mgr.get_components_by_id<Rigidbody>(ai.game_object_id); + Rigidbody & rigidbody = rigidbodies.front().get(); + + vec2 force = this->calculate(ai); + vec2 acceleration = force / rigidbody.data.mass; + rigidbody.data.linear_velocity += acceleration * dt; + } +} + +vec2 AISystem::calculate(AI & ai) { + vec2 force; + + if (ai.on(AI::BehaviorType::FLEE)) { + vec2 force_to_add = this->flee(ai); + + if (!this->accumulate_force(ai, force, force_to_add)) { + return force; + } + } + if (ai.on(AI::BehaviorType::ARRIVE)) { + vec2 force_to_add = this->arrive(ai); + + if (!this->accumulate_force(ai, force, force_to_add)) { + return force; + } + } + if (ai.on(AI::BehaviorType::SEEK)) { + vec2 force_to_add = this->seek(ai); + + 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); + + if (!this->accumulate_force(ai, force, force_to_add)) { + return force; + } + } + + return force; +} + +bool AISystem::accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add) { + float magnitude = running_total.length(); + float magnitude_remaining = ai.max_force - magnitude; + + if (magnitude_remaining <= 0.0f) { + return false; + } + + float magnitude_to_add = force_to_add.length(); + if (magnitude_to_add < magnitude_remaining) { + running_total += force_to_add; + } else { + force_to_add.normalize(); + running_total += force_to_add * magnitude_remaining; + } + + return true; +} + +vec2 AISystem::seek(const AI & ai) { + const Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id); + Transform & transform = transforms.front().get(); + RefVector<Rigidbody> rigidbodies = mgr.get_components_by_id<Rigidbody>(ai.game_object_id); + Rigidbody & rigidbody = rigidbodies.front().get(); + + vec2 desired_velocity = ai.seek_target - transform.position; + desired_velocity.normalize(); + desired_velocity *= rigidbody.data.max_linear_velocity; + + return desired_velocity - rigidbody.data.linear_velocity; +} + +vec2 AISystem::flee(const AI & ai) { + const Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id); + Transform & transform = transforms.front().get(); + RefVector<Rigidbody> rigidbodies = mgr.get_components_by_id<Rigidbody>(ai.game_object_id); + Rigidbody & rigidbody = rigidbodies.front().get(); + + vec2 desired_velocity = transform.position - ai.flee_target; + if (desired_velocity.length_squared() > ai.square_flee_panic_distance) { + return vec2{0, 0}; + } + + desired_velocity.normalize(); + desired_velocity *= rigidbody.data.max_linear_velocity; + + return desired_velocity - rigidbody.data.linear_velocity; +} + +vec2 AISystem::arrive(const AI & ai) { + const Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id); + Transform & transform = transforms.front().get(); + RefVector<Rigidbody> rigidbodies = mgr.get_components_by_id<Rigidbody>(ai.game_object_id); + Rigidbody & rigidbody = rigidbodies.front().get(); + + vec2 to_target = ai.seek_target - transform.position; + float distance = to_target.length(); + if (distance > 0.0f) { + float speed = distance / ai.arrive_deceleration; + speed = std::min(speed, rigidbody.data.max_linear_velocity.length()); + vec2 desired_velocity = to_target * (speed / distance); + + return desired_velocity - rigidbody.data.linear_velocity; + } + + return vec2{0, 0}; +} + +vec2 AISystem::path_follow(AI & ai) { + const Mediator & mediator = this->mediator; + ComponentManager & mgr = mediator.component_manager; + RefVector<Transform> transforms = mgr.get_components_by_id<Transform>(ai.game_object_id); + Transform & transform = transforms.front().get(); + RefVector<Rigidbody> rigidbodies = mgr.get_components_by_id<Rigidbody>(ai.game_object_id); + Rigidbody & rigidbody = rigidbodies.front().get(); + + if (ai.path.empty()) { + return vec2{0, 0}; + } + + vec2 to_target = ai.path.at(ai.path_index) - transform.position; + if (to_target.length_squared() > ai.path_node_distance * ai.path_node_distance) { + ai.seek_target = ai.path.at(ai.path_index); + } else { + ai.path_index++; + if (ai.path_index >= ai.path.size()) { + if (ai.path_loop) { + ai.path_index = 0; + } else { + ai.path_index = ai.path.size() - 1; + return this->arrive(ai); + } + } + } + + return this->seek(ai); +} diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h new file mode 100644 index 0000000..27861d9 --- /dev/null +++ b/src/crepe/system/AISystem.h @@ -0,0 +1,26 @@ +#pragma once + +#include "api/AI.h" + +#include "System.h" +#include "types.h" + +namespace crepe { + +class AISystem : public System { +public: + using System::System; + + void update() override; + +private: + vec2 calculate(AI & ai); + bool accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add); + + vec2 seek(const AI & ai); + vec2 flee(const AI & ai); + vec2 arrive(const AI & ai); + vec2 path_follow(AI & ai); +}; + +} // namespace crepe diff --git a/src/crepe/system/CMakeLists.txt b/src/crepe/system/CMakeLists.txt index 95f6e33..7de5198 100644 --- a/src/crepe/system/CMakeLists.txt +++ b/src/crepe/system/CMakeLists.txt @@ -7,6 +7,7 @@ target_sources(crepe PUBLIC RenderSystem.cpp AnimatorSystem.cpp InputSystem.cpp + AISystem.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -17,4 +18,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES RenderSystem.h AnimatorSystem.h InputSystem.h + AISystem.h ) diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp index 44a0431..0483693 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -290,7 +290,6 @@ vec2 CollisionSystem::get_circle_box_resolution(const CircleCollider & circle_co // Compute penetration depth float penetration_depth = circle_collider.radius - distance; - // Compute the resolution vector vec2 resolution = collision_normal * penetration_depth; |