diff options
Diffstat (limited to 'src/crepe')
-rw-r--r-- | src/crepe/api/AI.cpp | 11 | ||||
-rw-r--r-- | src/crepe/api/AI.h | 60 | ||||
-rw-r--r-- | src/crepe/api/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/crepe/api/LoopManager.cpp | 14 | ||||
-rw-r--r-- | src/crepe/api/Vector2.h | 24 | ||||
-rw-r--r-- | src/crepe/api/Vector2.hpp | 48 | ||||
-rw-r--r-- | src/crepe/system/AISystem.cpp | 78 | ||||
-rw-r--r-- | src/crepe/system/AISystem.h | 23 | ||||
-rw-r--r-- | src/crepe/system/CMakeLists.txt | 2 |
9 files changed, 261 insertions, 1 deletions
diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp new file mode 100644 index 0000000..7f820a8 --- /dev/null +++ b/src/crepe/api/AI.cpp @@ -0,0 +1,11 @@ +#include "AI.h" + +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) {} + +} // namespace crepe diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h new file mode 100644 index 0000000..242ff89 --- /dev/null +++ b/src/crepe/api/AI.h @@ -0,0 +1,60 @@ +#pragma once + +#include "Component.h" +#include "types.h" + +namespace crepe { + +class AI : public Component { +public: + enum BehaviorType { + NONE = 0x00000, + SEEK = 0x00002, + FLEE = 0x00004, + ARRIVE = 0x00008, + PATH_FOLLOW = 0x00010, + }; + +public: + 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: + vec2 velocity; + friend class AISystem; + + int flags = 0; +}; + +} // namespace crepe diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index b2e3df8..9f61ef5 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -24,6 +24,7 @@ target_sources(crepe PUBLIC Script.cpp Button.cpp UIObject.cpp + AI.cpp ) target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -58,4 +59,5 @@ target_sources(crepe PUBLIC FILE_SET HEADERS FILES Asset.h Button.h UIObject.h + AI.h ) diff --git a/src/crepe/api/LoopManager.cpp b/src/crepe/api/LoopManager.cpp index b9e91dd..2f8a0a7 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -1,3 +1,5 @@ +#include "../facade/SDLContext.h" +#include "../system/AISystem.h" #include "../system/AnimatorSystem.h" #include "../system/CollisionSystem.h" #include "../system/InputSystem.h" @@ -23,6 +25,7 @@ LoopManager::LoopManager() { this->load_system<RenderSystem>(); this->load_system<ScriptSystem>(); this->load_system<InputSystem>(); + this->load_system<AISystem>(); } void LoopManager::process_input() { this->get_system<InputSystem>().update(); } @@ -58,6 +61,11 @@ void LoopManager::loop() { this->render(); timer.enforce_frame_rate(); + + // Stop the game after 10 seconds, for testing purposes + if (timer.get_current_time() > 10) { + this->game_running = false; + } } } @@ -67,6 +75,7 @@ void LoopManager::setup() { this->scene_manager.load_next_scene(); timer.start(); timer.set_fps(200); + this->scene_manager.load_next_scene(); } void LoopManager::render() { @@ -75,4 +84,7 @@ void LoopManager::render() { this->get_system<RenderSystem>().update(); } -void LoopManager::update() {} +void LoopManager::update() { + this->get_system<AnimatorSystem>().update(); + this->get_system<AISystem>().update(); +} diff --git a/src/crepe/api/Vector2.h b/src/crepe/api/Vector2.h index c278c87..bf9d124 100644 --- a/src/crepe/api/Vector2.h +++ b/src/crepe/api/Vector2.h @@ -66,6 +66,30 @@ struct Vector2 { //! Checks if this vector is not equal to another vector. bool operator!=(const Vector2<T> & other) const; + + //! Truncates the vector to a maximum length. + void truncate(T max); + + //! Normalizes the vector (resulting in vector with a length of 1). + void normalize(); + + //! Returns the length of the vector. + T length() const; + + //! Returns the squared length of the vector. + T length_squared() const; + + //! Returns the dot product (inwendig product) of this vector and another vector. + T dot(const Vector2<T> & other) const; + + //! Returns the distance between this vector and another vector. + T distance(const Vector2<T> & other) const; + + //! Returns the squared distance between this vector and another vector. + T distance_squared(const Vector2<T> & other) const; + + //! Returns the perpendicular vector to this vector. + Vector2 perpendicular() const; }; } // namespace crepe diff --git a/src/crepe/api/Vector2.hpp b/src/crepe/api/Vector2.hpp index cad15f8..ff53cb0 100644 --- a/src/crepe/api/Vector2.hpp +++ b/src/crepe/api/Vector2.hpp @@ -1,5 +1,7 @@ #pragma once +#include <cmath> + #include "Vector2.h" namespace crepe { @@ -115,4 +117,50 @@ bool Vector2<T>::operator!=(const Vector2<T> & other) const { return !(*this == other); } +template <class T> +void Vector2<T>::truncate(T max) { + if (length() > max) { + normalize(); + *this *= max; + } +} + +template <class T> +void Vector2<T>::normalize() { + T len = length(); + if (len > 0) { + *this /= len; + } +} + +template <class T> +T Vector2<T>::length() const { + return std::sqrt(x * x + y * y); +} + +template <class T> +T Vector2<T>::length_squared() const { + return x * x + y * y; +} + +template <class T> +T Vector2<T>::dot(const Vector2<T> & other) const { + return x * other.x + y * other.y; +} + +template <class T> +T Vector2<T>::distance(const Vector2<T> & other) const { + return (*this - other).length(); +} + +template <class T> +T Vector2<T>::distance_squared(const Vector2<T> & other) const { + return (*this - other).length_squared(); +} + +template <class T> +Vector2<T> Vector2<T>::perpendicular() const { + return {-y, x}; +} + } // namespace crepe diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp new file mode 100644 index 0000000..3c61c78 --- /dev/null +++ b/src/crepe/system/AISystem.cpp @@ -0,0 +1,78 @@ +#include "api/LoopTimer.h" +#include "api/Transform.h" +#include "manager/ComponentManager.h" +#include "manager/Mediator.h" +#include "types.h" + +#include "AISystem.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) { + vec2 force = this->calculate(ai); + vec2 acceleration = force / ai.mass; + ai.velocity += acceleration * dt; + ai.velocity.truncate(ai.max_speed); + + // Update the position + RefVector<Transform> transforms + = mgr.get_components_by_id<Transform>(ai.game_object_id); + Transform & transform = transforms.front().get(); + transform.position += ai.velocity * dt; + } +} + +vec2 AISystem::calculate(AI & ai) { + vec2 force; + + if (ai.on(AI::BehaviorType::SEEK)) { + vec2 force_to_add = this->seek(ai); + + if (!this->accumulate_force(force, force_to_add)) { + return force; + } + } + 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; +} + +bool AISystem::accumulate_force(vec2 & running_total, vec2 force_to_add) { + double magnitude_remaining = running_total.length(); + double magnitude_to_add = force_to_add.length(); + + if (magnitude_remaining + magnitude_to_add > 0) { + running_total += force_to_add; + return true; + } + + return false; +} + +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(); + + vec2 desired_velocity = ai.seek_target - transform.position; + desired_velocity.normalize(); + desired_velocity *= ai.max_speed; + + return desired_velocity - ai.velocity; +} diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h new file mode 100644 index 0000000..5e94ccb --- /dev/null +++ b/src/crepe/system/AISystem.h @@ -0,0 +1,23 @@ +#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(vec2 & running_total, vec2 force_to_add); + + vec2 seek(const 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 ) |