#include #include #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_components = mgr.get_components_by_type(); double dt = LoopTimer::get_instance().get_fixed_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 / 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 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 *= 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 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 = 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 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 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); 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 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}; } 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); }