diff options
| -rw-r--r-- | src/crepe/api/AI.cpp | 23 | ||||
| -rw-r--r-- | src/crepe/api/AI.h | 50 | ||||
| -rw-r--r-- | src/crepe/system/AISystem.cpp | 1 | ||||
| -rw-r--r-- | src/crepe/system/AISystem.h | 44 | ||||
| -rw-r--r-- | src/example/AITest.cpp | 10 | 
5 files changed, 112 insertions, 16 deletions
| diff --git a/src/crepe/api/AI.cpp b/src/crepe/api/AI.cpp index d785bb5..49f6b92 100644 --- a/src/crepe/api/AI.cpp +++ b/src/crepe/api/AI.cpp @@ -4,4 +4,27 @@ 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, vec2 center, float start_angle, bool clockwise) { +	// The step size is determined by the radius (step size is in radians) +	float step = 400.0f / radius; +	// Force at least 16 steps (in case of a small radius) +	if (step > 2 * M_PI / 16) { +		step = 2 * M_PI / 16; +	} +	// The path node distance is determined by the step size and the radius +	path_node_distance = radius * step * 0.75f; + +	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))}); +		} +	} +} +  } // namespace crepe diff --git a/src/crepe/api/AI.h b/src/crepe/api/AI.h index 35b8998..9f5c0a8 100644 --- a/src/crepe/api/AI.h +++ b/src/crepe/api/AI.h @@ -5,8 +5,15 @@  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 BehaviorType {  		NONE = 0x00000,  		SEEK = 0x00002, @@ -16,53 +23,76 @@ public:  	};  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/activated +	 * +	 * \param behavior The behavior to check +	 * \return true if the behavior is on, false otherwise +	 */  	bool on(BehaviorType behavior) const { return (flags & behavior) == behavior; } +	//! Turn on the seek behavior  	void seek_on() { flags |= SEEK; } +	//! Turn off the seek behavior  	void seek_off() {  		if (on(SEEK)) flags ^= SEEK;  	} +	//! Turn on the flee behavior  	void flee_on() { flags |= FLEE; } +	//! Turn off the flee behavior  	void flee_off() {  		if (on(FLEE)) flags ^= FLEE;  	} +	//! Turn on the arrive behavior  	void arrive_on() { flags |= ARRIVE; } +	//! Turn off the arrive behavior  	void arrive_off() {  		if (on(ARRIVE)) 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() {  		if (on(PATH_FOLLOW)) flags ^= PATH_FOLLOW;  	} +	//! Add a path node to the path  	void add_path_node(vec2 node) { path.push_back(node); } +	//! Create a circle path +	void make_circle_path(float radius, vec2 center = {0, 0}, float start_angle = 0, +						  bool clockwise = true);  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 or arrive at +	//! The target to seek or arrive at  	vec2 seek_target; -	// The target to flee from +	//! The target to flee from  	vec2 flee_target; -	// The distance at which the entity will start to flee from the 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)) +	//! 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 +	//! The path to follow  	std::vector<vec2> path; -	// The distance from the path node at which the entity will move to the next node +	//! The distance from the path node at which the entity will move to the next node  	float path_node_distance = 400.0f; -	// Looping behavior for the path +	//! Looping behavior for the path  	bool path_loop = true;  private: -	// The flags for the behaviors +	//! The flags for the behaviors  	int flags = 0; -	// The current path index +	//! The current path index  	size_t path_index = 0; -	// The AISystem is the only class that can access the private members of AI +	//! The AISystem is the only class that should access the flags and path_index variables  	friend class AISystem;  }; diff --git a/src/crepe/system/AISystem.cpp b/src/crepe/system/AISystem.cpp index 55dc14c..7b801c3 100644 --- a/src/crepe/system/AISystem.cpp +++ b/src/crepe/system/AISystem.cpp @@ -17,6 +17,7 @@ void AISystem::update() {  	ComponentManager & mgr = mediator.component_manager;  	RefVector<AI> ai_components = mgr.get_components_by_type<AI>(); +	//TODO: Use fixed loop dt (this is not available at master at the moment)  	double dt = LoopTimer::get_instance().get_delta_time();  	for (AI & ai : ai_components) { diff --git a/src/crepe/system/AISystem.h b/src/crepe/system/AISystem.h index 27861d9..670d20d 100644 --- a/src/crepe/system/AISystem.h +++ b/src/crepe/system/AISystem.h @@ -7,19 +7,63 @@  namespace crepe { +/** + * \brief The AISystem is used to control the movement of entities using AI. + * + * The AISystem is used to control the movement of entities using AI. The AISystem can be used to + * implement different behaviors such as seeking, fleeing, arriving, and path following. + */  class AISystem : public System {  public:  	using System::System; +	//! Update the AI system  	void update() override;  private: +	/** +	 * \brief Calculate the total force to apply to the entity +	 * +	 * \param ai The AI component +	 */  	vec2 calculate(AI & ai); +	/** +	 * \brief Accumulate the force to apply to the entity +	 * +	 * \param ai The AI component +	 * \param running_total The running total of the force +	 * \param force_to_add The force to add +	 * \return true if the force was added, false otherwise +	 */  	bool accumulate_force(AI & ai, vec2 & running_total, vec2 force_to_add); +	/** +	 * \brief Calculate the seek force +	 * +	 * \param ai The AI component +	 * \return The seek force +	 */  	vec2 seek(const AI & ai); +	/** +	 * \brief Calculate the flee force +	 * +	 * \param ai The AI component +	 * \return The flee force +	 */  	vec2 flee(const AI & ai); +	/** +	 * \brief Calculate the arrive force +	 * +	 * \param ai The AI component +	 * \return The arrive force +	 */  	vec2 arrive(const AI & ai); +	/** +	 * \brief Calculate the path follow force +	 * +	 * \param ai The AI component +	 * \return The path follow force +	 */  	vec2 path_follow(AI & ai);  }; diff --git a/src/example/AITest.cpp b/src/example/AITest.cpp index d12a99a..72e06cf 100644 --- a/src/example/AITest.cpp +++ b/src/example/AITest.cpp @@ -52,16 +52,14 @@ public:  		Texture img = Texture("asset/texture/test_ap43.png");  		game_object1.add_component<Sprite>(img, Color::MAGENTA,  										   Sprite::FlipSettings{false, false}, 1, 1, 195); -		AI & ai = game_object1.add_component<AI>(300); +		AI & ai = game_object1.add_component<AI>(3000);  		// ai.arrive_on();  		// ai.flee_on();  		ai.path_follow_on(); -		ai.add_path_node(vec2{1200, 1200}); -		ai.add_path_node(vec2{-1200, 1200}); -		ai.add_path_node(vec2{1200, -1200}); -		ai.add_path_node(vec2{-1200, -1200}); +		ai.make_circle_path(1000, {0, -1000}, 1.5707, true); +		ai.make_circle_path(1000, {0, 1000}, 4.7124, false);  		game_object1.add_component<Rigidbody>(Rigidbody::Data{ -			.mass = 0.5f, .max_linear_velocity = {50, 50}, // sqrt(21^2 + 21^2) = 30 +			.mass = 0.5f, .max_linear_velocity = {40, 40}, // sqrt(21^2 + 21^2) = 30  		});  		game_object1.add_component<BehaviorScript>().set_script<Script1>(); |