diff options
Diffstat (limited to 'src/crepe/api')
| -rw-r--r-- | src/crepe/api/AI.cpp | 89 | ||||
| -rw-r--r-- | src/crepe/api/AI.h | 128 | ||||
| -rw-r--r-- | src/crepe/api/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/crepe/api/LoopManager.cpp | 6 | ||||
| -rw-r--r-- | src/crepe/api/LoopManager.h | 6 | ||||
| -rw-r--r-- | src/crepe/api/Script.cpp | 8 | ||||
| -rw-r--r-- | src/crepe/api/Script.h | 22 | ||||
| -rw-r--r-- | src/crepe/api/Script.hpp | 30 | ||||
| -rw-r--r-- | src/crepe/api/Vector2.h | 24 | ||||
| -rw-r--r-- | src/crepe/api/Vector2.hpp | 48 | 
10 files changed, 353 insertions, 10 deletions
| diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp new file mode 100644 index 0000000..2195249 --- /dev/null +++ b/src/crepe/api/AI.cpp @@ -0,0 +1,89 @@ +#include <stdexcept> +#include <type_traits> + +#include "AI.h" +#include "types.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, 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 = 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 +	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) { +			path.push_back(vec2{static_cast<float>(center.x + radius * cos(i)), +								static_cast<float>(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<float>(center.x + radius * cos(i)), +								static_cast<float>(center.y + radius * sin(i))}); +		} +	} +} + +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 = 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 +	this->path_node_distance = max_radius * step * PATH_NODE_DISTANCE_FACTOR; + +	std::function<vec2(vec2, vec2)> 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<float>(center.x + radius_x * cos(i)), +						  static_cast<float>(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<float>(center.x + radius_x * cos(i)), +						  static_cast<float>(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 new file mode 100644 index 0000000..c780a91 --- /dev/null +++ b/src/crepe/api/AI.h @@ -0,0 +1,128 @@ +#pragma once + +#include "Component.h" +#include "types.h" + +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 BehaviorTypeMask { +		SEEK = 0x00002, +		FLEE = 0x00004, +		ARRIVE = 0x00008, +		PATH_FOLLOW = 0x00010, +	}; + +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 (aka activated) +	 * +	 * \param behavior The behavior to check +	 * \return true if the behavior is on, false otherwise +	 */ +	bool on(BehaviorTypeMask behavior) const { return (flags & behavior); } +	//! Turn on the seek behavior +	void seek_on() { flags |= SEEK; } +	//! Turn off the seek behavior +	void seek_off() { flags &= ~SEEK; } +	//! Turn on the flee behavior +	void flee_on() { flags |= FLEE; } +	//! Turn off the flee behavior +	void flee_off() { flags &= ~FLEE; } +	//! Turn on the arrive behavior +	void arrive_on() { flags |= ARRIVE; } +	//! Turn off the arrive behavior +	void arrive_off() { 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() { flags &= ~PATH_FOLLOW; } + +	/** +	 * \brief Add a path node (for the path following behavior) +	 * +	 * \note The path is not relative to the entity's position (it is an absolute path) +	 * +	 * \param node The path node to add +	 */ +	void add_path_node(const vec2 & node) { path.push_back(node); } +	/** +	 * \brief Make a circle path (for the path following behavior) +	 * +	 * \note The path is not relative to the entity's position (it is an absolute path) +	 * +	 * \param radius The radius of the circle (in game units) +	 * \param center The center of the circle (in game units) +	 * \param start_angle The start angle of the circle (in radians) +	 * \param clockwise The direction of the circle +	 */ +	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) +	 * +	 * \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, const 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) +	float max_force; + +	//! The target to seek at +	vec2 seek_target; +	//! The target to arrive at +	vec2 arrive_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 = 200.0f * 200.0f; +	//! 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 (for the path following behavior) +	std::vector<vec2> path; +	//! The distance from the path node at which the entity will move to the next node (automatically set by make_circle_path()) +	float path_node_distance = 400.0f; +	//! Looping behavior for the path +	bool path_loop = true; + +private: +	//! The flags for the behaviors +	int flags = 0; +	//! The current path index +	size_t path_index = 0; + +	//! The AISystem is the only class that should access the flags and path_index variables +	friend class AISystem; + +	//! The minimum amount of steps for the path following behavior +	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 diff --git a/src/crepe/api/CMakeLists.txt b/src/crepe/api/CMakeLists.txt index 718e497..46deb67 100644 --- a/src/crepe/api/CMakeLists.txt +++ b/src/crepe/api/CMakeLists.txt @@ -22,6 +22,7 @@ target_sources(crepe PUBLIC  	Script.cpp  	Button.cpp  	UIObject.cpp +	AI.cpp  )  target_sources(crepe PUBLIC FILE_SET HEADERS FILES @@ -53,4 +54,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 88243c4..1f0ba72 100644 --- a/src/crepe/api/LoopManager.cpp +++ b/src/crepe/api/LoopManager.cpp @@ -1,4 +1,6 @@ +#include "../system/AISystem.h"  #include "../system/AnimatorSystem.h" +#include "../system/AudioSystem.h"  #include "../system/CollisionSystem.h"  #include "../system/InputSystem.h"  #include "../system/ParticleSystem.h" @@ -20,6 +22,8 @@ LoopManager::LoopManager() {  	this->load_system<RenderSystem>();  	this->load_system<ScriptSystem>();  	this->load_system<InputSystem>(); +	this->load_system<AudioSystem>(); +	this->load_system<AISystem>();  }  void LoopManager::process_input() { this->get_system<InputSystem>().update(); } @@ -35,8 +39,10 @@ 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(); +	this->get_system<AudioSystem>().update();  }  void LoopManager::loop() { diff --git a/src/crepe/api/LoopManager.h b/src/crepe/api/LoopManager.h index b800f5b..7389124 100644 --- a/src/crepe/api/LoopManager.h +++ b/src/crepe/api/LoopManager.h @@ -4,6 +4,8 @@  #include "../facade/SDLContext.h"  #include "../manager/ComponentManager.h" +#include "../manager/ResourceManager.h" +#include "../manager/SaveManager.h"  #include "../manager/SceneManager.h"  #include "../system/System.h" @@ -96,6 +98,10 @@ private:  	ComponentManager component_manager{mediator};  	//! Scene manager instance  	SceneManager scene_manager{mediator}; +	//! Resource manager instance +	ResourceManager resource_manager{mediator}; +	//! Save manager instance +	SaveManager save_manager{mediator};  	SDLContext sdl_context {mediator}; diff --git a/src/crepe/api/Script.cpp b/src/crepe/api/Script.cpp index 4091fd4..753a9e3 100644 --- a/src/crepe/api/Script.cpp +++ b/src/crepe/api/Script.cpp @@ -8,8 +8,7 @@ using namespace crepe;  using namespace std;  Script::~Script() { -	Mediator & mediator = this->mediator; -	EventManager & mgr = mediator.event_manager; +	EventManager & mgr = this->mediator->event_manager;  	for (auto id : this->listeners) {  		mgr.unsubscribe(id);  	} @@ -21,7 +20,8 @@ void Script::subscribe(const EventHandler<CollisionEvent> & callback) {  }  void Script::set_next_scene(const string & name) { -	Mediator & mediator = this->mediator; -	SceneManager & mgr = mediator.scene_manager; +	SceneManager & mgr = this->mediator->scene_manager;  	mgr.set_next_scene(name);  } + +SaveManager & Script::get_save_manager() const { return this->mediator->save_manager; } diff --git a/src/crepe/api/Script.h b/src/crepe/api/Script.h index d99ab0e..668e5d1 100644 --- a/src/crepe/api/Script.h +++ b/src/crepe/api/Script.h @@ -86,6 +86,25 @@ protected:  	RefVector<T> get_components() const;  	/** +	 * \copydoc ComponentManager::get_components_by_id +	 * \see ComponentManager::get_components_by_id +	 */ +	template <typename T> +	RefVector<T> get_components_by_id(game_object_id_t id) const; +	/** +	 * \copydoc ComponentManager::get_components_by_name +	 * \see ComponentManager::get_components_by_name +	 */ +	template <typename T> +	RefVector<T> get_components_by_name(const std::string & name) const; +	/** +	 * \copydoc ComponentManager::get_components_by_tag +	 * \see ComponentManager::get_components_by_tag +	 */ +	template <typename T> +	RefVector<T> get_components_by_tag(const std::string & tag) const; + +	/**  	 * \brief Log a message using Log::logf  	 *  	 * \tparam Args Log::logf parameters @@ -113,6 +132,9 @@ protected:  	 */  	void set_next_scene(const std::string & name); +	//! Retrieve SaveManager reference +	SaveManager & get_save_manager() const; +  	//! \}  private: diff --git a/src/crepe/api/Script.hpp b/src/crepe/api/Script.hpp index 45f1ff1..225a51c 100644 --- a/src/crepe/api/Script.hpp +++ b/src/crepe/api/Script.hpp @@ -20,10 +20,7 @@ T & Script::get_component() const {  template <typename T>  RefVector<T> Script::get_components() const { -	Mediator & mediator = this->mediator; -	ComponentManager & mgr = mediator.component_manager; - -	return mgr.get_components_by_id<T>(this->game_object_id); +	return this->get_components_by_id<T>(this->game_object_id);  }  template <typename... Args> @@ -34,8 +31,7 @@ void Script::logf(Args &&... args) {  template <typename EventType>  void Script::subscribe_internal(const EventHandler<EventType> & callback,  								event_channel_t channel) { -	Mediator & mediator = this->mediator; -	EventManager & mgr = mediator.event_manager; +	EventManager & mgr = this->mediator->event_manager;  	subscription_t listener = mgr.subscribe<EventType>(  		[this, callback](const EventType & data) -> bool {  			bool & active = this->active; @@ -56,4 +52,26 @@ void Script::subscribe(const EventHandler<EventType> & callback) {  	this->subscribe_internal(callback, EventManager::CHANNEL_ALL);  } +template <typename T> +RefVector<T> Script::get_components_by_id(game_object_id_t id) const { +	Mediator & mediator = this->mediator; +	ComponentManager & mgr = mediator.component_manager; + +	return mgr.get_components_by_id<T>(id); +} +template <typename T> +RefVector<T> Script::get_components_by_name(const std::string & name) const { +	Mediator & mediator = this->mediator; +	ComponentManager & mgr = mediator.component_manager; + +	return mgr.get_components_by_name<T>(name); +} +template <typename T> +RefVector<T> Script::get_components_by_tag(const std::string & tag) const { +	Mediator & mediator = this->mediator; +	ComponentManager & mgr = mediator.component_manager; + +	return mgr.get_components_by_tag<T>(tag); +} +  } // 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 |