diff --git a/project/src/Behaviors/GSTFace.gd b/project/src/Behaviors/GSTFace.gd index 45e7983..ab9bd61 100644 --- a/project/src/Behaviors/GSTFace.gd +++ b/project/src/Behaviors/GSTFace.gd @@ -16,7 +16,7 @@ func _face(acceleration: GSTTargetAcceleration, target_position: Vector3) -> GST acceleration.set_zero() return acceleration else: - var orientation = atan2(to_target.x, -to_target.y) + var orientation = GSTUtils.vector_to_angle(to_target) return _match_orientation(acceleration, orientation) diff --git a/project/src/Behaviors/GSTFollowPath.gd b/project/src/Behaviors/GSTFollowPath.gd index 25149ff..e3c649f 100644 --- a/project/src/Behaviors/GSTFollowPath.gd +++ b/project/src/Behaviors/GSTFollowPath.gd @@ -6,8 +6,6 @@ extends GSTArrive var path: GSTPath var path_offset := 0.0 -var path_param := {segment_index = 0, distance = 0} - var arrive_enabled := true var prediction_time := 0.0 @@ -27,10 +25,10 @@ func _calculate_steering(acceleration: GSTTargetAcceleration) -> GSTTargetAccele agent.position if prediction_time == 0 else agent.position + (agent.linear_velocity * prediction_time)) - var distance := path.calculate_distance(location, path_param) + var distance := path.calculate_distance(location) var target_distance := distance + path_offset - var target_position := path.calculate_target_position(path_param, target_distance) + var target_position := path.calculate_target_position(target_distance) if arrive_enabled and path.open: if path_offset >= 0: diff --git a/project/src/Behaviors/GSTLookWhereYouGo.gd b/project/src/Behaviors/GSTLookWhereYouGo.gd index 460233b..3057c1f 100644 --- a/project/src/Behaviors/GSTLookWhereYouGo.gd +++ b/project/src/Behaviors/GSTLookWhereYouGo.gd @@ -12,5 +12,5 @@ func _calculate_steering(accel: GSTTargetAcceleration) -> GSTTargetAcceleration: accel.set_zero() return accel else: - var orientation := atan2(agent.linear_velocity.x, -agent.linear_velocity.y) + var orientation := GSTUtils.vector_to_angle(agent.linear_velocity) return _match_orientation(accel, orientation) diff --git a/project/src/GSTAgentLocation.gd b/project/src/GSTAgentLocation.gd index 9a6261e..9b2a788 100644 --- a/project/src/GSTAgentLocation.gd +++ b/project/src/GSTAgentLocation.gd @@ -1,6 +1,8 @@ class_name GSTAgentLocation -# Data type to represent an agent with a location and an orientation +# Data type to represent an agent with a location and an orientation only. +# The agent's position in space. var position := Vector3.ZERO +# The agent's orientation on its Y axis rotation. var orientation := 0.0 diff --git a/project/src/GSTGroupBehavior.gd b/project/src/GSTGroupBehavior.gd index 6e98159..309a8a2 100644 --- a/project/src/GSTGroupBehavior.gd +++ b/project/src/GSTGroupBehavior.gd @@ -3,14 +3,17 @@ class_name GSTGroupBehavior # Extended behavior that features a Proximity group for group-based behaviors. +# The group area definition that will be used to find the owning agent's neighbors var proximity: GSTProximity var _callback := funcref(self, "report_neighbor") +# Initializes the behavior with its owning `agent` and group's `proximity` func _init(agent: GSTSteeringAgent, proximity: GSTProximity).(agent) -> void: self.proximity = proximity +# Internal callback for the behavior to define whether or not a member is relevant func report_neighbor(neighbor: GSTSteeringAgent) -> bool: return false diff --git a/project/src/GSTPath.gd b/project/src/GSTPath.gd index bc5cd57..8625101 100644 --- a/project/src/GSTPath.gd +++ b/project/src/GSTPath.gd @@ -1,12 +1,12 @@ extends Reference class_name GSTPath # Represents a path made up of Vector3 waypoints, split into path segments for use by path -# following algorithms. - -# # Keeping it updated requires calling `create_path` to update the path. +# following behaviors. +# Whether the path is a loop, or has its start and end disconnected and open ended var open: bool +# The length of the full path var length: float var _segments: Array @@ -15,6 +15,7 @@ var _nearest_point_on_segment: Vector3 var _nearest_point_on_path: Vector3 +# Initializes and creates a path with the provided waypoints func _init(waypoints: Array, open := false) -> void: self.open = open create_path(waypoints) @@ -22,6 +23,7 @@ func _init(waypoints: Array, open := false) -> void: _nearest_point_on_path = waypoints[0] +# Creates a path with the provided waypoints func create_path(waypoints: Array) -> void: if not waypoints or waypoints.size() < 2: printerr("Waypoints cannot be null and must contain at least two (2) waypoints.") @@ -46,7 +48,8 @@ func create_path(waypoints: Array) -> void: _segments.append(segment) -func calculate_distance(agent_current_position: Vector3, path_parameter: Dictionary) -> float: +# Returns the distance from the provided `agent_current_position` to the next point waypoint. +func calculate_distance(agent_current_position: Vector3) -> float: if _segments.size() == 0: return 0.0 var smallest_distance_squared: float = INF @@ -63,18 +66,17 @@ func calculate_distance(agent_current_position: Vector3, path_parameter: Diction _nearest_point_on_path = _nearest_point_on_segment smallest_distance_squared = distance_squared nearest_segment = segment - path_parameter.segment_index = i var length_on_path := ( nearest_segment.cumulative_length - _nearest_point_on_path.distance_to(nearest_segment.end)) - path_parameter.distance = length_on_path - return length_on_path -func calculate_target_position(param: Dictionary, target_distance: float) -> Vector3: +# Calculates the target position on the path based on the provided `target_distance` from the +# path's starting point. +func calculate_target_position(target_distance: float) -> Vector3: if open: target_distance = clamp(target_distance, 0, length) else: @@ -100,10 +102,12 @@ func calculate_target_position(param: Dictionary, target_distance: float) -> Vec (distance / desired_segment.length) + desired_segment.end) +# Returns the position of the beginning point of the path func get_start_point() -> Vector3: return _segments.front().begin +# Returns the position of the last point of the path func get_end_point() -> Vector3: return _segments.back().end diff --git a/project/src/GSTSteeringAgent.gd b/project/src/GSTSteeringAgent.gd index 11fe727..5944a4d 100644 --- a/project/src/GSTSteeringAgent.gd +++ b/project/src/GSTSteeringAgent.gd @@ -1,14 +1,24 @@ extends GSTAgentLocation class_name GSTSteeringAgent -# Extended agent data type that adds velocity, speed, and size data +# An extended `GSTAgentLocation` that adds velocity, speed, and size data. It is the character's +# responsibility to keep this information up to date for the steering toolkit to work correctly. +# The amount of velocity to be considered effectively not moving. var zero_linear_speed_threshold := 0.01 +# The maximum amount of speed the agent can move at. var linear_speed_max := 0.0 +# The maximum amount of acceleration that any behavior can apply to an agent. var linear_acceleration_max := 0.0 +# The maximum amount of angular speed the agent can rotate at. var angular_speed_max := 0.0 +# The maximum amount of angular acceleration that any behavior can apply to an agent. var angular_acceleration_max := 0.0 +# The current speed in a given direction the agent is traveling at. var linear_velocity := Vector3.ZERO +# The current angular speed the agent is traveling at. var angular_velocity := 0.0 +# The radius of the sphere that approximates the agent's size in space. var bounding_radius := 0.0 +# Used internally by group behaviors and proximities to mark an agent as already considered. var tagged := false diff --git a/project/src/GSTSteeringBehavior.gd b/project/src/GSTSteeringBehavior.gd index b8ac9e0..b55b8e5 100644 --- a/project/src/GSTSteeringBehavior.gd +++ b/project/src/GSTSteeringBehavior.gd @@ -1,15 +1,21 @@ class_name GSTSteeringBehavior -# Base class to calculate how an AI agent steers itself. +# The base class for all steering behaviors to extend. A steering behavior calculates the linear +# and/or angular acceleration to be applied to its owning agent +# Whether this behavior is enabled. All disabled behaviors return zero amounts of acceleration. +# Defaults to true var enabled := true +# The agent on which all steering calculations are to be made. var agent: GSTSteeringAgent +# Sets the behavior's owning `agent` func _init(agent: GSTSteeringAgent) -> void: self.agent = agent +# Returns the `acceleration` parameter modified with the behavior's desired amount of acceleration func calculate_steering(acceleration: GSTTargetAcceleration) -> GSTTargetAcceleration: if enabled: return _calculate_steering(acceleration) diff --git a/project/src/GSTTargetAcceleration.gd b/project/src/GSTTargetAcceleration.gd index 4a6c74b..0668f33 100644 --- a/project/src/GSTTargetAcceleration.gd +++ b/project/src/GSTTargetAcceleration.gd @@ -1,11 +1,14 @@ class_name GSTTargetAcceleration -# A linear and angular amount of acceleration. +# A desired linear and angular amount of acceleration requested by the steering system. +# The linear amount of acceleration var linear := Vector3.ZERO +# The angular amount of acceleration var angular := 0.0 +# Sets the linear and angular components to 0 func set_zero() -> void: linear.x = 0.0 linear.y = 0.0 @@ -13,14 +16,17 @@ func set_zero() -> void: angular = 0.0 +# Adds `accel`'s components, multiplied by `scalar`, to this one. func add_scaled_accel(accel: GSTTargetAcceleration, scalar: float) -> void: linear += accel.linear * scalar angular += accel.angular * scalar +# Returns the squared magnitude of the linear and angular components func get_magnitude_squared() -> float: return linear.length_squared() + angular * angular +# Returns the magnitude of the linear and angular components func get_magnitude() -> float: return sqrt(get_magnitude_squared()) diff --git a/project/src/GSTUtils.gd b/project/src/GSTUtils.gd index f63caf1..9f8064e 100644 --- a/project/src/GSTUtils.gd +++ b/project/src/GSTUtils.gd @@ -1,10 +1,17 @@ class_name GSTUtils -# Useful math and utility functions to complement Godot's own. +# Useful math and vector manipulation functions to complement Godot's own. +# Returns the provided `vector` with its length capped to `limit`. static func clampedv3(vector: Vector3, limit: float) -> Vector3: var length_squared := vector.length_squared() var limit_squared := limit * limit if length_squared > limit_squared: vector *= sqrt(limit_squared / length_squared) return vector + + +# Returns the provided vector as an angle in radians. This assumes orientation for 2D +# agents or 3D agents that are upright and rotate around the Y axis. +static func vector_to_angle(vector: Vector3) -> float: + return atan2(vector.x, -vector.y)