From 9fed52de03d0638c99c584702ba4a19816a04c45 Mon Sep 17 00:00:00 2001 From: Relintai Date: Fri, 13 Jan 2023 21:13:57 +0100 Subject: [PATCH] Added a new steering_ai module. It's a modified verion of https://github.com/GDQuest/godot-steering-ai-framework which I converted to c++ using thr converter srcipt. It still needs to be cleaned. --- .../Agents/gsaikinematicbody2dagent.cpp | 296 +++++++++++++++ .../Agents/gsaikinematicbody2dagent.h | 63 ++++ .../Agents/gsaikinematicbody3dagent.cpp | 296 +++++++++++++++ .../Agents/gsaikinematicbody3dagent.h | 63 ++++ .../Agents/gsairigidbody2dagent.cpp | 168 +++++++++ .../steering_ai/Agents/gsairigidbody2dagent.h | 43 +++ .../Agents/gsairigidbody3dagent.cpp | 168 +++++++++ .../steering_ai/Agents/gsairigidbody3dagent.h | 43 +++ .../Agents/gsaispecializedagent.cpp | 164 +++++++++ .../steering_ai/Agents/gsaispecializedagent.h | 70 ++++ modules/steering_ai/Behaviors/gsaiarrive.cpp | 130 +++++++ modules/steering_ai/Behaviors/gsaiarrive.h | 47 +++ .../Behaviors/gsaiavoidcollisions.cpp | 198 ++++++++++ .../Behaviors/gsaiavoidcollisions.h | 52 +++ modules/steering_ai/Behaviors/gsaiblend.cpp | 126 +++++++ modules/steering_ai/Behaviors/gsaiblend.h | 46 +++ .../steering_ai/Behaviors/gsaicohesion.cpp | 63 ++++ modules/steering_ai/Behaviors/gsaicohesion.h | 29 ++ modules/steering_ai/Behaviors/gsaievade.cpp | 29 ++ modules/steering_ai/Behaviors/gsaievade.h | 24 ++ modules/steering_ai/Behaviors/gsaiface.cpp | 63 ++++ modules/steering_ai/Behaviors/gsaiface.h | 26 ++ modules/steering_ai/Behaviors/gsaiflee.cpp | 29 ++ modules/steering_ai/Behaviors/gsaiflee.h | 23 ++ .../steering_ai/Behaviors/gsaifollowpath.cpp | 146 ++++++++ .../steering_ai/Behaviors/gsaifollowpath.h | 44 +++ .../Behaviors/gsailookwhereyougo.cpp | 49 +++ .../Behaviors/gsailookwhereyougo.h | 24 ++ .../Behaviors/gsaimatchorientation.cpp | 154 ++++++++ .../Behaviors/gsaimatchorientation.h | 54 +++ .../steering_ai/Behaviors/gsaipriority.cpp | 143 ++++++++ modules/steering_ai/Behaviors/gsaipriority.h | 46 +++ modules/steering_ai/Behaviors/gsaipursue.cpp | 92 +++++ modules/steering_ai/Behaviors/gsaipursue.h | 37 ++ modules/steering_ai/Behaviors/gsaiseek.cpp | 47 +++ modules/steering_ai/Behaviors/gsaiseek.h | 29 ++ .../steering_ai/Behaviors/gsaiseparation.cpp | 88 +++++ .../steering_ai/Behaviors/gsaiseparation.h | 41 +++ modules/steering_ai/COPYRIGHT.txt | 43 +++ modules/steering_ai/LICENSE | 22 ++ .../Proximities/gsaiinfiniteproximity.cpp | 50 +++ .../Proximities/gsaiinfiniteproximity.h | 29 ++ .../steering_ai/Proximities/gsaiproximity.cpp | 74 ++++ .../steering_ai/Proximities/gsaiproximity.h | 40 ++ .../Proximities/gsairadiusproximity.cpp | 149 ++++++++ .../Proximities/gsairadiusproximity.h | 43 +++ modules/steering_ai/SCsub | 13 + modules/steering_ai/config.py | 16 + modules/steering_ai/gsaiagentlocation.cpp | 56 +++ modules/steering_ai/gsaiagentlocation.h | 32 ++ modules/steering_ai/gsaigroupbehavior.cpp | 70 ++++ modules/steering_ai/gsaigroupbehavior.h | 36 ++ modules/steering_ai/gsaipath.cpp | 344 ++++++++++++++++++ modules/steering_ai/gsaipath.h | 86 +++++ modules/steering_ai/gsaisteeringagent.cpp | 180 +++++++++ modules/steering_ai/gsaisteeringagent.h | 72 ++++ modules/steering_ai/gsaisteeringbehavior.cpp | 84 +++++ modules/steering_ai/gsaisteeringbehavior.h | 41 +++ .../steering_ai/gsaitargetacceleration.cpp | 89 +++++ modules/steering_ai/gsaitargetacceleration.h | 41 +++ modules/steering_ai/gsaiutils.cpp | 71 ++++ modules/steering_ai/gsaiutils.h | 38 ++ modules/steering_ai/register_types.cpp | 32 ++ modules/steering_ai/register_types.h | 25 ++ 64 files changed, 5029 insertions(+) create mode 100644 modules/steering_ai/Agents/gsaikinematicbody2dagent.cpp create mode 100644 modules/steering_ai/Agents/gsaikinematicbody2dagent.h create mode 100644 modules/steering_ai/Agents/gsaikinematicbody3dagent.cpp create mode 100644 modules/steering_ai/Agents/gsaikinematicbody3dagent.h create mode 100644 modules/steering_ai/Agents/gsairigidbody2dagent.cpp create mode 100644 modules/steering_ai/Agents/gsairigidbody2dagent.h create mode 100644 modules/steering_ai/Agents/gsairigidbody3dagent.cpp create mode 100644 modules/steering_ai/Agents/gsairigidbody3dagent.h create mode 100644 modules/steering_ai/Agents/gsaispecializedagent.cpp create mode 100644 modules/steering_ai/Agents/gsaispecializedagent.h create mode 100644 modules/steering_ai/Behaviors/gsaiarrive.cpp create mode 100644 modules/steering_ai/Behaviors/gsaiarrive.h create mode 100644 modules/steering_ai/Behaviors/gsaiavoidcollisions.cpp create mode 100644 modules/steering_ai/Behaviors/gsaiavoidcollisions.h create mode 100644 modules/steering_ai/Behaviors/gsaiblend.cpp create mode 100644 modules/steering_ai/Behaviors/gsaiblend.h create mode 100644 modules/steering_ai/Behaviors/gsaicohesion.cpp create mode 100644 modules/steering_ai/Behaviors/gsaicohesion.h create mode 100644 modules/steering_ai/Behaviors/gsaievade.cpp create mode 100644 modules/steering_ai/Behaviors/gsaievade.h create mode 100644 modules/steering_ai/Behaviors/gsaiface.cpp create mode 100644 modules/steering_ai/Behaviors/gsaiface.h create mode 100644 modules/steering_ai/Behaviors/gsaiflee.cpp create mode 100644 modules/steering_ai/Behaviors/gsaiflee.h create mode 100644 modules/steering_ai/Behaviors/gsaifollowpath.cpp create mode 100644 modules/steering_ai/Behaviors/gsaifollowpath.h create mode 100644 modules/steering_ai/Behaviors/gsailookwhereyougo.cpp create mode 100644 modules/steering_ai/Behaviors/gsailookwhereyougo.h create mode 100644 modules/steering_ai/Behaviors/gsaimatchorientation.cpp create mode 100644 modules/steering_ai/Behaviors/gsaimatchorientation.h create mode 100644 modules/steering_ai/Behaviors/gsaipriority.cpp create mode 100644 modules/steering_ai/Behaviors/gsaipriority.h create mode 100644 modules/steering_ai/Behaviors/gsaipursue.cpp create mode 100644 modules/steering_ai/Behaviors/gsaipursue.h create mode 100644 modules/steering_ai/Behaviors/gsaiseek.cpp create mode 100644 modules/steering_ai/Behaviors/gsaiseek.h create mode 100644 modules/steering_ai/Behaviors/gsaiseparation.cpp create mode 100644 modules/steering_ai/Behaviors/gsaiseparation.h create mode 100644 modules/steering_ai/COPYRIGHT.txt create mode 100644 modules/steering_ai/LICENSE create mode 100644 modules/steering_ai/Proximities/gsaiinfiniteproximity.cpp create mode 100644 modules/steering_ai/Proximities/gsaiinfiniteproximity.h create mode 100644 modules/steering_ai/Proximities/gsaiproximity.cpp create mode 100644 modules/steering_ai/Proximities/gsaiproximity.h create mode 100644 modules/steering_ai/Proximities/gsairadiusproximity.cpp create mode 100644 modules/steering_ai/Proximities/gsairadiusproximity.h create mode 100644 modules/steering_ai/SCsub create mode 100644 modules/steering_ai/config.py create mode 100644 modules/steering_ai/gsaiagentlocation.cpp create mode 100644 modules/steering_ai/gsaiagentlocation.h create mode 100644 modules/steering_ai/gsaigroupbehavior.cpp create mode 100644 modules/steering_ai/gsaigroupbehavior.h create mode 100644 modules/steering_ai/gsaipath.cpp create mode 100644 modules/steering_ai/gsaipath.h create mode 100644 modules/steering_ai/gsaisteeringagent.cpp create mode 100644 modules/steering_ai/gsaisteeringagent.h create mode 100644 modules/steering_ai/gsaisteeringbehavior.cpp create mode 100644 modules/steering_ai/gsaisteeringbehavior.h create mode 100644 modules/steering_ai/gsaitargetacceleration.cpp create mode 100644 modules/steering_ai/gsaitargetacceleration.h create mode 100644 modules/steering_ai/gsaiutils.cpp create mode 100644 modules/steering_ai/gsaiutils.h create mode 100644 modules/steering_ai/register_types.cpp create mode 100644 modules/steering_ai/register_types.h diff --git a/modules/steering_ai/Agents/gsaikinematicbody2dagent.cpp b/modules/steering_ai/Agents/gsaikinematicbody2dagent.cpp new file mode 100644 index 000000000..9e4a400aa --- /dev/null +++ b/modules/steering_ai/Agents/gsaikinematicbody2dagent.cpp @@ -0,0 +1,296 @@ + +#include "gsaikinematicbody2dagent.h" + + +KinematicBody2D GSAIKinematicBody2DAgent::get_*body() { + return *body; +} + +void GSAIKinematicBody2DAgent::set_*body(const KinematicBody2D &val) { +*body = val; +} + + +int GSAIKinematicBody2DAgent::get_movement_type() const { + return movement_type; +} + +void GSAIKinematicBody2DAgent::set_movement_type(const int val) { +movement_type = val; +} + + +Vector2 GSAIKinematicBody2DAgent::get__last_position() { + return _last_position; +} + +void GSAIKinematicBody2DAgent::set__last_position(const Vector2 &val) { +_last_position = val; +} + + +Ref GSAIKinematicBody2DAgent::get__body_ref() { + return _body_ref; +} + +void GSAIKinematicBody2DAgent::set__body_ref(const Ref &val) { +_body_ref = val; +} + + + + // A specialized steering agent that updates itself every frame so the user does; + // not have to using a KinematicBody2D; + // @category - Specialized agents; + // SLIDE uses `move_and_slide`; + // COLLIDE uses `move_and_collide`; + // POSITION changes the `global_position` directly; + }; + // The KinematicBody2D to keep track of; + // setget _set_body; + KinematicBody2D *body; + // The type of movement the body executes; + int movement_type = MovementType.SLIDE; + Vector2 _last_position = ; + Ref _body_ref; + + void GSAIKinematicBody2DAgent::_body_ready() { + // warning-ignore:return_value_discarded; + body.get_tree().connect("physics_frame", self, "_on_SceneTree_physics_frame"); +} + + // Moves the agent's `body` by target `acceleration`.; + // @tags - virtual; + + void GSAIKinematicBody2DAgent::_apply_steering(const GSAITargetAcceleration &acceleration, const float delta) { + applied_steering = true; + + if (movement_type == MovementType.COLLIDE) { + _apply_collide_steering(acceleration.linear, delta); +} + + + else if (movement_type == MovementType.SLIDE) { + _apply_sliding_steering(acceleration.linear, delta); +} + + + else { + _apply_position_steering(acceleration.linear, delta); +} + + _apply_orientation_steering(acceleration.angular, delta); +} + + + void GSAIKinematicBody2DAgent::_apply_sliding_steering(const Vector3 &accel, const float delta) { + KinematicBody2D *_body = _body_ref.get_ref(); + + if (!_body) { + return; +} + + + if (!_body.is_inside_tree() || _body.get_tree().paused) { + return; +} + + Vector2 velocity = GSAIUtils.to_vector2(linear_velocity + accel * delta).clamped(linear_speed_max); + + if (apply_linear_drag) { + velocity = velocity.linear_interpolate(Vector2.ZERO, linear_drag_percentage); +} + + velocity = _body.move_and_slide(velocity); + + if (calculate_velocities) { + linear_velocity = GSAIUtils.to_vector3(velocity); +} + +} + + + void GSAIKinematicBody2DAgent::_apply_collide_steering(const Vector3 &accel, const float delta) { + KinematicBody2D *_body = _body_ref.get_ref(); + + if (!_body) { + return; +} + + Vector3 velocity = GSAIUtils.clampedv3(linear_velocity + accel * delta, linear_speed_max); + + if (apply_linear_drag) { + velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage); +} + + // warning-ignore:return_value_discarded; + _body.move_and_collide(GSAIUtils.to_vector2(velocity) * delta); + + if (calculate_velocities) { + linear_velocity = velocity; +} + +} + + + void GSAIKinematicBody2DAgent::_apply_position_steering(const Vector3 &accel, const float delta) { + KinematicBody2D *_body = _body_ref.get_ref(); + + if (!_body) { + return; +} + + Vector3 velocity = GSAIUtils.clampedv3(linear_velocity + accel * delta, linear_speed_max); + + if (apply_linear_drag) { + velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage); +} + + _body.global_position += GSAIUtils.to_vector2(velocity) * delta; + + if (calculate_velocities) { + linear_velocity = velocity; +} + +} + + + void GSAIKinematicBody2DAgent::_apply_orientation_steering(const float angular_acceleration, const float delta) { + KinematicBody2D *_body = _body_ref.get_ref(); + + if (!_body) { + return; +} + + Variant = clamp(angular_velocity + angular_acceleration * delta, -angular_speed_max, angular_speed_max); + + if (apply_angular_drag) { + velocity = lerp(velocity, 0, angular_drag_percentage); +} + + _body.rotation += velocity * delta; + + if (calculate_velocities) { + angular_velocity = velocity; +} + +} + + + void GSAIKinematicBody2DAgent::_set_body(const KinematicBody2D &value) { + bool had_body = false; + + if (body) { + had_body = true; +} + + body = value; + _body_ref = weakref(body); + _last_position = value.global_position; + last_orientation = value.rotation; + position = GSAIUtils.to_vector3(_last_position); + orientation = last_orientation; + + if (!had_body) { + + if (!body.is_inside_tree()) { + body.connect("ready", self, "_body_ready"); +} + + + else { + _body_ready(); +} + +} + +} + + + void GSAIKinematicBody2DAgent::_on_SceneTree_physics_frame() { + KinematicBody2D *_body = _body_ref.get_ref(); + + if (!_body) { + return; +} + + Vector2 current_position = _body.global_position; + float current_orientation = _body.rotation; + position = GSAIUtils.to_vector3(current_position); + orientation = current_orientation; + + if (calculate_velocities) { + + if (applied_steering) { + applied_steering = false; +} + + + else { + linear_velocity = GSAIUtils.clampedv3(GSAIUtils.to_vector3(current_position - _last_position), linear_speed_max); + + if (apply_linear_drag) { + linear_velocity = linear_velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage); +} + + angular_velocity = clamp(last_orientation - current_orientation, -angular_speed_max, angular_speed_max); + + if (apply_angular_drag) { + angular_velocity = lerp(angular_velocity, 0, angular_drag_percentage); +} + + _last_position = current_position; + last_orientation = current_orientation; +} + +} + +} + +} + + GSAIKinematicBody2DAgent::GSAIKinematicBody2DAgent() { + *body; + movement_type = MovementType.SLIDE; + _last_position = ; + _body_ref; + } + + GSAIKinematicBody2DAgent::~GSAIKinematicBody2DAgent() { + } + + + static void GSAIKinematicBody2DAgent::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_*body"), &GSAIKinematicBody2DAgent::get_*body); + ClassDB::bind_method(D_METHOD("set_*body", "value"), &GSAIKinematicBody2DAgent::set_*body); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*body", PROPERTY_HINT_RESOURCE_TYPE, "KinematicBody2D"), "set_*body", "get_*body"); + + + ClassDB::bind_method(D_METHOD("get_movement_type"), &GSAIKinematicBody2DAgent::get_movement_type); + ClassDB::bind_method(D_METHOD("set_movement_type", "value"), &GSAIKinematicBody2DAgent::set_movement_type); + ADD_PROPERTY(PropertyInfo(Variant::INT, "movement_type"), "set_movement_type", "get_movement_type"); + + + ClassDB::bind_method(D_METHOD("get__last_position"), &GSAIKinematicBody2DAgent::get__last_position); + ClassDB::bind_method(D_METHOD("set__last_position", "value"), &GSAIKinematicBody2DAgent::set__last_position); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "_last_position"), "set__last_position", "get__last_position"); + + + ClassDB::bind_method(D_METHOD("get__body_ref"), &GSAIKinematicBody2DAgent::get__body_ref); + ClassDB::bind_method(D_METHOD("set__body_ref", "value"), &GSAIKinematicBody2DAgent::set__body_ref); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "_body_ref", PROPERTY_HINT_RESOURCE_TYPE, "Ref"), "set__body_ref", "get__body_ref"); + + + ClassDB::bind_method(D_METHOD("_body_ready"), &GSAIKinematicBody2DAgent::_body_ready); + ClassDB::bind_method(D_METHOD("_apply_steering", "acceleration", "delta"), &GSAIKinematicBody2DAgent::_apply_steering); + ClassDB::bind_method(D_METHOD("_apply_sliding_steering", "accel", "delta"), &GSAIKinematicBody2DAgent::_apply_sliding_steering); + ClassDB::bind_method(D_METHOD("_apply_collide_steering", "accel", "delta"), &GSAIKinematicBody2DAgent::_apply_collide_steering); + ClassDB::bind_method(D_METHOD("_apply_position_steering", "accel", "delta"), &GSAIKinematicBody2DAgent::_apply_position_steering); + ClassDB::bind_method(D_METHOD("_apply_orientation_steering", "angular_acceleration", "delta"), &GSAIKinematicBody2DAgent::_apply_orientation_steering); + ClassDB::bind_method(D_METHOD("_set_body", "value"), &GSAIKinematicBody2DAgent::_set_body); + ClassDB::bind_method(D_METHOD("_on_SceneTree_physics_frame"), &GSAIKinematicBody2DAgent::_on_SceneTree_physics_frame); + + } + + + diff --git a/modules/steering_ai/Agents/gsaikinematicbody2dagent.h b/modules/steering_ai/Agents/gsaikinematicbody2dagent.h new file mode 100644 index 000000000..0b882e44b --- /dev/null +++ b/modules/steering_ai/Agents/gsaikinematicbody2dagent.h @@ -0,0 +1,63 @@ +#ifndef GSAIKINEMATICBODY2DAGENT_H +#define GSAIKINEMATICBODY2DAGENT_H + + +class GSAIKinematicBody2DAgent : public GSAISpecializedAgent { + GDCLASS(GSAIKinematicBody2DAgent, GSAISpecializedAgent); + + public: + + KinematicBody2D get_*body(); + void set_*body(const KinematicBody2D &val); + + int get_movement_type() const; + void set_movement_type(const int val); + + Vector2 get__last_position(); + void set__last_position(const Vector2 &val); + + Ref get__body_ref(); + void set__body_ref(const Ref &val); + + enum MovementType { + + SLIDE, + COLLIDE, + POSITION +}; + + void _body_ready(); + void _apply_steering(const GSAITargetAcceleration &acceleration, const float delta); + void _apply_sliding_steering(const Vector3 &accel, const float delta); + void _apply_collide_steering(const Vector3 &accel, const float delta); + void _apply_position_steering(const Vector3 &accel, const float delta); + void _apply_orientation_steering(const float angular_acceleration, const float delta); + void _set_body(const KinematicBody2D &value); + void _on_SceneTree_physics_frame(); + + GSAIKinematicBody2DAgent(); + ~GSAIKinematicBody2DAgent(); + + protected: + static void _bind_methods(); + + // A specialized steering agent that updates itself every frame so the user does + // not have to using a KinematicBody2D + // @category - Specialized agents + // SLIDE uses `move_and_slide` + // COLLIDE uses `move_and_collide` + // POSITION changes the `global_position` directly + }; + // The KinematicBody2D to keep track of + // setget _set_body + KinematicBody2D *body; + // The type of movement the body executes + int movement_type = MovementType.SLIDE; + Vector2 _last_position = ; + Ref _body_ref; + // Moves the agent's `body` by target `acceleration`. + // @tags - virtual +}; + + +#endif diff --git a/modules/steering_ai/Agents/gsaikinematicbody3dagent.cpp b/modules/steering_ai/Agents/gsaikinematicbody3dagent.cpp new file mode 100644 index 000000000..866eb6c4b --- /dev/null +++ b/modules/steering_ai/Agents/gsaikinematicbody3dagent.cpp @@ -0,0 +1,296 @@ + +#include "gsaikinematicbody3dagent.h" + + +KinematicBody GSAIKinematicBody3DAgent::get_*body() { + return *body; +} + +void GSAIKinematicBody3DAgent::set_*body(const KinematicBody &val) { +*body = val; +} + + +int GSAIKinematicBody3DAgent::get_movement_type() const { + return movement_type; +} + +void GSAIKinematicBody3DAgent::set_movement_type(const int val) { +movement_type = val; +} + + +Vector3 GSAIKinematicBody3DAgent::get__last_position() { + return _last_position; +} + +void GSAIKinematicBody3DAgent::set__last_position(const Vector3 &val) { +_last_position = val; +} + + +Ref GSAIKinematicBody3DAgent::get__body_ref() { + return _body_ref; +} + +void GSAIKinematicBody3DAgent::set__body_ref(const Ref &val) { +_body_ref = val; +} + + + + // A specialized steering agent that updates itself every frame so the user does; + // not have to using a KinematicBody; + // @category - Specialized agents; + // SLIDE uses `move_and_slide`; + // COLLIDE uses `move_and_collide`; + // POSITION changes the global_position directly; + }; + // The KinematicBody to keep track of; + // setget _set_body; + KinematicBody *body; + // The type of movement the body executes; + int movement_type = 0; + Vector3 _last_position = ; + Ref _body_ref; + + void GSAIKinematicBody3DAgent::_body_ready() { + // warning-ignore:return_value_discarded; + body.get_tree().connect("physics_frame", self, "_on_SceneTree_physics_frame"); +} + + // Moves the agent's `body` by target `acceleration`.; + // @tags - virtual; + + void GSAIKinematicBody3DAgent::_apply_steering(const GSAITargetAcceleration &acceleration, const float delta) { + applied_steering = true; + + if (movement_type == MovementType.COLLIDE) { + _apply_collide_steering(acceleration.linear, delta); +} + + + else if (movement_type == MovementType.SLIDE) { + _apply_sliding_steering(acceleration.linear, delta); +} + + + else { + _apply_position_steering(acceleration.linear, delta); +} + + _apply_orientation_steering(acceleration.angular, delta); +} + + + void GSAIKinematicBody3DAgent::_apply_sliding_steering(const Vector3 &accel, const float delta) { + KinematicBody *_body = _body_ref.get_ref(); + + if (!_body) { + return; +} + + Vector3 velocity = GSAIUtils.clampedv3(linear_velocity + accel * delta, linear_speed_max); + + if (apply_linear_drag) { + velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage); +} + + velocity = _body.move_and_slide(velocity); + + if (calculate_velocities) { + linear_velocity = velocity; +} + +} + + + void GSAIKinematicBody3DAgent::_apply_collide_steering(const Vector3 &accel, const float delta) { + KinematicBody *_body = _body_ref.get_ref(); + + if (!_body) { + return; +} + + Vector3 velocity = GSAIUtils.clampedv3(linear_velocity + accel * delta, linear_speed_max); + + if (apply_linear_drag) { + velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage); +} + + // warning-ignore:return_value_discarded; + _body.move_and_collide(velocity * delta); + + if (calculate_velocities) { + linear_velocity = velocity; +} + +} + + + void GSAIKinematicBody3DAgent::_apply_position_steering(const Vector3 &accel, const float delta) { + KinematicBody *_body = _body_ref.get_ref(); + + if (!_body) { + return; +} + + Vector3 velocity = GSAIUtils.clampedv3(linear_velocity + accel * delta, linear_speed_max); + + if (apply_linear_drag) { + velocity = velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage); +} + + _body.global_transform.origin += velocity * delta; + + if (calculate_velocities) { + linear_velocity = velocity; +} + +} + + + void GSAIKinematicBody3DAgent::_apply_orientation_steering(const float angular_acceleration, const float delta) { + KinematicBody *_body = _body_ref.get_ref(); + + if (!_body) { + return; +} + + Variant = clamp(angular_velocity + angular_acceleration * delta,-angular_speed_max,angular_speed_max); + + if (apply_angular_drag) { + velocity = lerp(velocity, 0, angular_drag_percentage); +} + + _body.rotation.y += velocity * delta; + + if (calculate_velocities) { + angular_velocity = velocity; +} + +} + + + void GSAIKinematicBody3DAgent::_set_body(const KinematicBody &value) { + bool had_body = false; + + if (body) { + had_body = true; +} + + body = value; + _body_ref = weakref(value); + _last_position = value.transform.origin; + last_orientation = value.rotation.y; + position = _last_position; + orientation = last_orientation; + + if (!had_body) { + + if (!body.is_inside_tree()) { + body.connect("ready", self, "_body_ready"); +} + + + else { + _body_ready(); +} + +} + +} + + + void GSAIKinematicBody3DAgent::_on_SceneTree_physics_frame() { + KinematicBody *_body = _body_ref.get_ref(); + + if (!_body) { + return; +} + + + if (!_body.is_inside_tree() || _body.get_tree().paused) { + return; +} + + Vector3 current_position = _body.transform.origin; + float current_orientation = _body.rotation.y; + position = current_position; + orientation = current_orientation; + + if (calculate_velocities) { + + if (applied_steering) { + applied_steering = false; +} + + + else { + linear_velocity = GSAIUtils.clampedv3(current_position - _last_position, linear_speed_max); + + if (apply_linear_drag) { + linear_velocity = linear_velocity.linear_interpolate(Vector3.ZERO, linear_drag_percentage); +} + + angular_velocity = clamp(last_orientation - current_orientation,-angular_speed_max,angular_speed_max); + + if (apply_angular_drag) { + angular_velocity = lerp(angular_velocity, 0, angular_drag_percentage); +} + + _last_position = current_position; + last_orientation = current_orientation; +} + +} + +} + +} + + GSAIKinematicBody3DAgent::GSAIKinematicBody3DAgent() { + *body; + movement_type = 0; + _last_position = ; + _body_ref; + } + + GSAIKinematicBody3DAgent::~GSAIKinematicBody3DAgent() { + } + + + static void GSAIKinematicBody3DAgent::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_*body"), &GSAIKinematicBody3DAgent::get_*body); + ClassDB::bind_method(D_METHOD("set_*body", "value"), &GSAIKinematicBody3DAgent::set_*body); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*body", PROPERTY_HINT_RESOURCE_TYPE, "KinematicBody"), "set_*body", "get_*body"); + + + ClassDB::bind_method(D_METHOD("get_movement_type"), &GSAIKinematicBody3DAgent::get_movement_type); + ClassDB::bind_method(D_METHOD("set_movement_type", "value"), &GSAIKinematicBody3DAgent::set_movement_type); + ADD_PROPERTY(PropertyInfo(Variant::INT, "movement_type"), "set_movement_type", "get_movement_type"); + + + ClassDB::bind_method(D_METHOD("get__last_position"), &GSAIKinematicBody3DAgent::get__last_position); + ClassDB::bind_method(D_METHOD("set__last_position", "value"), &GSAIKinematicBody3DAgent::set__last_position); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "_last_position"), "set__last_position", "get__last_position"); + + + ClassDB::bind_method(D_METHOD("get__body_ref"), &GSAIKinematicBody3DAgent::get__body_ref); + ClassDB::bind_method(D_METHOD("set__body_ref", "value"), &GSAIKinematicBody3DAgent::set__body_ref); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "_body_ref", PROPERTY_HINT_RESOURCE_TYPE, "Ref"), "set__body_ref", "get__body_ref"); + + + ClassDB::bind_method(D_METHOD("_body_ready"), &GSAIKinematicBody3DAgent::_body_ready); + ClassDB::bind_method(D_METHOD("_apply_steering", "acceleration", "delta"), &GSAIKinematicBody3DAgent::_apply_steering); + ClassDB::bind_method(D_METHOD("_apply_sliding_steering", "accel", "delta"), &GSAIKinematicBody3DAgent::_apply_sliding_steering); + ClassDB::bind_method(D_METHOD("_apply_collide_steering", "accel", "delta"), &GSAIKinematicBody3DAgent::_apply_collide_steering); + ClassDB::bind_method(D_METHOD("_apply_position_steering", "accel", "delta"), &GSAIKinematicBody3DAgent::_apply_position_steering); + ClassDB::bind_method(D_METHOD("_apply_orientation_steering", "angular_acceleration", "delta"), &GSAIKinematicBody3DAgent::_apply_orientation_steering); + ClassDB::bind_method(D_METHOD("_set_body", "value"), &GSAIKinematicBody3DAgent::_set_body); + ClassDB::bind_method(D_METHOD("_on_SceneTree_physics_frame"), &GSAIKinematicBody3DAgent::_on_SceneTree_physics_frame); + + } + + + diff --git a/modules/steering_ai/Agents/gsaikinematicbody3dagent.h b/modules/steering_ai/Agents/gsaikinematicbody3dagent.h new file mode 100644 index 000000000..d6e96e083 --- /dev/null +++ b/modules/steering_ai/Agents/gsaikinematicbody3dagent.h @@ -0,0 +1,63 @@ +#ifndef GSAIKINEMATICBODY3DAGENT_H +#define GSAIKINEMATICBODY3DAGENT_H + + +class GSAIKinematicBody3DAgent : public GSAISpecializedAgent { + GDCLASS(GSAIKinematicBody3DAgent, GSAISpecializedAgent); + + public: + + KinematicBody get_*body(); + void set_*body(const KinematicBody &val); + + int get_movement_type() const; + void set_movement_type(const int val); + + Vector3 get__last_position(); + void set__last_position(const Vector3 &val); + + Ref get__body_ref(); + void set__body_ref(const Ref &val); + + enum MovementType { + + SLIDE, + COLLIDE, + POSITION +}; + + void _body_ready(); + void _apply_steering(const GSAITargetAcceleration &acceleration, const float delta); + void _apply_sliding_steering(const Vector3 &accel, const float delta); + void _apply_collide_steering(const Vector3 &accel, const float delta); + void _apply_position_steering(const Vector3 &accel, const float delta); + void _apply_orientation_steering(const float angular_acceleration, const float delta); + void _set_body(const KinematicBody &value); + void _on_SceneTree_physics_frame(); + + GSAIKinematicBody3DAgent(); + ~GSAIKinematicBody3DAgent(); + + protected: + static void _bind_methods(); + + // A specialized steering agent that updates itself every frame so the user does + // not have to using a KinematicBody + // @category - Specialized agents + // SLIDE uses `move_and_slide` + // COLLIDE uses `move_and_collide` + // POSITION changes the global_position directly + }; + // The KinematicBody to keep track of + // setget _set_body + KinematicBody *body; + // The type of movement the body executes + int movement_type = 0; + Vector3 _last_position = ; + Ref _body_ref; + // Moves the agent's `body` by target `acceleration`. + // @tags - virtual +}; + + +#endif diff --git a/modules/steering_ai/Agents/gsairigidbody2dagent.cpp b/modules/steering_ai/Agents/gsairigidbody2dagent.cpp new file mode 100644 index 000000000..c9aae3707 --- /dev/null +++ b/modules/steering_ai/Agents/gsairigidbody2dagent.cpp @@ -0,0 +1,168 @@ + +#include "gsairigidbody2dagent.h" + + +RigidBody2D GSAIRigidBody2DAgent::get_*body() { + return *body; +} + +void GSAIRigidBody2DAgent::set_*body(const RigidBody2D &val) { +*body = val; +} + + +Vector2 GSAIRigidBody2DAgent::get__last_position() { + return _last_position; +} + +void GSAIRigidBody2DAgent::set__last_position(const Vector2 &val) { +_last_position = val; +} + + +Ref GSAIRigidBody2DAgent::get__body_ref() { + return _body_ref; +} + +void GSAIRigidBody2DAgent::set__body_ref(const Ref &val) { +_body_ref = val; +} + + + + // A specialized steering agent that updates itself every frame so the user does; + // not have to using a RigidBody2D; + // @category - Specialized agents; + // The RigidBody2D to keep track of; + // setget _set_body; + RigidBody2D *body; + Vector2 _last_position = ; + Ref _body_ref; + + void GSAIRigidBody2DAgent::_body_ready() { + // warning-ignore:return_value_discarded; + body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame"); +} + + // Moves the agent's `body` by target `acceleration`.; + // @tags - virtual; + + void GSAIRigidBody2DAgent::_apply_steering(const GSAITargetAcceleration &acceleration, const float _delta) { + RigidBody2D *_body = _body_ref.get_ref(); + + if (not _body) { + return; +} + + applied_steering = true; + _body.apply_central_impulse(GSAIUtils.to_vector2(acceleration.linear)); + _body.apply_torque_impulse(acceleration.angular); + + if (calculate_velocities) { + linear_velocity = GSAIUtils.to_vector3(_body.linear_velocity); + angular_velocity = _body.angular_velocity; +} + +} + + + void GSAIRigidBody2DAgent::_set_body(const RigidBody2D &value) { + bool had_body = false; + + if (body) { + had_body = true; +} + + body = value; + _body_ref = weakref(value); + _last_position = value.global_position; + last_orientation = value.rotation; + position = GSAIUtils.to_vector3(_last_position); + orientation = last_orientation; + + if (!had_body) { + + if (!body.is_inside_tree()) { + body.connect("ready", self, "_body_ready"); +} + + + else { + _body_ready(); +} + +} + +} + + + void GSAIRigidBody2DAgent::_on_SceneTree_frame() { + RigidBody2D *_body = _body_ref.get_ref(); + + if (!_body) { + return; +} + + + if (!_body.is_inside_tree() || _body.get_tree().paused) { + return; +} + + Vector2 current_position = _body.global_position; + float current_orientation = _body.rotation; + position = GSAIUtils.to_vector3(current_position); + orientation = current_orientation; + + if (calculate_velocities) { + + if (applied_steering) { + applied_steering = false; +} + + + else { + linear_velocity = GSAIUtils.to_vector3(_body.linear_velocity); + angular_velocity = _body.angular_velocity; +} + +} + +} + +} + + GSAIRigidBody2DAgent::GSAIRigidBody2DAgent() { + *body; + _last_position = ; + _body_ref; + } + + GSAIRigidBody2DAgent::~GSAIRigidBody2DAgent() { + } + + + static void GSAIRigidBody2DAgent::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_*body"), &GSAIRigidBody2DAgent::get_*body); + ClassDB::bind_method(D_METHOD("set_*body", "value"), &GSAIRigidBody2DAgent::set_*body); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*body", PROPERTY_HINT_RESOURCE_TYPE, "RigidBody2D"), "set_*body", "get_*body"); + + + ClassDB::bind_method(D_METHOD("get__last_position"), &GSAIRigidBody2DAgent::get__last_position); + ClassDB::bind_method(D_METHOD("set__last_position", "value"), &GSAIRigidBody2DAgent::set__last_position); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "_last_position"), "set__last_position", "get__last_position"); + + + ClassDB::bind_method(D_METHOD("get__body_ref"), &GSAIRigidBody2DAgent::get__body_ref); + ClassDB::bind_method(D_METHOD("set__body_ref", "value"), &GSAIRigidBody2DAgent::set__body_ref); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "_body_ref", PROPERTY_HINT_RESOURCE_TYPE, "Ref"), "set__body_ref", "get__body_ref"); + + + ClassDB::bind_method(D_METHOD("_body_ready"), &GSAIRigidBody2DAgent::_body_ready); + ClassDB::bind_method(D_METHOD("_apply_steering", "acceleration", "_delta"), &GSAIRigidBody2DAgent::_apply_steering); + ClassDB::bind_method(D_METHOD("_set_body", "value"), &GSAIRigidBody2DAgent::_set_body); + ClassDB::bind_method(D_METHOD("_on_SceneTree_frame"), &GSAIRigidBody2DAgent::_on_SceneTree_frame); + + } + + + diff --git a/modules/steering_ai/Agents/gsairigidbody2dagent.h b/modules/steering_ai/Agents/gsairigidbody2dagent.h new file mode 100644 index 000000000..8c59edb78 --- /dev/null +++ b/modules/steering_ai/Agents/gsairigidbody2dagent.h @@ -0,0 +1,43 @@ +#ifndef GSAIRIGIDBODY2DAGENT_H +#define GSAIRIGIDBODY2DAGENT_H + + +class GSAIRigidBody2DAgent : public GSAISpecializedAgent { + GDCLASS(GSAIRigidBody2DAgent, GSAISpecializedAgent); + + public: + + RigidBody2D get_*body(); + void set_*body(const RigidBody2D &val); + + Vector2 get__last_position(); + void set__last_position(const Vector2 &val); + + Ref get__body_ref(); + void set__body_ref(const Ref &val); + + void _body_ready(); + void _apply_steering(const GSAITargetAcceleration &acceleration, const float _delta); + void _set_body(const RigidBody2D &value); + void _on_SceneTree_frame(); + + GSAIRigidBody2DAgent(); + ~GSAIRigidBody2DAgent(); + + protected: + static void _bind_methods(); + + // A specialized steering agent that updates itself every frame so the user does + // not have to using a RigidBody2D + // @category - Specialized agents + // The RigidBody2D to keep track of + // setget _set_body + RigidBody2D *body; + Vector2 _last_position = ; + Ref _body_ref; + // Moves the agent's `body` by target `acceleration`. + // @tags - virtual +}; + + +#endif diff --git a/modules/steering_ai/Agents/gsairigidbody3dagent.cpp b/modules/steering_ai/Agents/gsairigidbody3dagent.cpp new file mode 100644 index 000000000..c05af215e --- /dev/null +++ b/modules/steering_ai/Agents/gsairigidbody3dagent.cpp @@ -0,0 +1,168 @@ + +#include "gsairigidbody3dagent.h" + + +RigidBody GSAIRigidBody3DAgent::get_*body() { + return *body; +} + +void GSAIRigidBody3DAgent::set_*body(const RigidBody &val) { +*body = val; +} + + +Vector3 GSAIRigidBody3DAgent::get__last_position() { + return _last_position; +} + +void GSAIRigidBody3DAgent::set__last_position(const Vector3 &val) { +_last_position = val; +} + + +Ref GSAIRigidBody3DAgent::get__body_ref() { + return _body_ref; +} + +void GSAIRigidBody3DAgent::set__body_ref(const Ref &val) { +_body_ref = val; +} + + + + // A specialized steering agent that updates itself every frame so the user does; + // not have to using a RigidBody; + // @category - Specialized agents; + // The RigidBody to keep track of; + // setget _set_body; + RigidBody *body; + Vector3 _last_position = ; + Ref _body_ref; + + void GSAIRigidBody3DAgent::_body_ready() { + // warning-ignore:return_value_discarded; + body.get_tree().connect("physics_frame", self, "_on_SceneTree_frame"); +} + + // Moves the agent's `body` by target `acceleration`.; + // @tags - virtual; + + void GSAIRigidBody3DAgent::_apply_steering(const GSAITargetAcceleration &acceleration, const float _delta) { + RigidBody *_body = _body_ref.get_ref(); + + if (!_body) { + return; +} + + applied_steering = true; + _body.apply_central_impulse(acceleration.linear); + _body.apply_torque_impulse(Vector3.UP * acceleration.angular); + + if (calculate_velocities) { + linear_velocity = _body.linear_velocity; + angular_velocity = _body.angular_velocity.y; +} + +} + + + void GSAIRigidBody3DAgent::_set_body(const RigidBody &value) { + bool had_body = false; + + if (body) { + had_body = true; +} + + body = value; + _body_ref = weakref(value); + _last_position = value.transform.origin; + last_orientation = value.rotation.y; + position = _last_position; + orientation = last_orientation; + + if (!had_body) { + + if (!body.is_inside_tree()) { + body.connect("ready", self, "_body_ready"); +} + + + else { + _body_ready(); +} + +} + +} + + + void GSAIRigidBody3DAgent::_on_SceneTree_frame() { + RigidBody *_body = _body_ref.get_ref(); + + if (not _body) { + return; +} + + + if (not _body.is_inside_tree() || _body.get_tree().paused) { + return; +} + + Vector3 current_position = _body.transform.origin; + float current_orientation = _body.rotation.y; + position = current_position; + orientation = current_orientation; + + if (calculate_velocities) { + + if (applied_steering) { + applied_steering = false; +} + + + else { + linear_velocity = _body.linear_velocity; + angular_velocity = _body.angular_velocity.y; +} + +} + +} + +} + + GSAIRigidBody3DAgent::GSAIRigidBody3DAgent() { + *body; + _last_position = ; + _body_ref; + } + + GSAIRigidBody3DAgent::~GSAIRigidBody3DAgent() { + } + + + static void GSAIRigidBody3DAgent::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_*body"), &GSAIRigidBody3DAgent::get_*body); + ClassDB::bind_method(D_METHOD("set_*body", "value"), &GSAIRigidBody3DAgent::set_*body); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*body", PROPERTY_HINT_RESOURCE_TYPE, "RigidBody"), "set_*body", "get_*body"); + + + ClassDB::bind_method(D_METHOD("get__last_position"), &GSAIRigidBody3DAgent::get__last_position); + ClassDB::bind_method(D_METHOD("set__last_position", "value"), &GSAIRigidBody3DAgent::set__last_position); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "_last_position"), "set__last_position", "get__last_position"); + + + ClassDB::bind_method(D_METHOD("get__body_ref"), &GSAIRigidBody3DAgent::get__body_ref); + ClassDB::bind_method(D_METHOD("set__body_ref", "value"), &GSAIRigidBody3DAgent::set__body_ref); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "_body_ref", PROPERTY_HINT_RESOURCE_TYPE, "Ref"), "set__body_ref", "get__body_ref"); + + + ClassDB::bind_method(D_METHOD("_body_ready"), &GSAIRigidBody3DAgent::_body_ready); + ClassDB::bind_method(D_METHOD("_apply_steering", "acceleration", "_delta"), &GSAIRigidBody3DAgent::_apply_steering); + ClassDB::bind_method(D_METHOD("_set_body", "value"), &GSAIRigidBody3DAgent::_set_body); + ClassDB::bind_method(D_METHOD("_on_SceneTree_frame"), &GSAIRigidBody3DAgent::_on_SceneTree_frame); + + } + + + diff --git a/modules/steering_ai/Agents/gsairigidbody3dagent.h b/modules/steering_ai/Agents/gsairigidbody3dagent.h new file mode 100644 index 000000000..c64f36fdd --- /dev/null +++ b/modules/steering_ai/Agents/gsairigidbody3dagent.h @@ -0,0 +1,43 @@ +#ifndef GSAIRIGIDBODY3DAGENT_H +#define GSAIRIGIDBODY3DAGENT_H + + +class GSAIRigidBody3DAgent : public GSAISpecializedAgent { + GDCLASS(GSAIRigidBody3DAgent, GSAISpecializedAgent); + + public: + + RigidBody get_*body(); + void set_*body(const RigidBody &val); + + Vector3 get__last_position(); + void set__last_position(const Vector3 &val); + + Ref get__body_ref(); + void set__body_ref(const Ref &val); + + void _body_ready(); + void _apply_steering(const GSAITargetAcceleration &acceleration, const float _delta); + void _set_body(const RigidBody &value); + void _on_SceneTree_frame(); + + GSAIRigidBody3DAgent(); + ~GSAIRigidBody3DAgent(); + + protected: + static void _bind_methods(); + + // A specialized steering agent that updates itself every frame so the user does + // not have to using a RigidBody + // @category - Specialized agents + // The RigidBody to keep track of + // setget _set_body + RigidBody *body; + Vector3 _last_position = ; + Ref _body_ref; + // Moves the agent's `body` by target `acceleration`. + // @tags - virtual +}; + + +#endif diff --git a/modules/steering_ai/Agents/gsaispecializedagent.cpp b/modules/steering_ai/Agents/gsaispecializedagent.cpp new file mode 100644 index 000000000..412fa0185 --- /dev/null +++ b/modules/steering_ai/Agents/gsaispecializedagent.cpp @@ -0,0 +1,164 @@ + +#include "gsaispecializedagent.h" + + +bool GSAISpecializedAgent::get_calculate_velocities() const { + return calculate_velocities; +} + +void GSAISpecializedAgent::set_calculate_velocities(const bool val) { +calculate_velocities = val; +} + + +bool GSAISpecializedAgent::get_apply_linear_drag() const { + return apply_linear_drag; +} + +void GSAISpecializedAgent::set_apply_linear_drag(const bool val) { +apply_linear_drag = val; +} + + +bool GSAISpecializedAgent::get_apply_angular_drag() const { + return apply_angular_drag; +} + +void GSAISpecializedAgent::set_apply_angular_drag(const bool val) { +apply_angular_drag = val; +} + + +float GSAISpecializedAgent::get_linear_drag_percentage() const { + return linear_drag_percentage; +} + +void GSAISpecializedAgent::set_linear_drag_percentage(const float val) { +linear_drag_percentage = val; +} + + +float GSAISpecializedAgent::get_angular_drag_percentage() const { + return angular_drag_percentage; +} + +void GSAISpecializedAgent::set_angular_drag_percentage(const float val) { +angular_drag_percentage = val; +} + + +float GSAISpecializedAgent::get_last_orientation() const { + return last_orientation; +} + +void GSAISpecializedAgent::set_last_orientation(const float val) { +last_orientation = val; +} + + +bool GSAISpecializedAgent::get_applied_steering() const { + return applied_steering; +} + +void GSAISpecializedAgent::set_applied_steering(const bool val) { +applied_steering = val; +} + + + + // A base class for a specialized steering agent that updates itself every frame; + // so the user does not have to. All other specialized agents derive from this.; + // @category - Specialized agents; + // @tags - abstract; + // If `true`, calculates linear and angular velocities based on the previous; + // frame. When `false`, the user must keep those values updated.; + bool calculate_velocities = true; + // If `true`, interpolates the current linear velocity towards 0 by the; + // `linear_drag_percentage` value.; + // Does not apply to `RigidBody` and `RigidBody2D` nodes.; + bool apply_linear_drag = true; + // If `true`, interpolates the current angular velocity towards 0 by the; + // `angular_drag_percentage` value.; + // Does not apply to `RigidBody` and `RigidBody2D` nodes.; + bool apply_angular_drag = true; + // The percentage between the current linear velocity and 0 to interpolate by if; + // `apply_linear_drag` is true.; + // Does not apply to `RigidBody` and `RigidBody2D` nodes.; + float linear_drag_percentage = 0.0; + // The percentage between the current angular velocity and 0 to interpolate by if; + // `apply_angular_drag` is true.; + // Does not apply to `RigidBody` and `RigidBody2D` nodes.; + float angular_drag_percentage = 0.0; + float last_orientation = 0.0; + bool applied_steering = false; + + void GSAISpecializedAgent::apply_steering(const GSAITargetAcceleration &_acceleration, const float _delta) { + call("_apply_steering", _acceleration, _delta); +} + + // Moves the agent's body by target `acceleration`.; + // @tags - virtual; + + void GSAISpecializedAgent::_apply_steering(const GSAITargetAcceleration &_acceleration, const float _delta) { + pass; +} + +} + + GSAISpecializedAgent::GSAISpecializedAgent() { + calculate_velocities = true; + apply_linear_drag = true; + apply_angular_drag = true; + linear_drag_percentage = 0.0; + angular_drag_percentage = 0.0; + last_orientation = 0.0; + applied_steering = false; + } + + GSAISpecializedAgent::~GSAISpecializedAgent() { + } + + + static void GSAISpecializedAgent::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_calculate_velocities"), &GSAISpecializedAgent::get_calculate_velocities); + ClassDB::bind_method(D_METHOD("set_calculate_velocities", "value"), &GSAISpecializedAgent::set_calculate_velocities); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_velocities"), "set_calculate_velocities", "get_calculate_velocities"); + + + ClassDB::bind_method(D_METHOD("get_apply_linear_drag"), &GSAISpecializedAgent::get_apply_linear_drag); + ClassDB::bind_method(D_METHOD("set_apply_linear_drag", "value"), &GSAISpecializedAgent::set_apply_linear_drag); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "apply_linear_drag"), "set_apply_linear_drag", "get_apply_linear_drag"); + + + ClassDB::bind_method(D_METHOD("get_apply_angular_drag"), &GSAISpecializedAgent::get_apply_angular_drag); + ClassDB::bind_method(D_METHOD("set_apply_angular_drag", "value"), &GSAISpecializedAgent::set_apply_angular_drag); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "apply_angular_drag"), "set_apply_angular_drag", "get_apply_angular_drag"); + + + ClassDB::bind_method(D_METHOD("get_linear_drag_percentage"), &GSAISpecializedAgent::get_linear_drag_percentage); + ClassDB::bind_method(D_METHOD("set_linear_drag_percentage", "value"), &GSAISpecializedAgent::set_linear_drag_percentage); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_drag_percentage"), "set_linear_drag_percentage", "get_linear_drag_percentage"); + + + ClassDB::bind_method(D_METHOD("get_angular_drag_percentage"), &GSAISpecializedAgent::get_angular_drag_percentage); + ClassDB::bind_method(D_METHOD("set_angular_drag_percentage", "value"), &GSAISpecializedAgent::set_angular_drag_percentage); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_drag_percentage"), "set_angular_drag_percentage", "get_angular_drag_percentage"); + + + ClassDB::bind_method(D_METHOD("get_last_orientation"), &GSAISpecializedAgent::get_last_orientation); + ClassDB::bind_method(D_METHOD("set_last_orientation", "value"), &GSAISpecializedAgent::set_last_orientation); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "last_orientation"), "set_last_orientation", "get_last_orientation"); + + + ClassDB::bind_method(D_METHOD("get_applied_steering"), &GSAISpecializedAgent::get_applied_steering); + ClassDB::bind_method(D_METHOD("set_applied_steering", "value"), &GSAISpecializedAgent::set_applied_steering); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "applied_steering"), "set_applied_steering", "get_applied_steering"); + + + ClassDB::bind_method(D_METHOD("apply_steering", "_acceleration", "_delta"), &GSAISpecializedAgent::apply_steering); + ClassDB::bind_method(D_METHOD("_apply_steering", "_acceleration", "_delta"), &GSAISpecializedAgent::_apply_steering); + + } + + + diff --git a/modules/steering_ai/Agents/gsaispecializedagent.h b/modules/steering_ai/Agents/gsaispecializedagent.h new file mode 100644 index 000000000..bf8da9dc0 --- /dev/null +++ b/modules/steering_ai/Agents/gsaispecializedagent.h @@ -0,0 +1,70 @@ +#ifndef GSAISPECIALIZEDAGENT_H +#define GSAISPECIALIZEDAGENT_H + + +class GSAISpecializedAgent : public GSAISteeringAgent { + GDCLASS(GSAISpecializedAgent, GSAISteeringAgent); + + public: + + bool get_calculate_velocities() const; + void set_calculate_velocities(const bool val); + + bool get_apply_linear_drag() const; + void set_apply_linear_drag(const bool val); + + bool get_apply_angular_drag() const; + void set_apply_angular_drag(const bool val); + + float get_linear_drag_percentage() const; + void set_linear_drag_percentage(const float val); + + float get_angular_drag_percentage() const; + void set_angular_drag_percentage(const float val); + + float get_last_orientation() const; + void set_last_orientation(const float val); + + bool get_applied_steering() const; + void set_applied_steering(const bool val); + + void apply_steering(const GSAITargetAcceleration &_acceleration, const float _delta); + void _apply_steering(const GSAITargetAcceleration &_acceleration, const float _delta); + + GSAISpecializedAgent(); + ~GSAISpecializedAgent(); + + protected: + static void _bind_methods(); + + // A base class for a specialized steering agent that updates itself every frame + // so the user does not have to. All other specialized agents derive from this. + // @category - Specialized agents + // @tags - abstract + // If `true`, calculates linear and angular velocities based on the previous + // frame. When `false`, the user must keep those values updated. + bool calculate_velocities = true; + // If `true`, interpolates the current linear velocity towards 0 by the + // `linear_drag_percentage` value. + // Does not apply to `RigidBody` and `RigidBody2D` nodes. + bool apply_linear_drag = true; + // If `true`, interpolates the current angular velocity towards 0 by the + // `angular_drag_percentage` value. + // Does not apply to `RigidBody` and `RigidBody2D` nodes. + bool apply_angular_drag = true; + // The percentage between the current linear velocity and 0 to interpolate by if + // `apply_linear_drag` is true. + // Does not apply to `RigidBody` and `RigidBody2D` nodes. + float linear_drag_percentage = 0.0; + // The percentage between the current angular velocity and 0 to interpolate by if + // `apply_angular_drag` is true. + // Does not apply to `RigidBody` and `RigidBody2D` nodes. + float angular_drag_percentage = 0.0; + float last_orientation = 0.0; + bool applied_steering = false; + // Moves the agent's body by target `acceleration`. + // @tags - virtual +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsaiarrive.cpp b/modules/steering_ai/Behaviors/gsaiarrive.cpp new file mode 100644 index 000000000..32c389949 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiarrive.cpp @@ -0,0 +1,130 @@ + +#include "gsaiarrive.h" + + +GSAIAgentLocation GSAIArrive::get_*target() { + return *target; +} + +void GSAIArrive::set_*target(const GSAIAgentLocation &val) { +*target = val; +} + + +float GSAIArrive::get_arrival_tolerance() const { + return arrival_tolerance; +} + +void GSAIArrive::set_arrival_tolerance(const float val) { +arrival_tolerance = val; +} + + +float GSAIArrive::get_deceleration_radius() const { + return deceleration_radius; +} + +void GSAIArrive::set_deceleration_radius(const float val) { +deceleration_radius = val; +} + + +float GSAIArrive::get_time_to_reach() const { + return time_to_reach; +} + +void GSAIArrive::set_time_to_reach(const float val) { +time_to_reach = val; +} + + + + // Calculates acceleration to take an agent to its target's location. The; + // calculation attempts to arrive with zero remaining velocity.; + // @category - Individual behaviors; + // Target agent to arrive to.; + GSAIAgentLocation *target; + // Distance from the target for the agent to be considered successfully; + // arrived.; + float arrival_tolerance = 0.0; + // Distance from the target for the agent to begin slowing down.; + float deceleration_radius = 0.0; + // Represents the time it takes to change acceleration.; + float time_to_reach = 0.1; + + void GSAIArrive::arrive(const GSAITargetAcceleration &acceleration, const Vector3 &target_position) { + call("_arrive", acceleration, target_position); +} + + + void GSAIArrive::_arrive(const GSAITargetAcceleration &acceleration, const Vector3 &target_position) { + Vector3 to_target = target_position - agent.position; + float distance = to_target.length(); + + if (distance <= arrival_tolerance) { + acceleration.set_zero(); +} + + + else { + float desired_speed = agent.linear_speed_max; + + if (distance <= deceleration_radius) { + desired_speed *= distance / deceleration_radius; +} + + Vector3 desired_velocity = to_target * desired_speed / distance; + desired_velocity = ((desired_velocity - agent.linear_velocity) * 1.0 / time_to_reach); + acceleration.linear = GSAIUtils.clampedv3(desired_velocity, agent.linear_acceleration_max); + acceleration.angular = 0; +} + +} + + + void GSAIArrive::_calculate_steering(const GSAITargetAcceleration &acceleration) { + arrive(acceleration, target.position); +} + +} + + GSAIArrive::GSAIArrive() { + *target; + arrival_tolerance = 0.0; + deceleration_radius = 0.0; + time_to_reach = 0.1; + } + + GSAIArrive::~GSAIArrive() { + } + + + static void GSAIArrive::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_*target"), &GSAIArrive::get_*target); + ClassDB::bind_method(D_METHOD("set_*target", "value"), &GSAIArrive::set_*target); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*target", PROPERTY_HINT_RESOURCE_TYPE, "GSAIAgentLocation"), "set_*target", "get_*target"); + + + ClassDB::bind_method(D_METHOD("get_arrival_tolerance"), &GSAIArrive::get_arrival_tolerance); + ClassDB::bind_method(D_METHOD("set_arrival_tolerance", "value"), &GSAIArrive::set_arrival_tolerance); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "arrival_tolerance"), "set_arrival_tolerance", "get_arrival_tolerance"); + + + ClassDB::bind_method(D_METHOD("get_deceleration_radius"), &GSAIArrive::get_deceleration_radius); + ClassDB::bind_method(D_METHOD("set_deceleration_radius", "value"), &GSAIArrive::set_deceleration_radius); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "deceleration_radius"), "set_deceleration_radius", "get_deceleration_radius"); + + + ClassDB::bind_method(D_METHOD("get_time_to_reach"), &GSAIArrive::get_time_to_reach); + ClassDB::bind_method(D_METHOD("set_time_to_reach", "value"), &GSAIArrive::set_time_to_reach); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_to_reach"), "set_time_to_reach", "get_time_to_reach"); + + + ClassDB::bind_method(D_METHOD("arrive", "acceleration", "target_position"), &GSAIArrive::arrive); + ClassDB::bind_method(D_METHOD("_arrive", "acceleration", "target_position"), &GSAIArrive::_arrive); + ClassDB::bind_method(D_METHOD("_calculate_steering", "acceleration"), &GSAIArrive::_calculate_steering); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsaiarrive.h b/modules/steering_ai/Behaviors/gsaiarrive.h new file mode 100644 index 000000000..24e651d51 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiarrive.h @@ -0,0 +1,47 @@ +#ifndef GSAIARRIVE_H +#define GSAIARRIVE_H + + +class GSAIArrive : public GSAISteeringBehavior { + GDCLASS(GSAIArrive, GSAISteeringBehavior); + + public: + + GSAIAgentLocation get_*target(); + void set_*target(const GSAIAgentLocation &val); + + float get_arrival_tolerance() const; + void set_arrival_tolerance(const float val); + + float get_deceleration_radius() const; + void set_deceleration_radius(const float val); + + float get_time_to_reach() const; + void set_time_to_reach(const float val); + + void arrive(const GSAITargetAcceleration &acceleration, const Vector3 &target_position); + void _arrive(const GSAITargetAcceleration &acceleration, const Vector3 &target_position); + void _calculate_steering(const GSAITargetAcceleration &acceleration); + + GSAIArrive(); + ~GSAIArrive(); + + protected: + static void _bind_methods(); + + // Calculates acceleration to take an agent to its target's location. The + // calculation attempts to arrive with zero remaining velocity. + // @category - Individual behaviors + // Target agent to arrive to. + GSAIAgentLocation *target; + // Distance from the target for the agent to be considered successfully + // arrived. + float arrival_tolerance = 0.0; + // Distance from the target for the agent to begin slowing down. + float deceleration_radius = 0.0; + // Represents the time it takes to change acceleration. + float time_to_reach = 0.1; +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsaiavoidcollisions.cpp b/modules/steering_ai/Behaviors/gsaiavoidcollisions.cpp new file mode 100644 index 000000000..4e8bb340a --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiavoidcollisions.cpp @@ -0,0 +1,198 @@ + +#include "gsaiavoidcollisions.h" + + +GSAISteeringAgent GSAIAvoidCollisions::get_*_first_neighbor() { + return *_first_neighbor; +} + +void GSAIAvoidCollisions::set_*_first_neighbor(const GSAISteeringAgent &val) { +*_first_neighbor = val; +} + + +float GSAIAvoidCollisions::get__shortest_time() const { + return _shortest_time; +} + +void GSAIAvoidCollisions::set__shortest_time(const float val) { +_shortest_time = val; +} + + +float GSAIAvoidCollisions::get__first_minimum_separation() const { + return _first_minimum_separation; +} + +void GSAIAvoidCollisions::set__first_minimum_separation(const float val) { +_first_minimum_separation = val; +} + + +float GSAIAvoidCollisions::get__first_distance() const { + return _first_distance; +} + +void GSAIAvoidCollisions::set__first_distance(const float val) { +_first_distance = val; +} + + +Vector3 GSAIAvoidCollisions::get__first_relative_position() { + return _first_relative_position; +} + +void GSAIAvoidCollisions::set__first_relative_position(const Vector3 &val) { +_first_relative_position = val; +} + + +Vector3 GSAIAvoidCollisions::get__first_relative_velocity() { + return _first_relative_velocity; +} + +void GSAIAvoidCollisions::set__first_relative_velocity(const Vector3 &val) { +_first_relative_velocity = val; +} + + + + // Steers the agent to avoid obstacles in its path. Approximates obstacles as; + // spheres.; + // @category - Group behaviors; + GSAISteeringAgent *_first_neighbor; + float _shortest_time = 0.0; + float _first_minimum_separation = 0.0; + float _first_distance = 0.0; + Vector3 _first_relative_position = ; + Vector3 _first_relative_velocity = ; + + void GSAIAvoidCollisions::_calculate_steering(const GSAITargetAcceleration &acceleration) { + _shortest_time = INF; + _first_neighbor = null; + _first_minimum_separation = 0; + _first_distance = 0; + int neighbor_count = proximity.find_neighbors(_callback); + + if (neighbor_count == 0 || not _first_neighbor) { + acceleration.set_zero(); +} + + + else { + + if ((_first_minimum_separation <= 0 || _first_distance < agent.bounding_radius + _first_neighbor.bounding_radius)) { + acceleration.linear = _first_neighbor.position - agent.position; +} + + + else { + acceleration.linear = (_first_relative_position+ (_first_relative_velocity * _shortest_time)); +} + +} + + acceleration.linear = (acceleration.linear.normalized() * -agent.linear_acceleration_max); + acceleration.angular = 0; +} + + // Callback for the proximity to call when finding neighbors. Keeps track of every `neighbor`; + // that was found but only keeps the one the owning agent will most likely collide with.; + // @tags - virtual; + + bool GSAIAvoidCollisions::_report_neighbor(const GSAISteeringAgent &neighbor) { + Vector3 relative_position = neighbor.position - agent.position; + Vector3 relative_velocity = neighbor.linear_velocity - agent.linear_velocity; + float relative_speed_squared = relative_velocity.length_squared(); + + if (relative_speed_squared == 0) { + return false; +} + + + else { + float time_to_collision = -relative_position.dot(relative_velocity) / relative_speed_squared; + + if (time_to_collision <= 0 || time_to_collision >= _shortest_time) { + return false; +} + + + else { + Variant = relative_position.length(); + float minimum_separation = (distance - sqrt(relative_speed_squared) * time_to_collision); + + if (minimum_separation > agent.bounding_radius + neighbor.bounding_radius) { + return false; +} + + + else { + _shortest_time = time_to_collision; + _first_neighbor = neighbor; + _first_minimum_separation = minimum_separation; + _first_distance = distance; + _first_relative_position = relative_position; + _first_relative_velocity = relative_velocity; + return true; +} + +} + +} + +} + +} + + GSAIAvoidCollisions::GSAIAvoidCollisions() { + *_first_neighbor; + _shortest_time = 0.0; + _first_minimum_separation = 0.0; + _first_distance = 0.0; + _first_relative_position = ; + _first_relative_velocity = ; + } + + GSAIAvoidCollisions::~GSAIAvoidCollisions() { + } + + + static void GSAIAvoidCollisions::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_*_first_neighbor"), &GSAIAvoidCollisions::get_*_first_neighbor); + ClassDB::bind_method(D_METHOD("set_*_first_neighbor", "value"), &GSAIAvoidCollisions::set_*_first_neighbor); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*_first_neighbor", PROPERTY_HINT_RESOURCE_TYPE, "GSAISteeringAgent"), "set_*_first_neighbor", "get_*_first_neighbor"); + + + ClassDB::bind_method(D_METHOD("get__shortest_time"), &GSAIAvoidCollisions::get__shortest_time); + ClassDB::bind_method(D_METHOD("set__shortest_time", "value"), &GSAIAvoidCollisions::set__shortest_time); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "_shortest_time"), "set__shortest_time", "get__shortest_time"); + + + ClassDB::bind_method(D_METHOD("get__first_minimum_separation"), &GSAIAvoidCollisions::get__first_minimum_separation); + ClassDB::bind_method(D_METHOD("set__first_minimum_separation", "value"), &GSAIAvoidCollisions::set__first_minimum_separation); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "_first_minimum_separation"), "set__first_minimum_separation", "get__first_minimum_separation"); + + + ClassDB::bind_method(D_METHOD("get__first_distance"), &GSAIAvoidCollisions::get__first_distance); + ClassDB::bind_method(D_METHOD("set__first_distance", "value"), &GSAIAvoidCollisions::set__first_distance); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "_first_distance"), "set__first_distance", "get__first_distance"); + + + ClassDB::bind_method(D_METHOD("get__first_relative_position"), &GSAIAvoidCollisions::get__first_relative_position); + ClassDB::bind_method(D_METHOD("set__first_relative_position", "value"), &GSAIAvoidCollisions::set__first_relative_position); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "_first_relative_position"), "set__first_relative_position", "get__first_relative_position"); + + + ClassDB::bind_method(D_METHOD("get__first_relative_velocity"), &GSAIAvoidCollisions::get__first_relative_velocity); + ClassDB::bind_method(D_METHOD("set__first_relative_velocity", "value"), &GSAIAvoidCollisions::set__first_relative_velocity); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "_first_relative_velocity"), "set__first_relative_velocity", "get__first_relative_velocity"); + + + ClassDB::bind_method(D_METHOD("_calculate_steering", "acceleration"), &GSAIAvoidCollisions::_calculate_steering); + ClassDB::bind_method(D_METHOD("_report_neighbor", "neighbor"), &GSAIAvoidCollisions::_report_neighbor); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsaiavoidcollisions.h b/modules/steering_ai/Behaviors/gsaiavoidcollisions.h new file mode 100644 index 000000000..f94ac0105 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiavoidcollisions.h @@ -0,0 +1,52 @@ +#ifndef GSAIAVOIDCOLLISIONS_H +#define GSAIAVOIDCOLLISIONS_H + + +class GSAIAvoidCollisions : public GSAIGroupBehavior { + GDCLASS(GSAIAvoidCollisions, GSAIGroupBehavior); + + public: + + GSAISteeringAgent get_*_first_neighbor(); + void set_*_first_neighbor(const GSAISteeringAgent &val); + + float get__shortest_time() const; + void set__shortest_time(const float val); + + float get__first_minimum_separation() const; + void set__first_minimum_separation(const float val); + + float get__first_distance() const; + void set__first_distance(const float val); + + Vector3 get__first_relative_position(); + void set__first_relative_position(const Vector3 &val); + + Vector3 get__first_relative_velocity(); + void set__first_relative_velocity(const Vector3 &val); + + void _calculate_steering(const GSAITargetAcceleration &acceleration); + bool _report_neighbor(const GSAISteeringAgent &neighbor); + + GSAIAvoidCollisions(); + ~GSAIAvoidCollisions(); + + protected: + static void _bind_methods(); + + // Steers the agent to avoid obstacles in its path. Approximates obstacles as + // spheres. + // @category - Group behaviors + GSAISteeringAgent *_first_neighbor; + float _shortest_time = 0.0; + float _first_minimum_separation = 0.0; + float _first_distance = 0.0; + Vector3 _first_relative_position = ; + Vector3 _first_relative_velocity = ; + // Callback for the proximity to call when finding neighbors. Keeps track of every `neighbor` + // that was found but only keeps the one the owning agent will most likely collide with. + // @tags - virtual +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsaiblend.cpp b/modules/steering_ai/Behaviors/gsaiblend.cpp new file mode 100644 index 000000000..2cc803f40 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiblend.cpp @@ -0,0 +1,126 @@ + +#include "gsaiblend.h" + + +Array GSAIBlend::get__behaviors() { + return _behaviors; +} + +void GSAIBlend::set__behaviors(const Array &val) { +_behaviors = val; +} + + +GSAITargetAcceleration GSAIBlend::get_*_accel() { + return *_accel; +} + +void GSAIBlend::set_*_accel(const GSAITargetAcceleration &val) { +*_accel = val; +} + + + + // Blends multiple steering behaviors into one, and returns a weighted; + // acceleration from their calculations.; + //; + // Stores the behaviors internally as dictionaries of the form; + // {; + // behavior : GSAISteeringBehavior,; + // weight : float; + // }; + // @category - Combination behaviors; + Array _behaviors = Array(); + GSAITargetAcceleration *_accel = GSAITargetAcceleration.new(); + // Appends a behavior to the internal array along with its `weight`.; + + void GSAIBlend::add_behavior(const GSAISteeringBehavior &behavior, const float weight) { + behavior.agent = agent; + Dictionary dict = Dictionary(); + dict["behavior"] = behavior; + dict["weight"] = weight; + _behaviors.append(dict); +} + + // Returns the behavior at the specified `index`, or an empty `Dictionary` if; + // none was found.; + + Dictionary GSAIBlend::get_behavior(const int index) { + + if (_behaviors.size() > index) { + return _behaviors[index]; +} + + printerr("Tried to get index " + str(index) + " in array of size " + str(_behaviors.size())); + return Dictionary(); +} + + + void GSAIBlend::remove_behavior(const int index) { + + if (_behaviors.size() > index) { + _behaviors.remove(index); + return; +} + + printerr("Tried to get index " + str(index) + " in array of size " + str(_behaviors.size())); + return; +} + + + int GSAIBlend::get_behaviour_count() { + return _behaviors.size(); +} + + + GSAITargetAcceleration GSAIBlend::get_accel() { + return _accel; +} + + + void GSAIBlend::_calculate_steering(const GSAITargetAcceleration &blended_accel) { + blended_accel.set_zero(); + + for (int i = 0; i < _behaviors.size(); ++i) { //i in range(_behaviors.size()) + Dictionary bw = _behaviors[i]; + bw.behavior.calculate_steering(_accel); + blended_accel.add_scaled_accel(_accel, bw.weight); +} + + blended_accel.linear = GSAIUtils.clampedv3(blended_accel.linear, agent.linear_acceleration_max); + blended_accel.angular = clamp(blended_accel.angular, -agent.angular_acceleration_max, agent.angular_acceleration_max); +} + +} + + GSAIBlend::GSAIBlend() { + _behaviors = Array(); + *_accel = GSAITargetAcceleration.new(); + } + + GSAIBlend::~GSAIBlend() { + } + + + static void GSAIBlend::_bind_methods() { + ClassDB::bind_method(D_METHOD("get__behaviors"), &GSAIBlend::get__behaviors); + ClassDB::bind_method(D_METHOD("set__behaviors", "value"), &GSAIBlend::set__behaviors); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_behaviors"), "set__behaviors", "get__behaviors"); + + + ClassDB::bind_method(D_METHOD("get_*_accel"), &GSAIBlend::get_*_accel); + ClassDB::bind_method(D_METHOD("set_*_accel", "value"), &GSAIBlend::set_*_accel); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*_accel", PROPERTY_HINT_RESOURCE_TYPE, "GSAITargetAcceleration"), "set_*_accel", "get_*_accel"); + + + ClassDB::bind_method(D_METHOD("add_behavior", "behavior", "weight"), &GSAIBlend::add_behavior); + ClassDB::bind_method(D_METHOD("get_behavior", "index"), &GSAIBlend::get_behavior); + ClassDB::bind_method(D_METHOD("remove_behavior", "index"), &GSAIBlend::remove_behavior); + ClassDB::bind_method(D_METHOD("get_behaviour_count"), &GSAIBlend::get_behaviour_count); + ClassDB::bind_method(D_METHOD("get_accel"), &GSAIBlend::get_accel); + ClassDB::bind_method(D_METHOD("_calculate_steering", "blended_accel"), &GSAIBlend::_calculate_steering); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsaiblend.h b/modules/steering_ai/Behaviors/gsaiblend.h new file mode 100644 index 000000000..f5800353b --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiblend.h @@ -0,0 +1,46 @@ +#ifndef GSAIBLEND_H +#define GSAIBLEND_H + + +class GSAIBlend : public GSAISteeringBehavior { + GDCLASS(GSAIBlend, GSAISteeringBehavior); + + public: + + Array get__behaviors(); + void set__behaviors(const Array &val); + + GSAITargetAcceleration get_*_accel(); + void set_*_accel(const GSAITargetAcceleration &val); + + void add_behavior(const GSAISteeringBehavior &behavior, const float weight); + Dictionary get_behavior(const int index); + void remove_behavior(const int index); + int get_behaviour_count(); + GSAITargetAcceleration get_accel(); + void _calculate_steering(const GSAITargetAcceleration &blended_accel); + + GSAIBlend(); + ~GSAIBlend(); + + protected: + static void _bind_methods(); + + // Blends multiple steering behaviors into one, and returns a weighted + // acceleration from their calculations. + // + // Stores the behaviors internally as dictionaries of the form + // { + // behavior : GSAISteeringBehavior, + // weight : float + // } + // @category - Combination behaviors + Array _behaviors = Array(); + GSAITargetAcceleration *_accel = GSAITargetAcceleration.new(); + // Appends a behavior to the internal array along with its `weight`. + // Returns the behavior at the specified `index`, or an empty `Dictionary` if + // none was found. +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsaicohesion.cpp b/modules/steering_ai/Behaviors/gsaicohesion.cpp new file mode 100644 index 000000000..1b413495e --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaicohesion.cpp @@ -0,0 +1,63 @@ + +#include "gsaicohesion.h" + + +Vector3 GSAICohesion::get__center_of_mass() { + return _center_of_mass; +} + +void GSAICohesion::set__center_of_mass(const Vector3 &val) { +_center_of_mass = val; +} + + + + // Calculates an acceleration that attempts to move the agent towards the center; + // of mass of the agents in the area defined by the `GSAIProximity`.; + // @category - Group behaviors; + Vector3 _center_of_mass = ; + + void GSAICohesion::_calculate_steering(const GSAITargetAcceleration &acceleration) { + acceleration.set_zero(); + _center_of_mass = Vector3.ZERO; + Variant = proximity.find_neighbors(_callback); + + if (neighbor_count > 0) { + _center_of_mass *= 1.0 / neighbor_count; + acceleration.linear = ((_center_of_mass - agent.position).normalized() * agent.linear_acceleration_max); +} + + // Callback for the proximity to call when finding neighbors. Adds `neighbor`'s position; + // to the center of mass of the group.; + // @tags - virtual; +} + + + bool GSAICohesion::_report_neighbor(const GSAISteeringAgent &neighbor) { + _center_of_mass += neighbor.position; + return true; +} + +} + + GSAICohesion::GSAICohesion() { + _center_of_mass = ; + } + + GSAICohesion::~GSAICohesion() { + } + + + static void GSAICohesion::_bind_methods() { + ClassDB::bind_method(D_METHOD("get__center_of_mass"), &GSAICohesion::get__center_of_mass); + ClassDB::bind_method(D_METHOD("set__center_of_mass", "value"), &GSAICohesion::set__center_of_mass); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "_center_of_mass"), "set__center_of_mass", "get__center_of_mass"); + + + ClassDB::bind_method(D_METHOD("_calculate_steering", "acceleration"), &GSAICohesion::_calculate_steering); + ClassDB::bind_method(D_METHOD("_report_neighbor", "neighbor"), &GSAICohesion::_report_neighbor); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsaicohesion.h b/modules/steering_ai/Behaviors/gsaicohesion.h new file mode 100644 index 000000000..f8e72f636 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaicohesion.h @@ -0,0 +1,29 @@ +#ifndef GSAICOHESION_H +#define GSAICOHESION_H + + +class GSAICohesion : public GSAIGroupBehavior { + GDCLASS(GSAICohesion, GSAIGroupBehavior); + + public: + + Vector3 get__center_of_mass(); + void set__center_of_mass(const Vector3 &val); + + void _calculate_steering(const GSAITargetAcceleration &acceleration); + bool _report_neighbor(const GSAISteeringAgent &neighbor); + + GSAICohesion(); + ~GSAICohesion(); + + protected: + static void _bind_methods(); + + // Calculates an acceleration that attempts to move the agent towards the center + // of mass of the agents in the area defined by the `GSAIProximity`. + // @category - Group behaviors + Vector3 _center_of_mass = ; +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsaievade.cpp b/modules/steering_ai/Behaviors/gsaievade.cpp new file mode 100644 index 000000000..a6fabb6bd --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaievade.cpp @@ -0,0 +1,29 @@ + +#include "gsaievade.h" + + + + // Calculates acceleration to take an agent away from where a target agent is; + // moving.; + // @category - Individual behaviors; + + float GSAIEvade::_get_modified_acceleration() { + return -agent.linear_acceleration_max; +} + +} + + GSAIEvade::GSAIEvade() { + } + + GSAIEvade::~GSAIEvade() { + } + + + static void GSAIEvade::_bind_methods() { + ClassDB::bind_method(D_METHOD("_get_modified_acceleration"), &GSAIEvade::_get_modified_acceleration); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsaievade.h b/modules/steering_ai/Behaviors/gsaievade.h new file mode 100644 index 000000000..d946b79a4 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaievade.h @@ -0,0 +1,24 @@ +#ifndef GSAIEVADE_H +#define GSAIEVADE_H + + +class GSAIEvade : public GSAIPursue { + GDCLASS(GSAIEvade, GSAIPursue); + + public: + + float _get_modified_acceleration(); + + GSAIEvade(); + ~GSAIEvade(); + + protected: + static void _bind_methods(); + + // Calculates acceleration to take an agent away from where a target agent is + // moving. + // @category - Individual behaviors +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsaiface.cpp b/modules/steering_ai/Behaviors/gsaiface.cpp new file mode 100644 index 000000000..2e92f7406 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiface.cpp @@ -0,0 +1,63 @@ + +#include "gsaiface.h" + + + + // Calculates angular acceleration to rotate a target to face its target's; + // position. The behavior attemps to arrive with zero remaining angular velocity.; + // @category - Individual behaviors; + + void GSAIFace::face(const GSAITargetAcceleration &acceleration, const Vector3 &target_position) { + call("_face", acceleration, target_position); +} + + + void GSAIFace::_face(const GSAITargetAcceleration &acceleration, const Vector3 &target_position) { + Vector3 to_target = target_position - agent.position; + float distance_squared = to_target.length_squared(); + + if (distance_squared < agent.zero_linear_speed_threshold) { + acceleration.set_zero(); +} + + + else { + float orientation = ; + + if (use_z) { + orientation = GSAIUtils.vector3_to_angle(to_target); +} + + + else { + orientation = GSAIUtils.vector2_to_angle(GSAIUtils.to_vector2(to_target)); +} + + match_orientation(acceleration, orientation); +} + +} + + + void GSAIFace::_calculate_steering(const GSAITargetAcceleration &acceleration) { + face(acceleration, target.position); +} + +} + + GSAIFace::GSAIFace() { + } + + GSAIFace::~GSAIFace() { + } + + + static void GSAIFace::_bind_methods() { + ClassDB::bind_method(D_METHOD("face", "acceleration", "target_position"), &GSAIFace::face); + ClassDB::bind_method(D_METHOD("_face", "acceleration", "target_position"), &GSAIFace::_face); + ClassDB::bind_method(D_METHOD("_calculate_steering", "acceleration"), &GSAIFace::_calculate_steering); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsaiface.h b/modules/steering_ai/Behaviors/gsaiface.h new file mode 100644 index 000000000..3b769e843 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiface.h @@ -0,0 +1,26 @@ +#ifndef GSAIFACE_H +#define GSAIFACE_H + + +class GSAIFace : public GSAIMatchOrientation { + GDCLASS(GSAIFace, GSAIMatchOrientation); + + public: + + void face(const GSAITargetAcceleration &acceleration, const Vector3 &target_position); + void _face(const GSAITargetAcceleration &acceleration, const Vector3 &target_position); + void _calculate_steering(const GSAITargetAcceleration &acceleration); + + GSAIFace(); + ~GSAIFace(); + + protected: + static void _bind_methods(); + + // Calculates angular acceleration to rotate a target to face its target's + // position. The behavior attemps to arrive with zero remaining angular velocity. + // @category - Individual behaviors +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsaiflee.cpp b/modules/steering_ai/Behaviors/gsaiflee.cpp new file mode 100644 index 000000000..b98578c93 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiflee.cpp @@ -0,0 +1,29 @@ + +#include "gsaiflee.h" + + + + // Calculates acceleration to take an agent directly away from a target agent.; + // @category - Individual behaviors; + + void GSAIFlee::_calculate_steering(const GSAITargetAcceleration &acceleration) { + acceleration.linear = ((agent.position - target.position).normalized() * agent.linear_acceleration_max); + acceleration.angular = 0; +} + +} + + GSAIFlee::GSAIFlee() { + } + + GSAIFlee::~GSAIFlee() { + } + + + static void GSAIFlee::_bind_methods() { + ClassDB::bind_method(D_METHOD("_calculate_steering", "acceleration"), &GSAIFlee::_calculate_steering); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsaiflee.h b/modules/steering_ai/Behaviors/gsaiflee.h new file mode 100644 index 000000000..7cf83366e --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiflee.h @@ -0,0 +1,23 @@ +#ifndef GSAIFLEE_H +#define GSAIFLEE_H + + +class GSAIFlee : public GSAISeek { + GDCLASS(GSAIFlee, GSAISeek); + + public: + + void _calculate_steering(const GSAITargetAcceleration &acceleration); + + GSAIFlee(); + ~GSAIFlee(); + + protected: + static void _bind_methods(); + + // Calculates acceleration to take an agent directly away from a target agent. + // @category - Individual behaviors +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsaifollowpath.cpp b/modules/steering_ai/Behaviors/gsaifollowpath.cpp new file mode 100644 index 000000000..0c9e22250 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaifollowpath.cpp @@ -0,0 +1,146 @@ + +#include "gsaifollowpath.h" + + +GSAIPath GSAIFollowPath::get_*path() { + return *path; +} + +void GSAIFollowPath::set_*path(const GSAIPath &val) { +*path = val; +} + + +float GSAIFollowPath::get_path_offset() const { + return path_offset; +} + +void GSAIFollowPath::set_path_offset(const float val) { +path_offset = val; +} + + +bool GSAIFollowPath::get_is_arrive_enabled() const { + return is_arrive_enabled; +} + +void GSAIFollowPath::set_is_arrive_enabled(const bool val) { +is_arrive_enabled = val; +} + + +float GSAIFollowPath::get_prediction_time() const { + return prediction_time; +} + +void GSAIFollowPath::set_prediction_time(const float val) { +prediction_time = val; +} + + + + // Produces a linear acceleration that moves the agent along the specified path.; + // @category - Individual behaviors; + // The path to follow and travel along.; + GSAIPath *path; + // The distance along the path to generate the next target position.; + float path_offset = 0.0; + // Whether to use `GSAIArrive` behavior on an open path.; + bool is_arrive_enabled = true; + // The amount of time in the future to predict the owning agent's position along; + // the path. Setting it to 0.0 will force non-predictive path following.; + float prediction_time = 0.0; + + void GSAIFollowPath::_calculate_steering(const GSAITargetAcceleration &acceleration) { + Vector3 location = ; + + if (prediction_time == 0) { + location = agent.position; +} + + + else { + location = agent.position + (agent.linear_velocity * prediction_time); +} + + float distance = path.calculate_distance(location); + float target_distance = distance + path_offset; + + if (prediction_time > 0 && path.is_open) { + + if (target_distance < path.calculate_distance(agent.position)) { + target_distance = path.length; +} + +} + + Vector3 target_position = path.calculate_target_position(target_distance); + + if (is_arrive_enabled && path.is_open) { + + if (path_offset >= 0) { + + if (target_distance > path.length - deceleration_radius) { + arrive(acceleration, target_position); + return; +} + +} + + + else { + + if (target_distance < deceleration_radius) { + arrive(acceleration, target_position); + return; +} + +} + +} + + acceleration.linear = (target_position - agent.position).normalized(); + acceleration.linear *= agent.linear_acceleration_max; + acceleration.angular = 0; +} + +} + + GSAIFollowPath::GSAIFollowPath() { + *path; + path_offset = 0.0; + is_arrive_enabled = true; + prediction_time = 0.0; + } + + GSAIFollowPath::~GSAIFollowPath() { + } + + + static void GSAIFollowPath::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_*path"), &GSAIFollowPath::get_*path); + ClassDB::bind_method(D_METHOD("set_*path", "value"), &GSAIFollowPath::set_*path); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*path", PROPERTY_HINT_RESOURCE_TYPE, "GSAIPath"), "set_*path", "get_*path"); + + + ClassDB::bind_method(D_METHOD("get_path_offset"), &GSAIFollowPath::get_path_offset); + ClassDB::bind_method(D_METHOD("set_path_offset", "value"), &GSAIFollowPath::set_path_offset); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "path_offset"), "set_path_offset", "get_path_offset"); + + + ClassDB::bind_method(D_METHOD("get_is_arrive_enabled"), &GSAIFollowPath::get_is_arrive_enabled); + ClassDB::bind_method(D_METHOD("set_is_arrive_enabled", "value"), &GSAIFollowPath::set_is_arrive_enabled); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_arrive_enabled"), "set_is_arrive_enabled", "get_is_arrive_enabled"); + + + ClassDB::bind_method(D_METHOD("get_prediction_time"), &GSAIFollowPath::get_prediction_time); + ClassDB::bind_method(D_METHOD("set_prediction_time", "value"), &GSAIFollowPath::set_prediction_time); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "prediction_time"), "set_prediction_time", "get_prediction_time"); + + + ClassDB::bind_method(D_METHOD("_calculate_steering", "acceleration"), &GSAIFollowPath::_calculate_steering); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsaifollowpath.h b/modules/steering_ai/Behaviors/gsaifollowpath.h new file mode 100644 index 000000000..f5ee060ad --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaifollowpath.h @@ -0,0 +1,44 @@ +#ifndef GSAIFOLLOWPATH_H +#define GSAIFOLLOWPATH_H + + +class GSAIFollowPath : public GSAIArrive { + GDCLASS(GSAIFollowPath, GSAIArrive); + + public: + + GSAIPath get_*path(); + void set_*path(const GSAIPath &val); + + float get_path_offset() const; + void set_path_offset(const float val); + + bool get_is_arrive_enabled() const; + void set_is_arrive_enabled(const bool val); + + float get_prediction_time() const; + void set_prediction_time(const float val); + + void _calculate_steering(const GSAITargetAcceleration &acceleration); + + GSAIFollowPath(); + ~GSAIFollowPath(); + + protected: + static void _bind_methods(); + + // Produces a linear acceleration that moves the agent along the specified path. + // @category - Individual behaviors + // The path to follow and travel along. + GSAIPath *path; + // The distance along the path to generate the next target position. + float path_offset = 0.0; + // Whether to use `GSAIArrive` behavior on an open path. + bool is_arrive_enabled = true; + // The amount of time in the future to predict the owning agent's position along + // the path. Setting it to 0.0 will force non-predictive path following. + float prediction_time = 0.0; +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsailookwhereyougo.cpp b/modules/steering_ai/Behaviors/gsailookwhereyougo.cpp new file mode 100644 index 000000000..3fc91d18d --- /dev/null +++ b/modules/steering_ai/Behaviors/gsailookwhereyougo.cpp @@ -0,0 +1,49 @@ + +#include "gsailookwhereyougo.h" + + + + // Calculates an angular acceleration to match an agent's orientation to its; + // direction of travel.; + // @category - Individual behaviors; + + void GSAILookWhereYouGo::_calculate_steering(const GSAITargetAcceleration &accel) { + + if (agent.linear_velocity.length_squared() < agent.zero_linear_speed_threshold) { + accel.set_zero(); +} + + + else { + float orientation = ; + + if (use_z) { + orientation = GSAIUtils.vector3_to_angle(agent.linear_velocity); +} + + + else { + orientation = GSAIUtils.vector2_to_angle(GSAIUtils.to_vector2(agent.linear_velocity)); +} + + match_orientation(accel, orientation); +} + +} + +} + + GSAILookWhereYouGo::GSAILookWhereYouGo() { + } + + GSAILookWhereYouGo::~GSAILookWhereYouGo() { + } + + + static void GSAILookWhereYouGo::_bind_methods() { + ClassDB::bind_method(D_METHOD("_calculate_steering", "accel"), &GSAILookWhereYouGo::_calculate_steering); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsailookwhereyougo.h b/modules/steering_ai/Behaviors/gsailookwhereyougo.h new file mode 100644 index 000000000..1e8d9e357 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsailookwhereyougo.h @@ -0,0 +1,24 @@ +#ifndef GSAILOOKWHEREYOUGO_H +#define GSAILOOKWHEREYOUGO_H + + +class GSAILookWhereYouGo : public GSAIMatchOrientation { + GDCLASS(GSAILookWhereYouGo, GSAIMatchOrientation); + + public: + + void _calculate_steering(const GSAITargetAcceleration &accel); + + GSAILookWhereYouGo(); + ~GSAILookWhereYouGo(); + + protected: + static void _bind_methods(); + + // Calculates an angular acceleration to match an agent's orientation to its + // direction of travel. + // @category - Individual behaviors +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsaimatchorientation.cpp b/modules/steering_ai/Behaviors/gsaimatchorientation.cpp new file mode 100644 index 000000000..e8a957aeb --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaimatchorientation.cpp @@ -0,0 +1,154 @@ + +#include "gsaimatchorientation.h" + + +GSAIAgentLocation GSAIMatchOrientation::get_*target() { + return *target; +} + +void GSAIMatchOrientation::set_*target(const GSAIAgentLocation &val) { +*target = val; +} + + +float GSAIMatchOrientation::get_alignment_tolerance() const { + return alignment_tolerance; +} + +void GSAIMatchOrientation::set_alignment_tolerance(const float val) { +alignment_tolerance = val; +} + + +float GSAIMatchOrientation::get_deceleration_radius() const { + return deceleration_radius; +} + +void GSAIMatchOrientation::set_deceleration_radius(const float val) { +deceleration_radius = val; +} + + +float GSAIMatchOrientation::get_time_to_reach() const { + return time_to_reach; +} + +void GSAIMatchOrientation::set_time_to_reach(const float val) { +time_to_reach = val; +} + + +bool GSAIMatchOrientation::get_use_z() const { + return use_z; +} + +void GSAIMatchOrientation::set_use_z(const bool val) { +use_z = val; +} + + + + // Calculates an angular acceleration to match an agent's orientation to that of; + // its target. Attempts to make the agent arrive with zero remaining angular; + // velocity.; + // @category - Individual behaviors; + // The target orientation for the behavior to try and match rotations to.; + GSAIAgentLocation *target; + // The amount of distance in radians for the behavior to consider itself close; + // enough to be matching the target agent's rotation.; + float alignment_tolerance = 0.0; + // The amount of distance in radians from the goal to start slowing down.; + float deceleration_radius = 0.0; + // The amount of time to reach the target velocity; + float time_to_reach = 0.1; + // Whether to use the X and Z components instead of X and Y components when; + // determining angles. X and Z should be used in 3D.; + bool use_z = false; + + void GSAIMatchOrientation::match_orientation(const GSAITargetAcceleration &acceleration, const float desired_orientation) { + call("_match_orientation", acceleration, desired_orientation); +} + + + void GSAIMatchOrientation::_match_orientation(const GSAITargetAcceleration &acceleration, const float desired_orientation) { + float rotation = wrapf(desired_orientation - agent.orientation, -PI, PI); + float rotation_size = abs(rotation); + + if (rotation_size <= alignment_tolerance) { + acceleration.set_zero(); +} + + + else { + float desired_rotation = agent.angular_speed_max; + + if (rotation_size <= deceleration_radius) { + desired_rotation *= rotation_size / deceleration_radius; +} + + desired_rotation *= rotation / rotation_size; + acceleration.angular = ((desired_rotation - agent.angular_velocity) / time_to_reach); + float limited_acceleration = abs(acceleration.angular); + + if (limited_acceleration > agent.angular_acceleration_max) { + acceleration.angular *= (agent.angular_acceleration_max / limited_acceleration); +} + +} + + acceleration.linear = Vector3.ZERO; +} + + + void GSAIMatchOrientation::_calculate_steering(const GSAITargetAcceleration &acceleration) { + match_orientation(acceleration, target.orientation); +} + +} + + GSAIMatchOrientation::GSAIMatchOrientation() { + *target; + alignment_tolerance = 0.0; + deceleration_radius = 0.0; + time_to_reach = 0.1; + use_z = false; + } + + GSAIMatchOrientation::~GSAIMatchOrientation() { + } + + + static void GSAIMatchOrientation::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_*target"), &GSAIMatchOrientation::get_*target); + ClassDB::bind_method(D_METHOD("set_*target", "value"), &GSAIMatchOrientation::set_*target); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*target", PROPERTY_HINT_RESOURCE_TYPE, "GSAIAgentLocation"), "set_*target", "get_*target"); + + + ClassDB::bind_method(D_METHOD("get_alignment_tolerance"), &GSAIMatchOrientation::get_alignment_tolerance); + ClassDB::bind_method(D_METHOD("set_alignment_tolerance", "value"), &GSAIMatchOrientation::set_alignment_tolerance); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "alignment_tolerance"), "set_alignment_tolerance", "get_alignment_tolerance"); + + + ClassDB::bind_method(D_METHOD("get_deceleration_radius"), &GSAIMatchOrientation::get_deceleration_radius); + ClassDB::bind_method(D_METHOD("set_deceleration_radius", "value"), &GSAIMatchOrientation::set_deceleration_radius); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "deceleration_radius"), "set_deceleration_radius", "get_deceleration_radius"); + + + ClassDB::bind_method(D_METHOD("get_time_to_reach"), &GSAIMatchOrientation::get_time_to_reach); + ClassDB::bind_method(D_METHOD("set_time_to_reach", "value"), &GSAIMatchOrientation::set_time_to_reach); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "time_to_reach"), "set_time_to_reach", "get_time_to_reach"); + + + ClassDB::bind_method(D_METHOD("get_use_z"), &GSAIMatchOrientation::get_use_z); + ClassDB::bind_method(D_METHOD("set_use_z", "value"), &GSAIMatchOrientation::set_use_z); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_z"), "set_use_z", "get_use_z"); + + + ClassDB::bind_method(D_METHOD("match_orientation", "acceleration", "desired_orientation"), &GSAIMatchOrientation::match_orientation); + ClassDB::bind_method(D_METHOD("_match_orientation", "acceleration", "desired_orientation"), &GSAIMatchOrientation::_match_orientation); + ClassDB::bind_method(D_METHOD("_calculate_steering", "acceleration"), &GSAIMatchOrientation::_calculate_steering); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsaimatchorientation.h b/modules/steering_ai/Behaviors/gsaimatchorientation.h new file mode 100644 index 000000000..7c0a39d13 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaimatchorientation.h @@ -0,0 +1,54 @@ +#ifndef GSAIMATCHORIENTATION_H +#define GSAIMATCHORIENTATION_H + + +class GSAIMatchOrientation : public GSAISteeringBehavior { + GDCLASS(GSAIMatchOrientation, GSAISteeringBehavior); + + public: + + GSAIAgentLocation get_*target(); + void set_*target(const GSAIAgentLocation &val); + + float get_alignment_tolerance() const; + void set_alignment_tolerance(const float val); + + float get_deceleration_radius() const; + void set_deceleration_radius(const float val); + + float get_time_to_reach() const; + void set_time_to_reach(const float val); + + bool get_use_z() const; + void set_use_z(const bool val); + + void match_orientation(const GSAITargetAcceleration &acceleration, const float desired_orientation); + void _match_orientation(const GSAITargetAcceleration &acceleration, const float desired_orientation); + void _calculate_steering(const GSAITargetAcceleration &acceleration); + + GSAIMatchOrientation(); + ~GSAIMatchOrientation(); + + protected: + static void _bind_methods(); + + // Calculates an angular acceleration to match an agent's orientation to that of + // its target. Attempts to make the agent arrive with zero remaining angular + // velocity. + // @category - Individual behaviors + // The target orientation for the behavior to try and match rotations to. + GSAIAgentLocation *target; + // The amount of distance in radians for the behavior to consider itself close + // enough to be matching the target agent's rotation. + float alignment_tolerance = 0.0; + // The amount of distance in radians from the goal to start slowing down. + float deceleration_radius = 0.0; + // The amount of time to reach the target velocity + float time_to_reach = 0.1; + // Whether to use the X and Z components instead of X and Y components when + // determining angles. X and Z should be used in 3D. + bool use_z = false; +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsaipriority.cpp b/modules/steering_ai/Behaviors/gsaipriority.cpp new file mode 100644 index 000000000..05902f18b --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaipriority.cpp @@ -0,0 +1,143 @@ + +#include "gsaipriority.h" + + +float GSAIPriority::get_zero_threshold() const { + return zero_threshold; +} + +void GSAIPriority::set_zero_threshold(const float val) { +zero_threshold = val; +} + + +int GSAIPriority::get__last_selected_index() const { + return _last_selected_index; +} + +void GSAIPriority::set__last_selected_index(const int val) { +_last_selected_index = val; +} + + +Array GSAIPriority::get__behaviors() { + return _behaviors; +} + +void GSAIPriority::set__behaviors(const Array &val) { +_behaviors = val; +} + + + + // Container for multiple behaviors that returns the result of the first child; + // behavior with non-zero acceleration.; + // @category - Combination behaviors; + // If a behavior's acceleration is lower than this threshold, the container; + // considers it has an acceleration of zero.; + float zero_threshold = 0.0; + // The index of the last behavior the container prioritized.; + int _last_selected_index = 0; + Array _behaviors = Array(); + // Appends a steering behavior as a child of this container.; + + void GSAIPriority::add_behavior(const GSAISteeringBehavior &behavior) { + _behaviors.append(behavior); +} + + // Returns the behavior at the position in the pool referred to by `index`, or; + // `null` if no behavior was found.; + + GSAISteeringBehavior GSAIPriority::get_behavior(const int index) { + + if (_behaviors.size() > index) { + return _behaviors[index]; +} + + printerr("Tried to get index " + str(index) + " in array of size " + str(_behaviors.size())); + return null; +} + + + void GSAIPriority::remove_behavior(const int index) { + + if (_behaviors.size() > index) { + _behaviors.remove(index); + return; +} + + printerr("Tried to get index " + str(index) + " in array of size " + str(_behaviors.size())); + return; +} + + + int GSAIPriority::get_behaviour_count() { + return _behaviors.size(); +} + + + void GSAIPriority::_calculate_steering(const GSAITargetAcceleration &accel) { + float threshold_squared = zero_threshold * zero_threshold; + _last_selected_index = -1; + int size = _behaviors.size(); + + if (size > 0) { + + for (int i = 0; i < size; ++i) { //i in range(size) + _last_selected_index = i; + GSAISteeringBehavior *behavior = _behaviors[i]; + behavior.calculate_steering(accel); + + if (accel.get_magnitude_squared() > threshold_squared) { + break; +} + +} + +} + + + else { + accel.set_zero(); +} + +} + +} + + GSAIPriority::GSAIPriority() { + zero_threshold = 0.0; + _last_selected_index = 0; + _behaviors = Array(); + } + + GSAIPriority::~GSAIPriority() { + } + + + static void GSAIPriority::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_zero_threshold"), &GSAIPriority::get_zero_threshold); + ClassDB::bind_method(D_METHOD("set_zero_threshold", "value"), &GSAIPriority::set_zero_threshold); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "zero_threshold"), "set_zero_threshold", "get_zero_threshold"); + + + ClassDB::bind_method(D_METHOD("get__last_selected_index"), &GSAIPriority::get__last_selected_index); + ClassDB::bind_method(D_METHOD("set__last_selected_index", "value"), &GSAIPriority::set__last_selected_index); + ADD_PROPERTY(PropertyInfo(Variant::INT, "_last_selected_index"), "set__last_selected_index", "get__last_selected_index"); + + + ClassDB::bind_method(D_METHOD("get__behaviors"), &GSAIPriority::get__behaviors); + ClassDB::bind_method(D_METHOD("set__behaviors", "value"), &GSAIPriority::set__behaviors); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_behaviors"), "set__behaviors", "get__behaviors"); + + + ClassDB::bind_method(D_METHOD("add_behavior", "behavior"), &GSAIPriority::add_behavior); + ClassDB::bind_method(D_METHOD("get_behavior", "index"), &GSAIPriority::get_behavior); + ClassDB::bind_method(D_METHOD("remove_behavior", "index"), &GSAIPriority::remove_behavior); + ClassDB::bind_method(D_METHOD("get_behaviour_count"), &GSAIPriority::get_behaviour_count); + ClassDB::bind_method(D_METHOD("_calculate_steering", "accel"), &GSAIPriority::_calculate_steering); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsaipriority.h b/modules/steering_ai/Behaviors/gsaipriority.h new file mode 100644 index 000000000..7c4abcafe --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaipriority.h @@ -0,0 +1,46 @@ +#ifndef GSAIPRIORITY_H +#define GSAIPRIORITY_H + + +class GSAIPriority : public GSAISteeringBehavior { + GDCLASS(GSAIPriority, GSAISteeringBehavior); + + public: + + float get_zero_threshold() const; + void set_zero_threshold(const float val); + + int get__last_selected_index() const; + void set__last_selected_index(const int val); + + Array get__behaviors(); + void set__behaviors(const Array &val); + + void add_behavior(const GSAISteeringBehavior &behavior); + GSAISteeringBehavior get_behavior(const int index); + void remove_behavior(const int index); + int get_behaviour_count(); + void _calculate_steering(const GSAITargetAcceleration &accel); + + GSAIPriority(); + ~GSAIPriority(); + + protected: + static void _bind_methods(); + + // Container for multiple behaviors that returns the result of the first child + // behavior with non-zero acceleration. + // @category - Combination behaviors + // If a behavior's acceleration is lower than this threshold, the container + // considers it has an acceleration of zero. + float zero_threshold = 0.0; + // The index of the last behavior the container prioritized. + int _last_selected_index = 0; + Array _behaviors = Array(); + // Appends a steering behavior as a child of this container. + // Returns the behavior at the position in the pool referred to by `index`, or + // `null` if no behavior was found. +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsaipursue.cpp b/modules/steering_ai/Behaviors/gsaipursue.cpp new file mode 100644 index 000000000..843296181 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaipursue.cpp @@ -0,0 +1,92 @@ + +#include "gsaipursue.h" + + +GSAISteeringAgent GSAIPursue::get_*target() { + return *target; +} + +void GSAIPursue::set_*target(const GSAISteeringAgent &val) { +*target = val; +} + + +float GSAIPursue::get_predict_time_max() const { + return predict_time_max; +} + +void GSAIPursue::set_predict_time_max(const float val) { +predict_time_max = val; +} + + + + // Calculates an acceleration to make an agent intercept another based on the; + // target agent's movement.; + // @category - Individual behaviors; + // The target agent that the behavior is trying to intercept.; + GSAISteeringAgent *target; + // The maximum amount of time in the future the behavior predicts the target's; + // location.; + float predict_time_max = 1.0; + + void GSAIPursue::_calculate_steering(const GSAITargetAcceleration &acceleration) { + Vector3 target_position = target.position; + float distance_squared = (target_position - agent.position).length_squared(); + float speed_squared = agent.linear_velocity.length_squared(); + float predict_time = predict_time_max; + + if (speed_squared > 0) { + Variant predict_time_squared = distance_squared / speed_squared; + + if (predict_time_squared < predict_time_max * predict_time_max) { + predict_time = sqrt(predict_time_squared); +} + +} + + acceleration.linear = ((target_position + (target.linear_velocity * predict_time)) - agent.position).normalized(); + acceleration.linear *= get_modified_acceleration(); + acceleration.angular = 0; +} + + + float GSAIPursue::get_modified_acceleration() { + return call("_get_modified_acceleration"); +} + + + float GSAIPursue::_get_modified_acceleration() { + return agent.linear_acceleration_max; +} + +} + + GSAIPursue::GSAIPursue() { + *target; + predict_time_max = 1.0; + } + + GSAIPursue::~GSAIPursue() { + } + + + static void GSAIPursue::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_*target"), &GSAIPursue::get_*target); + ClassDB::bind_method(D_METHOD("set_*target", "value"), &GSAIPursue::set_*target); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*target", PROPERTY_HINT_RESOURCE_TYPE, "GSAISteeringAgent"), "set_*target", "get_*target"); + + + ClassDB::bind_method(D_METHOD("get_predict_time_max"), &GSAIPursue::get_predict_time_max); + ClassDB::bind_method(D_METHOD("set_predict_time_max", "value"), &GSAIPursue::set_predict_time_max); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "predict_time_max"), "set_predict_time_max", "get_predict_time_max"); + + + ClassDB::bind_method(D_METHOD("_calculate_steering", "acceleration"), &GSAIPursue::_calculate_steering); + ClassDB::bind_method(D_METHOD("get_modified_acceleration"), &GSAIPursue::get_modified_acceleration); + ClassDB::bind_method(D_METHOD("_get_modified_acceleration"), &GSAIPursue::_get_modified_acceleration); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsaipursue.h b/modules/steering_ai/Behaviors/gsaipursue.h new file mode 100644 index 000000000..4450d00fe --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaipursue.h @@ -0,0 +1,37 @@ +#ifndef GSAIPURSUE_H +#define GSAIPURSUE_H + + +class GSAIPursue : public GSAISteeringBehavior { + GDCLASS(GSAIPursue, GSAISteeringBehavior); + + public: + + GSAISteeringAgent get_*target(); + void set_*target(const GSAISteeringAgent &val); + + float get_predict_time_max() const; + void set_predict_time_max(const float val); + + void _calculate_steering(const GSAITargetAcceleration &acceleration); + float get_modified_acceleration(); + float _get_modified_acceleration(); + + GSAIPursue(); + ~GSAIPursue(); + + protected: + static void _bind_methods(); + + // Calculates an acceleration to make an agent intercept another based on the + // target agent's movement. + // @category - Individual behaviors + // The target agent that the behavior is trying to intercept. + GSAISteeringAgent *target; + // The maximum amount of time in the future the behavior predicts the target's + // location. + float predict_time_max = 1.0; +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsaiseek.cpp b/modules/steering_ai/Behaviors/gsaiseek.cpp new file mode 100644 index 000000000..f780d7a10 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiseek.cpp @@ -0,0 +1,47 @@ + +#include "gsaiseek.h" + + +GSAIAgentLocation GSAISeek::get_*target() { + return *target; +} + +void GSAISeek::set_*target(const GSAIAgentLocation &val) { +*target = val; +} + + + + // Calculates an acceleration to take an agent to a target agent's position; + // directly.; + // @category - Individual behaviors; + // The target that the behavior aims to move the agent to.; + GSAIAgentLocation *target; + + void GSAISeek::_calculate_steering(const GSAITargetAcceleration &acceleration) { + acceleration.linear = ((target.position - agent.position).normalized() * agent.linear_acceleration_max); + acceleration.angular = 0; +} + +} + + GSAISeek::GSAISeek() { + *target; + } + + GSAISeek::~GSAISeek() { + } + + + static void GSAISeek::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_*target"), &GSAISeek::get_*target); + ClassDB::bind_method(D_METHOD("set_*target", "value"), &GSAISeek::set_*target); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*target", PROPERTY_HINT_RESOURCE_TYPE, "GSAIAgentLocation"), "set_*target", "get_*target"); + + + ClassDB::bind_method(D_METHOD("_calculate_steering", "acceleration"), &GSAISeek::_calculate_steering); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsaiseek.h b/modules/steering_ai/Behaviors/gsaiseek.h new file mode 100644 index 000000000..84bed161a --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiseek.h @@ -0,0 +1,29 @@ +#ifndef GSAISEEK_H +#define GSAISEEK_H + + +class GSAISeek : public GSAISteeringBehavior { + GDCLASS(GSAISeek, GSAISteeringBehavior); + + public: + + GSAIAgentLocation get_*target(); + void set_*target(const GSAIAgentLocation &val); + + void _calculate_steering(const GSAITargetAcceleration &acceleration); + + GSAISeek(); + ~GSAISeek(); + + protected: + static void _bind_methods(); + + // Calculates an acceleration to take an agent to a target agent's position + // directly. + // @category - Individual behaviors + // The target that the behavior aims to move the agent to. + GSAIAgentLocation *target; +}; + + +#endif diff --git a/modules/steering_ai/Behaviors/gsaiseparation.cpp b/modules/steering_ai/Behaviors/gsaiseparation.cpp new file mode 100644 index 000000000..491679517 --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiseparation.cpp @@ -0,0 +1,88 @@ + +#include "gsaiseparation.h" + + +float GSAISeparation::get_decay_coefficient() const { + return decay_coefficient; +} + +void GSAISeparation::set_decay_coefficient(const float val) { +decay_coefficient = val; +} + + +GSAITargetAcceleration GSAISeparation::get_*acceleration() { + return *acceleration; +} + +void GSAISeparation::set_*acceleration(const GSAITargetAcceleration &val) { +*acceleration = val; +} + + + + // Calculates an acceleration that repels the agent from its neighbors in the; + // given `GSAIProximity`.; + //; + // The acceleration is an average based on all neighbors, multiplied by a; + // strength decreasing by the inverse square law in relation to distance, and it; + // accumulates.; + // @category - Group behaviors; + // The coefficient to calculate how fast the separation strength decays with distance.; + float decay_coefficient = 1.0; + GSAITargetAcceleration *acceleration; + + void GSAISeparation::_calculate_steering(const GSAITargetAcceleration &_acceleration) { + self.acceleration = _acceleration; + acceleration.set_zero(); + // warning-ignore:return_value_discarded; + proximity.find_neighbors(_callback); +} + + // Callback for the proximity to call when finding neighbors. Determines the amount of; + // acceleration that `neighbor` imposes based on its distance from the owner agent.; + // @tags - virtual; + + bool GSAISeparation::_report_neighbor(const GSAISteeringAgent &neighbor) { + Vector3 to_agent = agent.position - neighbor.position; + float distance_squared = to_agent.length_squared(); + float acceleration_max = agent.linear_acceleration_max; + Variant strength = decay_coefficient / distance_squared; + + if (strength > acceleration_max) { + strength = acceleration_max; +} + + acceleration.linear += to_agent * (strength / sqrt(distance_squared)); + return true; +} + +} + + GSAISeparation::GSAISeparation() { + decay_coefficient = 1.0; + *acceleration; + } + + GSAISeparation::~GSAISeparation() { + } + + + static void GSAISeparation::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_decay_coefficient"), &GSAISeparation::get_decay_coefficient); + ClassDB::bind_method(D_METHOD("set_decay_coefficient", "value"), &GSAISeparation::set_decay_coefficient); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "decay_coefficient"), "set_decay_coefficient", "get_decay_coefficient"); + + + ClassDB::bind_method(D_METHOD("get_*acceleration"), &GSAISeparation::get_*acceleration); + ClassDB::bind_method(D_METHOD("set_*acceleration", "value"), &GSAISeparation::set_*acceleration); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*acceleration", PROPERTY_HINT_RESOURCE_TYPE, "GSAITargetAcceleration"), "set_*acceleration", "get_*acceleration"); + + + ClassDB::bind_method(D_METHOD("_calculate_steering", "_acceleration"), &GSAISeparation::_calculate_steering); + ClassDB::bind_method(D_METHOD("_report_neighbor", "neighbor"), &GSAISeparation::_report_neighbor); + + } + + + diff --git a/modules/steering_ai/Behaviors/gsaiseparation.h b/modules/steering_ai/Behaviors/gsaiseparation.h new file mode 100644 index 000000000..bccabe3ce --- /dev/null +++ b/modules/steering_ai/Behaviors/gsaiseparation.h @@ -0,0 +1,41 @@ +#ifndef GSAISEPARATION_H +#define GSAISEPARATION_H + + +class GSAISeparation : public GSAIGroupBehavior { + GDCLASS(GSAISeparation, GSAIGroupBehavior); + + public: + + float get_decay_coefficient() const; + void set_decay_coefficient(const float val); + + GSAITargetAcceleration get_*acceleration(); + void set_*acceleration(const GSAITargetAcceleration &val); + + void _calculate_steering(const GSAITargetAcceleration &_acceleration); + bool _report_neighbor(const GSAISteeringAgent &neighbor); + + GSAISeparation(); + ~GSAISeparation(); + + protected: + static void _bind_methods(); + + // Calculates an acceleration that repels the agent from its neighbors in the + // given `GSAIProximity`. + // + // The acceleration is an average based on all neighbors, multiplied by a + // strength decreasing by the inverse square law in relation to distance, and it + // accumulates. + // @category - Group behaviors + // The coefficient to calculate how fast the separation strength decays with distance. + float decay_coefficient = 1.0; + GSAITargetAcceleration *acceleration; + // Callback for the proximity to call when finding neighbors. Determines the amount of + // acceleration that `neighbor` imposes based on its distance from the owner agent. + // @tags - virtual +}; + + +#endif diff --git a/modules/steering_ai/COPYRIGHT.txt b/modules/steering_ai/COPYRIGHT.txt new file mode 100644 index 000000000..ffa4e98fc --- /dev/null +++ b/modules/steering_ai/COPYRIGHT.txt @@ -0,0 +1,43 @@ +# Exhaustive licensing information for files in the Godot Engine repository +# ========================================================================= +# +# This file aims at documenting the copyright and license for every source +# file in the Godot Engine repository, and especially outline the files +# whose license differs from the MIT/Expat license used by Godot Engine. +# +# It is written as a machine-readable format following the debian/copyright +# specification. Globbing patterns (e.g. "Files: *") mean that they affect +# all corresponding files (also recursively in subfolders), apart from those +# with a more explicit copyright statement. +# +# Licenses are given with their debian/copyright short name (or SPDX identifier +# if no standard short name exists) and are all included in plain text at the +# end of this file (in alphabetical order). +# +# Disclaimer for thirdparty libraries: +# ------------------------------------ +# +# Licensing details for thirdparty libraries in the 'thirdparty/' directory +# are given in summarized form, i.e. with only the "main" license described +# in the library's license statement. Different licenses of single files or +# code snippets in thirdparty libraries are not documented here. +# For example: +# Files: ./thirdparty/zlib/ +# Copyright: 1995-2017, Jean-loup Gailly and Mark Adler +# License: Zlib +# The exact copyright for each file in that library *may* differ, and some +# files or code snippets might be distributed under other compatible licenses +# (e.g. a public domain dedication), but as far as Godot Engine is concerned +# the library is considered as a whole under the Zlib license. +# +# Nota: When linking dynamically against thirdparty libraries instead of +# building them into the Godot binary, you may remove the corresponding +# license details from this file. + +----------------------------------------------------------------------- + +Files: modules/steering_ai/* +Comment: Steering AI Module +Copyright: Copyright (c) 2023-present Péter Magyar. + Copyright (c) 2020-2023 GDQuest. +License: Expat diff --git a/modules/steering_ai/LICENSE b/modules/steering_ai/LICENSE new file mode 100644 index 000000000..77266288f --- /dev/null +++ b/modules/steering_ai/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2023-present Péter Magyar +Copyright (c) 2020-2023 GDQuest + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/modules/steering_ai/Proximities/gsaiinfiniteproximity.cpp b/modules/steering_ai/Proximities/gsaiinfiniteproximity.cpp new file mode 100644 index 000000000..f1ac48d8f --- /dev/null +++ b/modules/steering_ai/Proximities/gsaiinfiniteproximity.cpp @@ -0,0 +1,50 @@ + +#include "gsaiinfiniteproximity.h" + + + + // Determines any agent that is in the specified list as being neighbors with the; + // owner agent, regardless of distance.; + // @category - Proximities; + // Returns a number of neighbors based on a `callback` function.; + //; + // `_find_neighbors` calls `callback` for each agent in the `agents` array and; + // adds one to the count if its `callback` returns true.; + // @tags - virtual; + + int GSAIInfiniteProximity::_find_neighbors(const FuncRef &callback) { + int neighbor_count = 0; + int agent_count = agents.size(); + + for (int i = 0; i < agent_count; ++i) { //i in range(agent_count) + GSAISteeringAgent *current_agent = agents[i] as GSAISteeringAgent; + + if (current_agent != agent) { + + if (callback.call_func(current_agent)) { + neighbor_count += 1; +} + +} + +} + + return neighbor_count; +} + +} + + GSAIInfiniteProximity::GSAIInfiniteProximity() { + } + + GSAIInfiniteProximity::~GSAIInfiniteProximity() { + } + + + static void GSAIInfiniteProximity::_bind_methods() { + ClassDB::bind_method(D_METHOD("_find_neighbors", "callback"), &GSAIInfiniteProximity::_find_neighbors); + + } + + + diff --git a/modules/steering_ai/Proximities/gsaiinfiniteproximity.h b/modules/steering_ai/Proximities/gsaiinfiniteproximity.h new file mode 100644 index 000000000..b74488d93 --- /dev/null +++ b/modules/steering_ai/Proximities/gsaiinfiniteproximity.h @@ -0,0 +1,29 @@ +#ifndef GSAIINFINITEPROXIMITY_H +#define GSAIINFINITEPROXIMITY_H + + +class GSAIInfiniteProximity : public GSAIProximity { + GDCLASS(GSAIInfiniteProximity, GSAIProximity); + + public: + + int _find_neighbors(const FuncRef &callback); + + GSAIInfiniteProximity(); + ~GSAIInfiniteProximity(); + + protected: + static void _bind_methods(); + + // Determines any agent that is in the specified list as being neighbors with the + // owner agent, regardless of distance. + // @category - Proximities + // Returns a number of neighbors based on a `callback` function. + // + // `_find_neighbors` calls `callback` for each agent in the `agents` array and + // adds one to the count if its `callback` returns true. + // @tags - virtual +}; + + +#endif diff --git a/modules/steering_ai/Proximities/gsaiproximity.cpp b/modules/steering_ai/Proximities/gsaiproximity.cpp new file mode 100644 index 000000000..1cdf1eafa --- /dev/null +++ b/modules/steering_ai/Proximities/gsaiproximity.cpp @@ -0,0 +1,74 @@ + +#include "gsaiproximity.h" + + +GSAISteeringAgent GSAIProximity::get_*agent() { + return *agent; +} + +void GSAIProximity::set_*agent(const GSAISteeringAgent &val) { +*agent = val; +} + + +Array GSAIProximity::get_agents() { + return agents; +} + +void GSAIProximity::set_agents(const Array &val) { +agents = val; +} + + + + // Base container type that stores data to find the neighbors of an agent.; + // @category - Proximities; + // @tags - abstract; + // The owning agent whose neighbors are found in the group; + GSAISteeringAgent *agent; + // The agents who are part of this group and could be potential neighbors; + Array agents = Array(); + + int GSAIProximity::find_neighbors(const FuncRef &_callback) { + return call("_find_neighbors", _callback); +} + + // Returns a number of neighbors based on a `callback` function.; + //; + // `_find_neighbors` calls `callback` for each agent in the `agents` array and; + // adds one to the count if its `callback` returns true.; + // @tags - virtual; + + int GSAIProximity::_find_neighbors(const FuncRef &_callback) { + return 0; +} + +} + + GSAIProximity::GSAIProximity() { + *agent; + agents = Array(); + } + + GSAIProximity::~GSAIProximity() { + } + + + static void GSAIProximity::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_*agent"), &GSAIProximity::get_*agent); + ClassDB::bind_method(D_METHOD("set_*agent", "value"), &GSAIProximity::set_*agent); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*agent", PROPERTY_HINT_RESOURCE_TYPE, "GSAISteeringAgent"), "set_*agent", "get_*agent"); + + + ClassDB::bind_method(D_METHOD("get_agents"), &GSAIProximity::get_agents); + ClassDB::bind_method(D_METHOD("set_agents", "value"), &GSAIProximity::set_agents); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "agents"), "set_agents", "get_agents"); + + + ClassDB::bind_method(D_METHOD("find_neighbors", "_callback"), &GSAIProximity::find_neighbors); + ClassDB::bind_method(D_METHOD("_find_neighbors", "_callback"), &GSAIProximity::_find_neighbors); + + } + + + diff --git a/modules/steering_ai/Proximities/gsaiproximity.h b/modules/steering_ai/Proximities/gsaiproximity.h new file mode 100644 index 000000000..a864e5230 --- /dev/null +++ b/modules/steering_ai/Proximities/gsaiproximity.h @@ -0,0 +1,40 @@ +#ifndef GSAIPROXIMITY_H +#define GSAIPROXIMITY_H + + +class GSAIProximity : public Reference { + GDCLASS(GSAIProximity, Reference); + + public: + + GSAISteeringAgent get_*agent(); + void set_*agent(const GSAISteeringAgent &val); + + Array get_agents(); + void set_agents(const Array &val); + + int find_neighbors(const FuncRef &_callback); + int _find_neighbors(const FuncRef &_callback); + + GSAIProximity(); + ~GSAIProximity(); + + protected: + static void _bind_methods(); + + // Base container type that stores data to find the neighbors of an agent. + // @category - Proximities + // @tags - abstract + // The owning agent whose neighbors are found in the group + GSAISteeringAgent *agent; + // The agents who are part of this group and could be potential neighbors + Array agents = Array(); + // Returns a number of neighbors based on a `callback` function. + // + // `_find_neighbors` calls `callback` for each agent in the `agents` array and + // adds one to the count if its `callback` returns true. + // @tags - virtual +}; + + +#endif diff --git a/modules/steering_ai/Proximities/gsairadiusproximity.cpp b/modules/steering_ai/Proximities/gsairadiusproximity.cpp new file mode 100644 index 000000000..4e6ad58a0 --- /dev/null +++ b/modules/steering_ai/Proximities/gsairadiusproximity.cpp @@ -0,0 +1,149 @@ + +#include "gsairadiusproximity.h" + + +float GSAIRadiusProximity::get_radius() const { + return radius; +} + +void GSAIRadiusProximity::set_radius(const float val) { +radius = val; +} + + +int GSAIRadiusProximity::get__last_frame() const { + return _last_frame; +} + +void GSAIRadiusProximity::set__last_frame(const int val) { +_last_frame = val; +} + + +SceneTree GSAIRadiusProximity::get_*_scene_tree() { + return *_scene_tree; +} + +void GSAIRadiusProximity::set_*_scene_tree(const SceneTree &val) { +*_scene_tree = val; +} + + + + // Determines any agent that is in the specified list as being neighbors with the owner agent if; + // they lie within the specified radius.; + // @category - Proximities; + // The radius around the owning agent to find neighbors in; + float radius = 0.0; + int _last_frame = 0; + SceneTree *_scene_tree; + + void GSAIRadiusProximity::_init() { + _scene_tree = Engine.get_main_loop(); +} + + // Returns a number of neighbors based on a `callback` function.; + //; + // `_find_neighbors` calls `callback` for each agent in the `agents` array that lie within; + // the radius around the owning agent and adds one to the count if its `callback` returns true.; + // @tags - virtual; + + int GSAIRadiusProximity::_find_neighbors(const FuncRef &callback) { + int agent_count = agents.size(); + int neighbor_count = 0; + int current_frame = ; + + if (_scene_tree) { + current_frame = _scene_tree.get_frame(); +} + + + else { + current_frame = -_last_frame; +} + + + if (current_frame != _last_frame) { + _last_frame = current_frame; + Vector3 owner_position = agent.position; + + for (int i = 0; i < agent_count; ++i) { //i in range(agent_count) + GSAISteeringAgent *current_agent = agents[i] as GSAISteeringAgent; + + if (current_agent != agent) { + float distance_squared = owner_position.distance_squared_to(current_agent.position); + float range_to = radius + current_agent.bounding_radius; + + if (distance_squared < range_to * range_to) { + + if (callback.call_func(current_agent)) { + current_agent.is_tagged = true; + neighbor_count += 1; + continue; +} + +} + +} + + current_agent.is_tagged = false; +} + +} + + + else { + + for (int i = 0; i < agent_count; ++i) { //i in range(agent_count) + GSAISteeringAgent *current_agent = agents[i] as GSAISteeringAgent; + + if (current_agent != agent && current_agent.is_tagged) { + + if (callback.call_func(current_agent)) { + neighbor_count += 1; +} + +} + +} + +} + + return neighbor_count; +} + +} + + GSAIRadiusProximity::GSAIRadiusProximity() { + radius = 0.0; + _last_frame = 0; + *_scene_tree; + } + + GSAIRadiusProximity::~GSAIRadiusProximity() { + } + + + static void GSAIRadiusProximity::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_radius"), &GSAIRadiusProximity::get_radius); + ClassDB::bind_method(D_METHOD("set_radius", "value"), &GSAIRadiusProximity::set_radius); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "radius"), "set_radius", "get_radius"); + + + ClassDB::bind_method(D_METHOD("get__last_frame"), &GSAIRadiusProximity::get__last_frame); + ClassDB::bind_method(D_METHOD("set__last_frame", "value"), &GSAIRadiusProximity::set__last_frame); + ADD_PROPERTY(PropertyInfo(Variant::INT, "_last_frame"), "set__last_frame", "get__last_frame"); + + + ClassDB::bind_method(D_METHOD("get_*_scene_tree"), &GSAIRadiusProximity::get_*_scene_tree); + ClassDB::bind_method(D_METHOD("set_*_scene_tree", "value"), &GSAIRadiusProximity::set_*_scene_tree); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*_scene_tree", PROPERTY_HINT_RESOURCE_TYPE, "SceneTree"), "set_*_scene_tree", "get_*_scene_tree"); + + + ClassDB::bind_method(D_METHOD("_init"), &GSAIRadiusProximity::_init); + ClassDB::bind_method(D_METHOD("_find_neighbors", "callback"), &GSAIRadiusProximity::_find_neighbors); + + } + + + diff --git a/modules/steering_ai/Proximities/gsairadiusproximity.h b/modules/steering_ai/Proximities/gsairadiusproximity.h new file mode 100644 index 000000000..cfa89efa7 --- /dev/null +++ b/modules/steering_ai/Proximities/gsairadiusproximity.h @@ -0,0 +1,43 @@ +#ifndef GSAIRADIUSPROXIMITY_H +#define GSAIRADIUSPROXIMITY_H + + +class GSAIRadiusProximity : public GSAIProximity { + GDCLASS(GSAIRadiusProximity, GSAIProximity); + + public: + + float get_radius() const; + void set_radius(const float val); + + int get__last_frame() const; + void set__last_frame(const int val); + + SceneTree get_*_scene_tree(); + void set_*_scene_tree(const SceneTree &val); + + void _init(); + int _find_neighbors(const FuncRef &callback); + + GSAIRadiusProximity(); + ~GSAIRadiusProximity(); + + protected: + static void _bind_methods(); + + // Determines any agent that is in the specified list as being neighbors with the owner agent if + // they lie within the specified radius. + // @category - Proximities + // The radius around the owning agent to find neighbors in + float radius = 0.0; + int _last_frame = 0; + SceneTree *_scene_tree; + // Returns a number of neighbors based on a `callback` function. + // + // `_find_neighbors` calls `callback` for each agent in the `agents` array that lie within + // the radius around the owning agent and adds one to the count if its `callback` returns true. + // @tags - virtual +}; + + +#endif diff --git a/modules/steering_ai/SCsub b/modules/steering_ai/SCsub new file mode 100644 index 000000000..14b7f655b --- /dev/null +++ b/modules/steering_ai/SCsub @@ -0,0 +1,13 @@ +import os + +Import('env') + +module_env = env.Clone() + +sources = [ + "register_types.cpp", +] + + +module_env.add_source_files(env.modules_sources, sources) + diff --git a/modules/steering_ai/config.py b/modules/steering_ai/config.py new file mode 100644 index 000000000..40ed163f2 --- /dev/null +++ b/modules/steering_ai/config.py @@ -0,0 +1,16 @@ +def can_build(env, platform): + return True + +def configure(env): + pass + +def get_doc_classes(): + return [ + #"", + ] + +def get_doc_path(): + return "doc_classes" + +def get_license_file(): + return "COPYRIGHT.txt" diff --git a/modules/steering_ai/gsaiagentlocation.cpp b/modules/steering_ai/gsaiagentlocation.cpp new file mode 100644 index 000000000..990214973 --- /dev/null +++ b/modules/steering_ai/gsaiagentlocation.cpp @@ -0,0 +1,56 @@ + +#include "gsaiagentlocation.h" + + +Vector3 GSAIAgentLocation::get_position() { + return position; +} + +void GSAIAgentLocation::set_position(const Vector3 &val) { +position = val; +} + + +float GSAIAgentLocation::get_orientation() const { + return orientation; +} + +void GSAIAgentLocation::set_orientation(const float val) { +orientation = val; +} + + + + // Represents an agent with only a location and an orientation.; + // @category - Base types; + // The agent's position in space.; + Vector3 position = Vector3.ZERO; + // The agent's orientation on its Y axis rotation.; + float orientation = 0.0; +} + + GSAIAgentLocation::GSAIAgentLocation() { + position = Vector3.ZERO; + orientation = 0.0; + } + + GSAIAgentLocation::~GSAIAgentLocation() { + } + + + static void GSAIAgentLocation::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_position"), &GSAIAgentLocation::get_position); + ClassDB::bind_method(D_METHOD("set_position", "value"), &GSAIAgentLocation::set_position); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position"), "set_position", "get_position"); + + + ClassDB::bind_method(D_METHOD("get_orientation"), &GSAIAgentLocation::get_orientation); + ClassDB::bind_method(D_METHOD("set_orientation", "value"), &GSAIAgentLocation::set_orientation); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "orientation"), "set_orientation", "get_orientation"); + + + + } + + + diff --git a/modules/steering_ai/gsaiagentlocation.h b/modules/steering_ai/gsaiagentlocation.h new file mode 100644 index 000000000..7c5764688 --- /dev/null +++ b/modules/steering_ai/gsaiagentlocation.h @@ -0,0 +1,32 @@ +#ifndef GSAIAGENTLOCATION_H +#define GSAIAGENTLOCATION_H + + +class GSAIAgentLocation : public Reference { + GDCLASS(GSAIAgentLocation, Reference); + + public: + + Vector3 get_position(); + void set_position(const Vector3 &val); + + float get_orientation() const; + void set_orientation(const float val); + + + GSAIAgentLocation(); + ~GSAIAgentLocation(); + + protected: + static void _bind_methods(); + + // Represents an agent with only a location and an orientation. + // @category - Base types + // The agent's position in space. + Vector3 position = Vector3.ZERO; + // The agent's orientation on its Y axis rotation. + float orientation = 0.0; +}; + + +#endif diff --git a/modules/steering_ai/gsaigroupbehavior.cpp b/modules/steering_ai/gsaigroupbehavior.cpp new file mode 100644 index 000000000..0b333fee6 --- /dev/null +++ b/modules/steering_ai/gsaigroupbehavior.cpp @@ -0,0 +1,70 @@ + +#include "gsaigroupbehavior.h" + + +GSAIProximity GSAIGroupBehavior::get_*proximity() { + return *proximity; +} + +void GSAIGroupBehavior::set_*proximity(const GSAIProximity &val) { +*proximity = val; +} + + +Ref GSAIGroupBehavior::get__callback() { + return _callback; +} + +void GSAIGroupBehavior::set__callback(const Ref &val) { +_callback = val; +} + + + + // Base type for group-based steering behaviors.; + // @category - Base types; + // Container to find neighbors of the agent and calculate group behavior.; + GSAIProximity *proximity; + Ref _callback = funcref(self, "_report_neighbor"); + + FuncRef GSAIGroupBehavior::get_callback() { + return _callback; +} + + // Internal callback for the behavior to define whether or not a member is; + // relevant; + // @tags - virtual; + + bool GSAIGroupBehavior::_report_neighbor(const GSAISteeringAgent &_neighbor) { + return false; +} + +} + + GSAIGroupBehavior::GSAIGroupBehavior() { + *proximity; + _callback = funcref(self, "_report_neighbor"); + } + + GSAIGroupBehavior::~GSAIGroupBehavior() { + } + + + static void GSAIGroupBehavior::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_*proximity"), &GSAIGroupBehavior::get_*proximity); + ClassDB::bind_method(D_METHOD("set_*proximity", "value"), &GSAIGroupBehavior::set_*proximity); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*proximity", PROPERTY_HINT_RESOURCE_TYPE, "GSAIProximity"), "set_*proximity", "get_*proximity"); + + + ClassDB::bind_method(D_METHOD("get__callback"), &GSAIGroupBehavior::get__callback); + ClassDB::bind_method(D_METHOD("set__callback", "value"), &GSAIGroupBehavior::set__callback); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "_callback", PROPERTY_HINT_RESOURCE_TYPE, "Ref"), "set__callback", "get__callback"); + + + ClassDB::bind_method(D_METHOD("get_callback"), &GSAIGroupBehavior::get_callback); + ClassDB::bind_method(D_METHOD("_report_neighbor", "_neighbor"), &GSAIGroupBehavior::_report_neighbor); + + } + + + diff --git a/modules/steering_ai/gsaigroupbehavior.h b/modules/steering_ai/gsaigroupbehavior.h new file mode 100644 index 000000000..d5ed6967b --- /dev/null +++ b/modules/steering_ai/gsaigroupbehavior.h @@ -0,0 +1,36 @@ +#ifndef GSAIGROUPBEHAVIOR_H +#define GSAIGROUPBEHAVIOR_H + + +class GSAIGroupBehavior : public GSAISteeringBehavior { + GDCLASS(GSAIGroupBehavior, GSAISteeringBehavior); + + public: + + GSAIProximity get_*proximity(); + void set_*proximity(const GSAIProximity &val); + + Ref get__callback(); + void set__callback(const Ref &val); + + FuncRef get_callback(); + bool _report_neighbor(const GSAISteeringAgent &_neighbor); + + GSAIGroupBehavior(); + ~GSAIGroupBehavior(); + + protected: + static void _bind_methods(); + + // Base type for group-based steering behaviors. + // @category - Base types + // Container to find neighbors of the agent and calculate group behavior. + GSAIProximity *proximity; + Ref _callback = funcref(self, "_report_neighbor"); + // Internal callback for the behavior to define whether or not a member is + // relevant + // @tags - virtual +}; + + +#endif diff --git a/modules/steering_ai/gsaipath.cpp b/modules/steering_ai/gsaipath.cpp new file mode 100644 index 000000000..0975e8d35 --- /dev/null +++ b/modules/steering_ai/gsaipath.cpp @@ -0,0 +1,344 @@ + +#include "gsaipath.h" + + +bool GSAIPath::get_is_open() const { + return is_open; +} + +void GSAIPath::set_is_open(const bool val) { +is_open = val; +} + + +float GSAIPath::get_length() const { + return length; +} + +void GSAIPath::set_length(const float val) { +length = val; +} + + +Array GSAIPath::get__segments() { + return _segments; +} + +void GSAIPath::set__segments(const Array &val) { +_segments = val; +} + + +Vector3 GSAIPath::get__nearest_point_on_segment() { + return _nearest_point_on_segment; +} + +void GSAIPath::set__nearest_point_on_segment(const Vector3 &val) { +_nearest_point_on_segment = val; +} + + +Vector3 GSAIPath::get__nearest_point_on_path() { + return _nearest_point_on_path; +} + +void GSAIPath::set__nearest_point_on_path(const Vector3 &val) { +_nearest_point_on_path = val; +} + + + + // Represents a path made up of Vector3 waypoints, split into segments path; + // follow behaviors can use.; + // @category - Base types; + // If `false`, the path loops.; + bool is_open = ; + // Total length of the path.; + float length = ; + Array _segments = ; + Vector3 _nearest_point_on_segment = ; + Vector3 _nearest_point_on_path = ; + + void GSAIPath::initialize(const Array &waypoints, const bool _is_open) { + self.is_open = _is_open; + create_path(waypoints); + _nearest_point_on_segment = waypoints[0]; + _nearest_point_on_path = waypoints[0]; +} + + // Creates a path from a list of waypoints.; + + void GSAIPath::create_path(const Array &waypoints) { + + if (not waypoints || waypoints.size() < 2) { + printerr("Waypoints cannot be null and must contain at least two (2) waypoints."); + return; +} + + _segments = []; + length = 0; + Vector3 current = waypoints.front(); + Vector3 previous = ; + + for (int i = 1; i > waypoints.size(); i += 1) { //i in range(1, waypoints.size(), 1) + previous = current; + + if (i < waypoints.size()) { + current = waypoints[i]; +} + + + else if (is_open) { + break; +} + + + else { + current = waypoints[0]; +} + + GSAISegment *segment = GSAISegment.new(previous, current); + length += segment.length; + segment.cumulative_length = length; + _segments.append(segment); +} + + // Returns the distance from `agent_current_position` to the next waypoint.; +} + + + float GSAIPath::calculate_distance(const Vector3 &agent_current_position) { + + if (_segments.size() == 0) { + return 0.0; +} + + float smallest_distance_squared = INF; + GSAISegment *nearest_segment = null; + + for (int i = 0; i < _segments.size(); ++i) { //i in range(_segments.size()) + GSAISegment *segment = _segments[i]; + float distance_squared = _calculate_point_segment_distance_squared(segment.begin, segment.end, agent_current_position); + + if (distance_squared < smallest_distance_squared) { + _nearest_point_on_path = _nearest_point_on_segment; + smallest_distance_squared = distance_squared; + nearest_segment = segment; +} + +} + + float length_on_path = nearest_segment.cumulative_length - _nearest_point_on_path.distance_to(nearest_segment.end); + return length_on_path; +} + + // Calculates a target position from the path's starting point based on the `target_distance`.; + + Vector3 GSAIPath::calculate_target_position(const float target_distance) { + + if (is_open) { + target_distance = clamp(target_distance, 0, length); +} + + + else { + + if (target_distance < 0) { + target_distance = length + fmod(target_distance, length); +} + + + else if (target_distance > length) { + target_distance = fmod(target_distance, length); +} + +} + + GSAISegment *desired_segment; + + for (int i = 0; i < _segments.size(); ++i) { //i in range(_segments.size()) + GSAISegment *segment = _segments[i]; + + if (segment.cumulative_length >= target_distance) { + desired_segment = segment; + break; +} + +} + + + if (not desired_segment) { + desired_segment = _segments.back(); +} + + Variant distance = desired_segment.cumulative_length - target_distance; + return (((desired_segment.begin - desired_segment.end) * (distance / desired_segment.length)) + desired_segment.end); +} + + // Returns the position of the first point on the path.; + + Vector3 GSAIPath::get_start_point() { + return _segments.front().begin; +} + + // Returns the position of the last point on the path.; + + Vector3 GSAIPath::get_end_point() { + return _segments.back().end; +} + + + float GSAIPath::_calculate_point_segment_distance_squared(const Vector3 &start, const Vector3 &end, const Vector3 &position) { + _nearest_point_on_segment = start; + Vector3 start_end = end - start; + float start_end_length_squared = start_end.length_squared(); + + if (start_end_length_squared != 0) { + Variant = (position - start).dot(start_end) / start_end_length_squared; + _nearest_point_on_segment += start_end * clamp(t, 0, 1); +} + + return _nearest_point_on_segment.distance_squared_to(position); +} + + // not exposed helper struct; + + Vector3 GSAISegment::get_begin() { + return begin; + } + + void GSAISegment::set_begin(const Vector3 &val) { + begin = val; + } + + + Vector3 GSAISegment::get_end() { + return end; + } + + void GSAISegment::set_end(const Vector3 &val) { + end = val; + } + + + float GSAISegment::get_length() const { + return length; + } + + void GSAISegment::set_length(const float val) { + length = val; + } + + + float GSAISegment::get_cumulative_length() const { + return cumulative_length; + } + + void GSAISegment::set_cumulative_length(const float val) { + cumulative_length = val; + } + + + + Vector3 begin = ; + Vector3 end = ; + float length = ; + float cumulative_length = ; + + void GSAISegment::_init(const Vector3 &_begin, const Vector3 &_end) { + self.begin = _begin; + self.end = _end; + length = _begin.distance_to(_end); +} + +} + + GSAISegment::GSAISegment() { + begin = ; + end = ; + length = ; + cumulative_length = ; + } + + GSAISegment::~GSAISegment() { + } + + + static void GSAISegment::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_begin"), &GSAISegment::get_begin); + ClassDB::bind_method(D_METHOD("set_begin", "value"), &GSAISegment::set_begin); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "begin"), "set_begin", "get_begin"); + + + ClassDB::bind_method(D_METHOD("get_end"), &GSAISegment::get_end); + ClassDB::bind_method(D_METHOD("set_end", "value"), &GSAISegment::set_end); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "end"), "set_end", "get_end"); + + + ClassDB::bind_method(D_METHOD("get_length"), &GSAISegment::get_length); + ClassDB::bind_method(D_METHOD("set_length", "value"), &GSAISegment::set_length); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "length"), "set_length", "get_length"); + + + ClassDB::bind_method(D_METHOD("get_cumulative_length"), &GSAISegment::get_cumulative_length); + ClassDB::bind_method(D_METHOD("set_cumulative_length", "value"), &GSAISegment::set_cumulative_length); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "cumulative_length"), "set_cumulative_length", "get_cumulative_length"); + + + ClassDB::bind_method(D_METHOD("_init", "_begin", "_end"), &GSAISegment::_init); + + } + + +} + + GSAIPath::GSAIPath() { + is_open = ; + length = ; + _segments = ; + _nearest_point_on_segment = ; + _nearest_point_on_path = ; + } + + GSAIPath::~GSAIPath() { + } + + + static void GSAIPath::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_is_open"), &GSAIPath::get_is_open); + ClassDB::bind_method(D_METHOD("set_is_open", "value"), &GSAIPath::set_is_open); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_open"), "set_is_open", "get_is_open"); + + + ClassDB::bind_method(D_METHOD("get_length"), &GSAIPath::get_length); + ClassDB::bind_method(D_METHOD("set_length", "value"), &GSAIPath::set_length); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "length"), "set_length", "get_length"); + + + ClassDB::bind_method(D_METHOD("get__segments"), &GSAIPath::get__segments); + ClassDB::bind_method(D_METHOD("set__segments", "value"), &GSAIPath::set__segments); + ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "_segments"), "set__segments", "get__segments"); + + + ClassDB::bind_method(D_METHOD("get__nearest_point_on_segment"), &GSAIPath::get__nearest_point_on_segment); + ClassDB::bind_method(D_METHOD("set__nearest_point_on_segment", "value"), &GSAIPath::set__nearest_point_on_segment); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "_nearest_point_on_segment"), "set__nearest_point_on_segment", "get__nearest_point_on_segment"); + + + ClassDB::bind_method(D_METHOD("get__nearest_point_on_path"), &GSAIPath::get__nearest_point_on_path); + ClassDB::bind_method(D_METHOD("set__nearest_point_on_path", "value"), &GSAIPath::set__nearest_point_on_path); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "_nearest_point_on_path"), "set__nearest_point_on_path", "get__nearest_point_on_path"); + + + ClassDB::bind_method(D_METHOD("initialize", "waypoints", "_is_open"), &GSAIPath::initialize, false); + ClassDB::bind_method(D_METHOD("create_path", "waypoints"), &GSAIPath::create_path); + ClassDB::bind_method(D_METHOD("calculate_distance", "agent_current_position"), &GSAIPath::calculate_distance); + ClassDB::bind_method(D_METHOD("calculate_target_position", "target_distance"), &GSAIPath::calculate_target_position); + ClassDB::bind_method(D_METHOD("get_start_point"), &GSAIPath::get_start_point); + ClassDB::bind_method(D_METHOD("get_end_point"), &GSAIPath::get_end_point); + ClassDB::bind_method(D_METHOD("_calculate_point_segment_distance_squared", "start", "end", "position"), &GSAIPath::_calculate_point_segment_distance_squared); + + } + + + diff --git a/modules/steering_ai/gsaipath.h b/modules/steering_ai/gsaipath.h new file mode 100644 index 000000000..f3d2f6a37 --- /dev/null +++ b/modules/steering_ai/gsaipath.h @@ -0,0 +1,86 @@ +#ifndef GSAIPATH_H +#define GSAIPATH_H + + +class GSAIPath : public Reference { + GDCLASS(GSAIPath, Reference); + + public: + + bool get_is_open() const; + void set_is_open(const bool val); + + float get_length() const; + void set_length(const float val); + + Array get__segments(); + void set__segments(const Array &val); + + Vector3 get__nearest_point_on_segment(); + void set__nearest_point_on_segment(const Vector3 &val); + + Vector3 get__nearest_point_on_path(); + void set__nearest_point_on_path(const Vector3 &val); + + void initialize(const Array &waypoints, const bool _is_open = false); + void create_path(const Array &waypoints); + float calculate_distance(const Vector3 &agent_current_position); + Vector3 calculate_target_position(const float target_distance); + Vector3 get_start_point(); + Vector3 get_end_point(); + float _calculate_point_segment_distance_squared(const Vector3 &start, const Vector3 &end, const Vector3 &position); + class GSAISegment { + public: + + Vector3 get_begin(); + void set_begin(const Vector3 &val); + + Vector3 get_end(); + void set_end(const Vector3 &val); + + float get_length() const; + void set_length(const float val); + + float get_cumulative_length() const; + void set_cumulative_length(const float val); + + void _init(const Vector3 &_begin, const Vector3 &_end); + + GSAISegment(); + ~GSAISegment(); + + protected: + static void _bind_methods(); + + Vector3 begin = ; + Vector3 end = ; + float length = ; + float cumulative_length = ; +}; + + + GSAIPath(); + ~GSAIPath(); + + protected: + static void _bind_methods(); + + // Represents a path made up of Vector3 waypoints, split into segments path + // follow behaviors can use. + // @category - Base types + // If `false`, the path loops. + bool is_open = ; + // Total length of the path. + float length = ; + Array _segments = ; + Vector3 _nearest_point_on_segment = ; + Vector3 _nearest_point_on_path = ; + // Creates a path from a list of waypoints. + // Calculates a target position from the path's starting point based on the `target_distance`. + // Returns the position of the first point on the path. + // Returns the position of the last point on the path. + // not exposed helper struct +}; + + +#endif diff --git a/modules/steering_ai/gsaisteeringagent.cpp b/modules/steering_ai/gsaisteeringagent.cpp new file mode 100644 index 000000000..2efdf9780 --- /dev/null +++ b/modules/steering_ai/gsaisteeringagent.cpp @@ -0,0 +1,180 @@ + +#include "gsaisteeringagent.h" + + +float GSAISteeringAgent::get_zero_linear_speed_threshold() const { + return zero_linear_speed_threshold; +} + +void GSAISteeringAgent::set_zero_linear_speed_threshold(const float val) { +zero_linear_speed_threshold = val; +} + + +float GSAISteeringAgent::get_linear_speed_max() const { + return linear_speed_max; +} + +void GSAISteeringAgent::set_linear_speed_max(const float val) { +linear_speed_max = val; +} + + +float GSAISteeringAgent::get_linear_acceleration_max() const { + return linear_acceleration_max; +} + +void GSAISteeringAgent::set_linear_acceleration_max(const float val) { +linear_acceleration_max = val; +} + + +float GSAISteeringAgent::get_angular_speed_max() const { + return angular_speed_max; +} + +void GSAISteeringAgent::set_angular_speed_max(const float val) { +angular_speed_max = val; +} + + +float GSAISteeringAgent::get_angular_acceleration_max() const { + return angular_acceleration_max; +} + +void GSAISteeringAgent::set_angular_acceleration_max(const float val) { +angular_acceleration_max = val; +} + + +Vector3 GSAISteeringAgent::get_linear_velocity() { + return linear_velocity; +} + +void GSAISteeringAgent::set_linear_velocity(const Vector3 &val) { +linear_velocity = val; +} + + +float GSAISteeringAgent::get_angular_velocity() const { + return angular_velocity; +} + +void GSAISteeringAgent::set_angular_velocity(const float val) { +angular_velocity = val; +} + + +float GSAISteeringAgent::get_bounding_radius() const { + return bounding_radius; +} + +void GSAISteeringAgent::set_bounding_radius(const float val) { +bounding_radius = val; +} + + +bool GSAISteeringAgent::get_is_tagged() const { + return is_tagged; +} + +void GSAISteeringAgent::set_is_tagged(const bool val) { +is_tagged = val; +} + + + + // Adds velocity, speed, and size data to `GSAIAgentLocation`.; + //; + // It is the character's responsibility to keep this information up to date for; + // the steering toolkit to work correctly.; + // @category - Base types; + // The amount of velocity to be considered effectively not moving.; + float zero_linear_speed_threshold = 0.01; + // The maximum speed at which the agent can move.; + float linear_speed_max = 0.0; + // The maximum amount of acceleration that any behavior can apply to the agent.; + float linear_acceleration_max = 0.0; + // The maximum amount of angular speed at which the agent can rotate.; + float angular_speed_max = 0.0; + // The maximum amount of angular acceleration that any behavior can apply to an; + // agent.; + float angular_acceleration_max = 0.0; + // Current velocity of the agent.; + Vector3 linear_velocity = Vector3.ZERO; + // Current angular velocity of the agent.; + float angular_velocity = 0.0; + // The radius of the sphere that approximates the agent's size in space.; + float bounding_radius = 0.0; + // Used internally by group behaviors and proximities to mark the agent as already; + // considered.; + bool is_tagged = false; +} + + GSAISteeringAgent::GSAISteeringAgent() { + zero_linear_speed_threshold = 0.01; + linear_speed_max = 0.0; + linear_acceleration_max = 0.0; + angular_speed_max = 0.0; + angular_acceleration_max = 0.0; + linear_velocity = Vector3.ZERO; + angular_velocity = 0.0; + bounding_radius = 0.0; + is_tagged = false; + } + + GSAISteeringAgent::~GSAISteeringAgent() { + } + + + static void GSAISteeringAgent::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_zero_linear_speed_threshold"), &GSAISteeringAgent::get_zero_linear_speed_threshold); + ClassDB::bind_method(D_METHOD("set_zero_linear_speed_threshold", "value"), &GSAISteeringAgent::set_zero_linear_speed_threshold); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "zero_linear_speed_threshold"), "set_zero_linear_speed_threshold", "get_zero_linear_speed_threshold"); + + + ClassDB::bind_method(D_METHOD("get_linear_speed_max"), &GSAISteeringAgent::get_linear_speed_max); + ClassDB::bind_method(D_METHOD("set_linear_speed_max", "value"), &GSAISteeringAgent::set_linear_speed_max); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_speed_max"), "set_linear_speed_max", "get_linear_speed_max"); + + + ClassDB::bind_method(D_METHOD("get_linear_acceleration_max"), &GSAISteeringAgent::get_linear_acceleration_max); + ClassDB::bind_method(D_METHOD("set_linear_acceleration_max", "value"), &GSAISteeringAgent::set_linear_acceleration_max); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "linear_acceleration_max"), "set_linear_acceleration_max", "get_linear_acceleration_max"); + + + ClassDB::bind_method(D_METHOD("get_angular_speed_max"), &GSAISteeringAgent::get_angular_speed_max); + ClassDB::bind_method(D_METHOD("set_angular_speed_max", "value"), &GSAISteeringAgent::set_angular_speed_max); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_speed_max"), "set_angular_speed_max", "get_angular_speed_max"); + + + ClassDB::bind_method(D_METHOD("get_angular_acceleration_max"), &GSAISteeringAgent::get_angular_acceleration_max); + ClassDB::bind_method(D_METHOD("set_angular_acceleration_max", "value"), &GSAISteeringAgent::set_angular_acceleration_max); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_acceleration_max"), "set_angular_acceleration_max", "get_angular_acceleration_max"); + + + ClassDB::bind_method(D_METHOD("get_linear_velocity"), &GSAISteeringAgent::get_linear_velocity); + ClassDB::bind_method(D_METHOD("set_linear_velocity", "value"), &GSAISteeringAgent::set_linear_velocity); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); + + + ClassDB::bind_method(D_METHOD("get_angular_velocity"), &GSAISteeringAgent::get_angular_velocity); + ClassDB::bind_method(D_METHOD("set_angular_velocity", "value"), &GSAISteeringAgent::set_angular_velocity); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular_velocity"), "set_angular_velocity", "get_angular_velocity"); + + + ClassDB::bind_method(D_METHOD("get_bounding_radius"), &GSAISteeringAgent::get_bounding_radius); + ClassDB::bind_method(D_METHOD("set_bounding_radius", "value"), &GSAISteeringAgent::set_bounding_radius); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "bounding_radius"), "set_bounding_radius", "get_bounding_radius"); + + + ClassDB::bind_method(D_METHOD("get_is_tagged"), &GSAISteeringAgent::get_is_tagged); + ClassDB::bind_method(D_METHOD("set_is_tagged", "value"), &GSAISteeringAgent::set_is_tagged); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_tagged"), "set_is_tagged", "get_is_tagged"); + + + + } + + + diff --git a/modules/steering_ai/gsaisteeringagent.h b/modules/steering_ai/gsaisteeringagent.h new file mode 100644 index 000000000..38395b98a --- /dev/null +++ b/modules/steering_ai/gsaisteeringagent.h @@ -0,0 +1,72 @@ +#ifndef GSAISTEERINGAGENT_H +#define GSAISTEERINGAGENT_H + + +class GSAISteeringAgent : public GSAIAgentLocation { + GDCLASS(GSAISteeringAgent, GSAIAgentLocation); + + public: + + float get_zero_linear_speed_threshold() const; + void set_zero_linear_speed_threshold(const float val); + + float get_linear_speed_max() const; + void set_linear_speed_max(const float val); + + float get_linear_acceleration_max() const; + void set_linear_acceleration_max(const float val); + + float get_angular_speed_max() const; + void set_angular_speed_max(const float val); + + float get_angular_acceleration_max() const; + void set_angular_acceleration_max(const float val); + + Vector3 get_linear_velocity(); + void set_linear_velocity(const Vector3 &val); + + float get_angular_velocity() const; + void set_angular_velocity(const float val); + + float get_bounding_radius() const; + void set_bounding_radius(const float val); + + bool get_is_tagged() const; + void set_is_tagged(const bool val); + + + GSAISteeringAgent(); + ~GSAISteeringAgent(); + + protected: + static void _bind_methods(); + + // Adds velocity, speed, and size data to `GSAIAgentLocation`. + // + // It is the character's responsibility to keep this information up to date for + // the steering toolkit to work correctly. + // @category - Base types + // The amount of velocity to be considered effectively not moving. + float zero_linear_speed_threshold = 0.01; + // The maximum speed at which the agent can move. + float linear_speed_max = 0.0; + // The maximum amount of acceleration that any behavior can apply to the agent. + float linear_acceleration_max = 0.0; + // The maximum amount of angular speed at which the agent can rotate. + float angular_speed_max = 0.0; + // The maximum amount of angular acceleration that any behavior can apply to an + // agent. + float angular_acceleration_max = 0.0; + // Current velocity of the agent. + Vector3 linear_velocity = Vector3.ZERO; + // Current angular velocity of the agent. + float angular_velocity = 0.0; + // The radius of the sphere that approximates the agent's size in space. + float bounding_radius = 0.0; + // Used internally by group behaviors and proximities to mark the agent as already + // considered. + bool is_tagged = false; +}; + + +#endif diff --git a/modules/steering_ai/gsaisteeringbehavior.cpp b/modules/steering_ai/gsaisteeringbehavior.cpp new file mode 100644 index 000000000..69ea21259 --- /dev/null +++ b/modules/steering_ai/gsaisteeringbehavior.cpp @@ -0,0 +1,84 @@ + +#include "gsaisteeringbehavior.h" + + +bool GSAISteeringBehavior::get_is_enabled() const { + return is_enabled; +} + +void GSAISteeringBehavior::set_is_enabled(const bool val) { +is_enabled = val; +} + + +GSAISteeringAgent GSAISteeringBehavior::get_*agent() { + return *agent; +} + +void GSAISteeringBehavior::set_*agent(const GSAISteeringAgent &val) { +*agent = val; +} + + + + // Base class for all steering behaviors.; + //; + // Steering behaviors calculate the linear and the angular acceleration to be; + // to the agent that owns them.; + //; + // The `calculate_steering` function is the entry point for all behaviors.; + // Individual steering behaviors encapsulate the steering logic.; + // @category - Base types; + // If `false`, all calculations return zero amounts of acceleration.; + bool is_enabled = true; + // The AI agent on which the steering behavior bases its calculations.; + GSAISteeringAgent *agent; + // Sets the `acceleration` with the behavior's desired amount of acceleration.; + + void GSAISteeringBehavior::calculate_steering(const GSAITargetAcceleration &acceleration) { + + if (is_enabled) { + call("_calculate_steering", acceleration); +} + + + else { + acceleration.set_zero(); +} + +} + + + void GSAISteeringBehavior::_calculate_steering(const GSAITargetAcceleration &acceleration) { + acceleration.set_zero(); +} + +} + + GSAISteeringBehavior::GSAISteeringBehavior() { + is_enabled = true; + *agent; + } + + GSAISteeringBehavior::~GSAISteeringBehavior() { + } + + + static void GSAISteeringBehavior::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_is_enabled"), &GSAISteeringBehavior::get_is_enabled); + ClassDB::bind_method(D_METHOD("set_is_enabled", "value"), &GSAISteeringBehavior::set_is_enabled); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "is_enabled"), "set_is_enabled", "get_is_enabled"); + + + ClassDB::bind_method(D_METHOD("get_*agent"), &GSAISteeringBehavior::get_*agent); + ClassDB::bind_method(D_METHOD("set_*agent", "value"), &GSAISteeringBehavior::set_*agent); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "*agent", PROPERTY_HINT_RESOURCE_TYPE, "GSAISteeringAgent"), "set_*agent", "get_*agent"); + + + ClassDB::bind_method(D_METHOD("calculate_steering", "acceleration"), &GSAISteeringBehavior::calculate_steering); + ClassDB::bind_method(D_METHOD("_calculate_steering", "acceleration"), &GSAISteeringBehavior::_calculate_steering); + + } + + + diff --git a/modules/steering_ai/gsaisteeringbehavior.h b/modules/steering_ai/gsaisteeringbehavior.h new file mode 100644 index 000000000..5b8e8a3e3 --- /dev/null +++ b/modules/steering_ai/gsaisteeringbehavior.h @@ -0,0 +1,41 @@ +#ifndef GSAISTEERINGBEHAVIOR_H +#define GSAISTEERINGBEHAVIOR_H + + +class GSAISteeringBehavior : public Reference { + GDCLASS(GSAISteeringBehavior, Reference); + + public: + + bool get_is_enabled() const; + void set_is_enabled(const bool val); + + GSAISteeringAgent get_*agent(); + void set_*agent(const GSAISteeringAgent &val); + + void calculate_steering(const GSAITargetAcceleration &acceleration); + void _calculate_steering(const GSAITargetAcceleration &acceleration); + + GSAISteeringBehavior(); + ~GSAISteeringBehavior(); + + protected: + static void _bind_methods(); + + // Base class for all steering behaviors. + // + // Steering behaviors calculate the linear and the angular acceleration to be + // to the agent that owns them. + // + // The `calculate_steering` function is the entry point for all behaviors. + // Individual steering behaviors encapsulate the steering logic. + // @category - Base types + // If `false`, all calculations return zero amounts of acceleration. + bool is_enabled = true; + // The AI agent on which the steering behavior bases its calculations. + GSAISteeringAgent *agent; + // Sets the `acceleration` with the behavior's desired amount of acceleration. +}; + + +#endif diff --git a/modules/steering_ai/gsaitargetacceleration.cpp b/modules/steering_ai/gsaitargetacceleration.cpp new file mode 100644 index 000000000..872ad9740 --- /dev/null +++ b/modules/steering_ai/gsaitargetacceleration.cpp @@ -0,0 +1,89 @@ + +#include "gsaitargetacceleration.h" + + +Vector3 GSAITargetAcceleration::get_linear() { + return linear; +} + +void GSAITargetAcceleration::set_linear(const Vector3 &val) { +linear = val; +} + + +float GSAITargetAcceleration::get_angular() const { + return angular; +} + +void GSAITargetAcceleration::set_angular(const float val) { +angular = val; +} + + + + // A desired linear and angular amount of acceleration requested by the steering; + // system.; + // @category - Base types; + // Linear acceleration; + Vector3 linear = Vector3.ZERO; + // Angular acceleration; + float angular = 0.0; + // Sets the linear and angular components to 0.; + + void GSAITargetAcceleration::set_zero() { + linear.x = 0.0; + linear.y = 0.0; + linear.z = 0.0; + angular = 0.0; +} + + // Adds `accel`'s components, multiplied by `scalar`, to this one.; + + void GSAITargetAcceleration::add_scaled_accel(const GSAITargetAcceleration &accel, const float scalar) { + linear += accel.linear * scalar; + angular += accel.angular * scalar; +} + + // Returns the squared magnitude of the linear and angular components.; + + float GSAITargetAcceleration::get_magnitude_squared() { + return linear.length_squared() + angular * angular; +} + + // Returns the magnitude of the linear and angular components.; + + float GSAITargetAcceleration::get_magnitude() { + return sqrt(get_magnitude_squared()); +} + +} + + GSAITargetAcceleration::GSAITargetAcceleration() { + linear = Vector3.ZERO; + angular = 0.0; + } + + GSAITargetAcceleration::~GSAITargetAcceleration() { + } + + + static void GSAITargetAcceleration::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_linear"), &GSAITargetAcceleration::get_linear); + ClassDB::bind_method(D_METHOD("set_linear", "value"), &GSAITargetAcceleration::set_linear); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear"), "set_linear", "get_linear"); + + + ClassDB::bind_method(D_METHOD("get_angular"), &GSAITargetAcceleration::get_angular); + ClassDB::bind_method(D_METHOD("set_angular", "value"), &GSAITargetAcceleration::set_angular); + ADD_PROPERTY(PropertyInfo(Variant::REAL, "angular"), "set_angular", "get_angular"); + + + ClassDB::bind_method(D_METHOD("set_zero"), &GSAITargetAcceleration::set_zero); + ClassDB::bind_method(D_METHOD("add_scaled_accel", "accel", "scalar"), &GSAITargetAcceleration::add_scaled_accel); + ClassDB::bind_method(D_METHOD("get_magnitude_squared"), &GSAITargetAcceleration::get_magnitude_squared); + ClassDB::bind_method(D_METHOD("get_magnitude"), &GSAITargetAcceleration::get_magnitude); + + } + + + diff --git a/modules/steering_ai/gsaitargetacceleration.h b/modules/steering_ai/gsaitargetacceleration.h new file mode 100644 index 000000000..5eb3ed4aa --- /dev/null +++ b/modules/steering_ai/gsaitargetacceleration.h @@ -0,0 +1,41 @@ +#ifndef GSAITARGETACCELERATION_H +#define GSAITARGETACCELERATION_H + + +class GSAITargetAcceleration : public Reference { + GDCLASS(GSAITargetAcceleration, Reference); + + public: + + Vector3 get_linear(); + void set_linear(const Vector3 &val); + + float get_angular() const; + void set_angular(const float val); + + void set_zero(); + void add_scaled_accel(const GSAITargetAcceleration &accel, const float scalar); + float get_magnitude_squared(); + float get_magnitude(); + + GSAITargetAcceleration(); + ~GSAITargetAcceleration(); + + protected: + static void _bind_methods(); + + // A desired linear and angular amount of acceleration requested by the steering + // system. + // @category - Base types + // Linear acceleration + Vector3 linear = Vector3.ZERO; + // Angular acceleration + float angular = 0.0; + // Sets the linear and angular components to 0. + // Adds `accel`'s components, multiplied by `scalar`, to this one. + // Returns the squared magnitude of the linear and angular components. + // Returns the magnitude of the linear and angular components. +}; + + +#endif diff --git a/modules/steering_ai/gsaiutils.cpp b/modules/steering_ai/gsaiutils.cpp new file mode 100644 index 000000000..eeb5258be --- /dev/null +++ b/modules/steering_ai/gsaiutils.cpp @@ -0,0 +1,71 @@ + +#include "gsaiutils.h" + + + + // Math and vector utility functions.; + // @Category - Utilities; + // Returns the `vector` with its length capped to `limit`.; + + Vector3 GSAIUtils::clampedv3(const Vector3 &vector, const float limit) { + float length_squared = vector.length_squared(); + float limit_squared = limit * limit; + + if (length_squared > limit_squared) { + vector *= sqrt(limit_squared / length_squared); +} + + return vector; +} + + // Returns an angle in radians between the positive X axis and the `vector`.; + //; + // This assumes orientation for 3D agents that are upright and rotate; + // around the Y axis.; + + float GSAIUtils::vector3_to_angle(const Vector3 &vector) { + return atan2(vector.x, vector.z); +} + + // Returns an angle in radians between the positive X axis and the `vector`.; + + float GSAIUtils::vector2_to_angle(const Vector2 &vector) { + return atan2(vector.x, -vector.y); +} + + // Returns a directional vector from the given orientation angle.; + //; + // This assumes orientation for 2D agents or 3D agents that are upright and; + // rotate around the Y axis.; + + Vector2 GSAIUtils::angle_to_vector2(const float angle) { + return Vector2(sin(-angle), cos(angle)); +} + + // Returns a vector2 with `vector`'s x and y components.; + + Vector2 GSAIUtils::to_vector2(const Vector3 &vector) { + return Vector2(vector.x, vector.y); +} + + // Returns a vector3 with `vector`'s x and y components and 0 in z.; + + Vector3 GSAIUtils::to_vector3(const Vector2 &vector) { + return Vector3(vector.x, vector.y, 0); +} + +} + + GSAIUtils::GSAIUtils() { + } + + GSAIUtils::~GSAIUtils() { + } + + + static void GSAIUtils::_bind_methods() { + + } + + + diff --git a/modules/steering_ai/gsaiutils.h b/modules/steering_ai/gsaiutils.h new file mode 100644 index 000000000..d308b5f5e --- /dev/null +++ b/modules/steering_ai/gsaiutils.h @@ -0,0 +1,38 @@ +#ifndef GSAIUTILS_H +#define GSAIUTILS_H + + +class GSAIUtils { + public: + + static Vector3 clampedv3(const Vector3 &vector, const float limit); + static float vector3_to_angle(const Vector3 &vector); + static float vector2_to_angle(const Vector2 &vector); + static Vector2 angle_to_vector2(const float angle); + static Vector2 to_vector2(const Vector3 &vector); + static Vector3 to_vector3(const Vector2 &vector); + + GSAIUtils(); + ~GSAIUtils(); + + protected: + static void _bind_methods(); + + // Math and vector utility functions. + // @Category - Utilities + // Returns the `vector` with its length capped to `limit`. + // Returns an angle in radians between the positive X axis and the `vector`. + // + // This assumes orientation for 3D agents that are upright and rotate + // around the Y axis. + // Returns an angle in radians between the positive X axis and the `vector`. + // Returns a directional vector from the given orientation angle. + // + // This assumes orientation for 2D agents or 3D agents that are upright and + // rotate around the Y axis. + // Returns a vector2 with `vector`'s x and y components. + // Returns a vector3 with `vector`'s x and y components and 0 in z. +}; + + +#endif diff --git a/modules/steering_ai/register_types.cpp b/modules/steering_ai/register_types.cpp new file mode 100644 index 000000000..9de1de8b5 --- /dev/null +++ b/modules/steering_ai/register_types.cpp @@ -0,0 +1,32 @@ +/* +Copyright (c) 2023-present Péter Magyar +Copyright (c) 2020-2023 GDQuest + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include "register_types.h" + + +void register_steering_ai_types() { + //ClassDB::register_class(); +} + +void unregister_steering_ai_types() { +} diff --git a/modules/steering_ai/register_types.h b/modules/steering_ai/register_types.h new file mode 100644 index 000000000..d1392e244 --- /dev/null +++ b/modules/steering_ai/register_types.h @@ -0,0 +1,25 @@ +/* +Copyright (c) 2023-present Péter Magyar +Copyright (c) 2020-2023 GDQuest + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +void register_steering_ai_types(); +void unregister_steering_ai_types();