diff options
Diffstat (limited to 'src/crepe')
-rw-r--r-- | src/crepe/api/AI.cpp | 7 | ||||
-rw-r--r-- | src/crepe/api/AI.h | 55 | ||||
-rw-r--r-- | src/crepe/api/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/crepe/api/LoopManager.cpp | 10 | ||||
-rw-r--r-- | src/crepe/api/Rigidbody.h | 1 | ||||
-rw-r--r-- | src/crepe/api/Script.h | 2 | ||||
-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 | 85 | ||||
-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 | 38 | ||||
-rw-r--r-- | src/crepe/system/CollisionSystem.h | 6 |
13 files changed, 277 insertions, 29 deletions
diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp new file mode 100644 index 0000000..d785bb5 --- /dev/null +++ b/src/crepe/api/AI.cpp @@ -0,0 +1,7 @@ +#include "AI.h" + +namespace crepe { + +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 new file mode 100644 index 0000000..046426d --- /dev/null +++ b/src/crepe/api/AI.h @@ -0,0 +1,55 @@ +#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 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 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/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..90308a7 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(); } @@ -37,6 +40,7 @@ void LoopManager::fixed_update() { EventManager & ev = this->mediator.event_manager; ev.dispatch_events(); this->get_system<ScriptSystem>().update(); + this->get_system<AISystem>().update(); this->get_system<PhysicsSystem>().update(); this->get_system<CollisionSystem>().update(); } @@ -47,13 +51,13 @@ void LoopManager::loop() { while (game_running) { timer.update(); - + while (timer.get_lag() >= timer.get_fixed_delta_time()) { this->process_input(); this->fixed_update(); timer.advance_fixed_update(); } - + this->update(); this->render(); @@ -75,4 +79,4 @@ void LoopManager::render() { this->get_system<RenderSystem>().update(); } -void LoopManager::update() {} +void LoopManager::update() { this->get_system<AnimatorSystem>().update(); } diff --git a/src/crepe/api/Rigidbody.h b/src/crepe/api/Rigidbody.h index b0a24f7..722a665 100644 --- a/src/crepe/api/Rigidbody.h +++ b/src/crepe/api/Rigidbody.h @@ -7,7 +7,6 @@ #include "types.h" - namespace crepe { /** diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index 1474a09..fa83152 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -3,10 +3,10 @@ #include <vector> #include "../manager/EventManager.h" -#include "system/CollisionSystem.h" #include "../manager/Mediator.h" #include "../types.h" #include "../util/OptionalRef.h" +#include "system/CollisionSystem.h" namespace crepe { 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..4858000 --- /dev/null +++ b/src/crepe/system/AISystem.cpp @@ -0,0 +1,85 @@ +#include "api/LoopTimer.h" +#include "api/Rigidbody.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) { + 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::SEEK)) { + vec2 force_to_add = this->seek(ai); + + if (!this->accumulate_force(ai, 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(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; +} diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h new file mode 100644 index 0000000..18f1c61 --- /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(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 ) diff --git a/src/crepe/system/CollisionSystem.cpp b/src/crepe/system/CollisionSystem.cpp index 44ea7ee..2a487fd 100644 --- a/src/crepe/system/CollisionSystem.cpp +++ b/src/crepe/system/CollisionSystem.cpp @@ -6,6 +6,8 @@ #include <utility> #include <variant> +#include "../manager/ComponentManager.h" +#include "../manager/EventManager.h" #include "api/BoxCollider.h" #include "api/CircleCollider.h" #include "api/Event.h" @@ -13,8 +15,6 @@ #include "api/Rigidbody.h" #include "api/Transform.h" #include "api/Vector2.h" -#include "../manager/ComponentManager.h" -#include "../manager/EventManager.h" #include "Collider.h" #include "CollisionSystem.h" @@ -27,17 +27,14 @@ void CollisionSystem::update() { std::vector<CollisionInternal> all_colliders; game_object_id_t id = 0; ComponentManager & mgr = this->mediator.component_manager; - RefVector<Rigidbody> rigidbodies - = mgr.get_components_by_type<Rigidbody>(); + RefVector<Rigidbody> rigidbodies = mgr.get_components_by_type<Rigidbody>(); // Collisions can only happen on object with a rigidbody for (Rigidbody & rigidbody : rigidbodies) { if (!rigidbody.active) continue; id = rigidbody.game_object_id; - Transform & transform - = mgr.get_components_by_id<Transform>(id).front().get(); + Transform & transform = mgr.get_components_by_id<Transform>(id).front().get(); // Check if the boxcollider is active and has the same id as the rigidbody. - RefVector<BoxCollider> boxcolliders - = mgr.get_components_by_type<BoxCollider>(); + RefVector<BoxCollider> boxcolliders = mgr.get_components_by_type<BoxCollider>(); for (BoxCollider & boxcollider : boxcolliders) { if (boxcollider.game_object_id != id) continue; if (!boxcollider.active) continue; @@ -159,7 +156,7 @@ CollisionSystem::collision_handler(CollisionInternal & data1, CollisionInternal vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, data2.rigidbody); resolution = this->get_circle_box_resolution(collider2, collider1, collider_pos2, - collider_pos1,true); + collider_pos1, true); break; } case CollisionInternalType::CIRCLE_CIRCLE: { @@ -185,7 +182,7 @@ CollisionSystem::collision_handler(CollisionInternal & data1, CollisionInternal vec2 collider_pos2 = this->get_current_position(collider2.offset, data2.transform, data2.rigidbody); resolution = this->get_circle_box_resolution(collider1, collider2, collider_pos1, - collider_pos2,false); + collider_pos2, false); break; } } @@ -261,7 +258,6 @@ vec2 CollisionSystem::get_circle_circle_resolution(const CircleCollider & circle // Normalize the delta vector to get the collision direction vec2 collision_normal = delta / distance; - // Compute the resolution vector vec2 resolution = -collision_normal * penetration_depth; @@ -272,7 +268,8 @@ vec2 CollisionSystem::get_circle_circle_resolution(const CircleCollider & circle vec2 CollisionSystem::get_circle_box_resolution(const CircleCollider & circle_collider, const BoxCollider & box_collider, const vec2 & circle_position, - const vec2 & box_position,bool inverse) const { + const vec2 & box_position, + bool inverse) const { vec2 delta = circle_position - box_position; // Compute half-dimensions of the box @@ -294,7 +291,7 @@ vec2 CollisionSystem::get_circle_box_resolution(const CircleCollider & circle_co // Compute penetration depth float penetration_depth = circle_collider.radius - distance; - if(inverse) collision_normal = -collision_normal; + if (inverse) collision_normal = -collision_normal; // Compute the resolution vector vec2 resolution = collision_normal * penetration_depth; @@ -311,8 +308,7 @@ void CollisionSystem::determine_collision_handler(CollisionInfo & info) { // Call collision event for user CollisionEvent data(info); EventManager & emgr = this->mediator.event_manager; - emgr.trigger_event<CollisionEvent>( - data, info.this_collider.game_object_id); + emgr.trigger_event<CollisionEvent>(data, info.this_collider.game_object_id); } void CollisionSystem::static_collision_handler(CollisionInfo & info) { @@ -389,14 +385,14 @@ CollisionSystem::gather_collisions(std::vector<CollisionInternal> & colliders) { bool CollisionSystem::have_common_layer(const std::set<int> & layers1, const std::set<int> & layers2) { - + // Check if any number is equal in the layers for (int num : layers1) { - if (layers2.contains(num)) { - // Common layer found - return true; - break; - } + if (layers2.contains(num)) { + // Common layer found + return true; + break; + } } // No common layer found return false; diff --git a/src/crepe/system/CollisionSystem.h b/src/crepe/system/CollisionSystem.h index b978dbb..eee582b 100644 --- a/src/crepe/system/CollisionSystem.h +++ b/src/crepe/system/CollisionSystem.h @@ -6,11 +6,11 @@ #include "api/BoxCollider.h" #include "api/CircleCollider.h" +#include "api/Event.h" #include "api/Metadata.h" #include "api/Rigidbody.h" #include "api/Transform.h" #include "api/Vector2.h" -#include "api/Event.h" #include "Collider.h" #include "System.h" @@ -183,8 +183,8 @@ private: */ vec2 get_circle_box_resolution(const CircleCollider & circle_collider, const BoxCollider & box_collider, - const vec2 & circle_position, - const vec2 & box_position,bool inverse) const; + const vec2 & circle_position, const vec2 & box_position, + bool inverse) const; /** * \brief Determines the appropriate collision handler for a collision. |