Edit docstrings in src/Behaviors

This commit is contained in:
Nathan Lovato 2020-01-29 10:04:04 -06:00
parent 4885707145
commit bd41d5987c
16 changed files with 125 additions and 113 deletions

View File

@ -1,16 +1,17 @@
# Calculates acceleration to take an agent to its target's location.
# The calculation will attempt to arrive with zero remaining velocity.
# Calculates acceleration to take an agent to its target's location. The
# calculation attempts to arrive with zero remaining velocity.
class_name GSTArrive
extends GSTSteeringBehavior
# The target whose location the agent will be steered to arrive at
# Target agent to arrive to.
var target: GSTAgentLocation
# The distance from the target for the agent to be considered successfully arrived
# Distance from the target for the agent to be considered successfully
# arrived.
var arrival_tolerance: float
# The distance from the target for the agent to begin slowing down
# Distance from the target for the agent to begin slowing down.
var deceleration_radius: float
# The amount of time to reach the target velocity
# Represents the time it takes to change acceleration.
var time_to_reach := 0.1

View File

@ -1,4 +1,5 @@
# Behavior that steers the agent to avoid obstacles lying in its path as approximated by spheres.
# Steers the agent to avoid obstacles in its path. Approximates obstacles as
# spheres.
class_name GSTAvoidCollisions
extends GSTGroupBehavior
@ -20,22 +21,22 @@ func _calculate_steering(acceleration: GSTTargetAcceleration) -> GSTTargetAccele
first_neighbor = null
first_minimum_separation = 0
first_distance = 0
var neighbor_count := proximity.find_neighbors(_callback)
if neighbor_count == 0 or not first_neighbor:
acceleration.set_zero()
else:
if(
first_minimum_separation <= 0 or
first_minimum_separation <= 0 or
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
return acceleration
@ -45,12 +46,12 @@ func report_neighbor(neighbor: GSTSteeringAgent) -> bool:
var relative_position := neighbor.position - agent.position
var relative_velocity := neighbor.linear_velocity - agent.linear_velocity
var relative_speed_squared := relative_velocity.length_squared()
if relative_speed_squared == 0:
return false
else:
var time_to_collision = -relative_position.dot(relative_velocity) / relative_speed_squared
if time_to_collision <= 0 or time_to_collision >= shortest_time:
return false
else:

View File

@ -1,10 +1,11 @@
# Blends multiple steering behaviors into one, and returns acceleration combining all of them.
#
# Each behavior is associated with a weight - a modifier by which the result will be multiplied by,
# then added to a total acceleration.
#
# Each behavior is stored internally as a `Dictionary` with a `behavior` key with a value of type
# `GSTSteeringBehavior` and a `weight` key with a value of type float.
# Blends multiple steering behaviors into one, and returns a weighted
# acceleration from their calculations.
#
# Stores the behaviors internally as dictionaries of the form
# {
# behavior : GSTSteeringBehavior,
# weight : float
# }
class_name GSTBlend
extends GSTSteeringBehavior
@ -17,13 +18,14 @@ func _init(agent: GSTSteeringAgent).(agent) -> void:
pass
# Adds a behavior to the next index and gives it a `weight` by which its results will be multiplied
# Appends a behavior to the internal array along with its `weight`.
func add(behavior: GSTSteeringBehavior, weight: float) -> void:
behavior.agent = agent
_behaviors.append({behavior = behavior, weight = weight})
# Returns the behavior at the specified `index`. Returns an empty `Dictionary` if none was found.
# Returns the behavior at the specified `index`, or an empty `Dictionary` if
# none was found.
func get_behavior_at(index: int) -> Dictionary:
if _behaviors.size() > index:
return _behaviors[index]
@ -33,18 +35,18 @@ func get_behavior_at(index: int) -> Dictionary:
func _calculate_steering(blended_accel: GSTTargetAcceleration) -> GSTTargetAcceleration:
blended_accel.set_zero()
for i in range(_behaviors.size()):
var bw: Dictionary = _behaviors[i]
bw.behavior.calculate_steering(_accel)
blended_accel.add_scaled_accel(_accel, bw.weight)
blended_accel.linear = GSTUtils.clampedv3(blended_accel.linear, agent.linear_acceleration_max)
blended_accel.angular = clamp(
blended_accel.angular,
-agent.angular_acceleration_max,
agent.angular_acceleration_max
)
return blended_accel

View File

@ -1,5 +1,5 @@
# Group behavior that produces linear acceleration that attempts to move the agent towards the
# center of mass of the agents in the area defined by the Proximity.
# Calculates an acceleration that attempts to move the agent towards the center
# of mass of the agents in the area defined by the `GSTProximity`.
class_name GSTCohesion
extends GSTGroupBehavior

View File

@ -1,4 +1,5 @@
# Calculates acceleration to take an agent away from where a target agent will be.
# Calculates acceleration to take an agent away from where a target agent is
# moving.
class_name GSTEvade
extends GSTPursue

View File

@ -1,5 +1,5 @@
# Calculates angular acceleration to rotate a target to face its target's position.
# The acceleration will attempt to arrive with zero remaining angular velocity.
# Calculates angular acceleration to rotate a target to face its target's
# position. The behavior attemps to arrive with zero remaining angular velocity.
class_name GSTFace
extends GSTMatchOrientation
@ -11,7 +11,7 @@ func _init(agent: GSTSteeringAgent, target: GSTAgentLocation).(agent, target) ->
func _face(acceleration: GSTTargetAcceleration, target_position: Vector3) -> GSTTargetAcceleration:
var to_target := target_position - agent.position
var distance_squared := to_target.length_squared()
if distance_squared < agent.zero_linear_speed_threshold:
acceleration.set_zero()
return acceleration

View File

@ -11,5 +11,5 @@ func _calculate_steering(acceleration: GSTTargetAcceleration) -> GSTTargetAccele
acceleration.linear = (
(agent.position - target.position).normalized() * agent.linear_acceleration_max)
acceleration.angular = 0
return acceleration

View File

@ -3,22 +3,22 @@ class_name GSTFollowPath
extends GSTArrive
# The path to follow and travel along
# The path to follow and travel along.
var path: GSTPath
# The distance along the path to generate the next target position.
var path_offset := 0.0
# Whether to use `GSTArrive` behavior on an open path.
var arrive_enabled := true
# The amount of time in the future to predict the owning agent's position along the path. Setting
# it to 0 will force non-predictive path following.
# 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.
var prediction_time := 0.0
func _init(
agent: GSTSteeringAgent,
path: GSTPath,
path_offset := 0.0,
agent: GSTSteeringAgent,
path: GSTPath,
path_offset := 0.0,
prediction_time := 0.0).(agent, null) -> void:
self.path = path
self.path_offset = path_offset
@ -29,12 +29,12 @@ func _calculate_steering(acceleration: GSTTargetAcceleration) -> GSTTargetAccele
var location := (
agent.position if prediction_time == 0
else agent.position + (agent.linear_velocity * prediction_time))
var distance := path.calculate_distance(location)
var target_distance := distance + path_offset
var target_position := path.calculate_target_position(target_distance)
if arrive_enabled and path.open:
if path_offset >= 0:
if target_distance > path.length - deceleration_radius:
@ -42,9 +42,9 @@ func _calculate_steering(acceleration: GSTTargetAcceleration) -> GSTTargetAccele
else:
if target_distance < deceleration_radius:
return _arrive(acceleration, target_position)
acceleration.linear = (target_position - agent.position).normalized()
acceleration.linear *= agent.linear_acceleration_max
acceleration.angular = 0
return acceleration

View File

@ -1,4 +1,5 @@
# Calculates an angular acceleration to match an agent's orientation to its direction of travel.
# Calculates an angular acceleration to match an agent's orientation to its
# direction of travel.
class_name GSTLookWhereYouGo
extends GSTMatchOrientation

View File

@ -1,14 +1,16 @@
# Calculates an angular acceleration to match an agent's orientation to its target's.
# The calculation will attempt to arrive with zero remaining angular velocity.
# 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.
class_name GSTMatchOrientation
extends GSTSteeringBehavior
# The target orientation for the behavior to try and match rotations to
# The target orientation for the behavior to try and match rotations to.
var target: GSTAgentLocation
# The amount of distance in radians for the behavior to consider itself close enough to match
# The amount of distance in radians for the behavior to consider itself close
# enough to be matching the target agent's rotation.
var alignment_tolerance: float
# The amount of distance in radians from the goal to start slowing down
# The amount of distance in radians from the goal to start slowing down.
var deceleration_radius: float
# The amount of time to reach the target velocity
var time_to_reach: float = 0.1
@ -27,20 +29,20 @@ func _match_orientation(acceleration: GSTTargetAcceleration, desired_orientation
acceleration.set_zero()
else:
var 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
var limited_acceleration := abs(acceleration.angular)
if limited_acceleration > agent.angular_acceleration_max:
acceleration.angular *= agent.angular_acceleration_max / limited_acceleration
acceleration.linear = Vector3.ZERO
return acceleration

View File

@ -1,13 +1,15 @@
# Contains multiple behaviors and returns only the result of the first with non-zero acceleration.
# Container for multiple behaviors that returns the result of the first child
# behavior with non-zero acceleration.
class_name GSTPriority
extends GSTSteeringBehavior
var _behaviors := []
# The index in the behavior array of the last behavior that was selected.
# The index of the last behavior the container prioritized.
var last_selected_index: int
# The amount of acceleration for a behavior to be considered to have effectively zero acceleration
# If a behavior's acceleration is lower than this threshold, the container
# considers it has an acceleration of zero.
var zero_threshold: float
@ -15,13 +17,13 @@ func _init(agent: GSTSteeringAgent, zero_threshold := 0.001).(agent) -> void:
self.zero_threshold = zero_threshold
# Add a steering `behavior` to the pool of behaviors to consider
# Appends a steering behavior as a child of this container.
func add(behavior: GSTSteeringBehavior) -> void:
_behaviors.append(behavior)
# Returns the behavior at the position in the pool referred to by `index`.
# Returns `null` if none were found.
# Returns the behavior at the position in the pool referred to by `index`, or
# `null` if no behavior was found.
func get_behavior_at(index: int) -> GSTSteeringBehavior:
if _behaviors.size() > index:
return _behaviors[index]
@ -31,20 +33,20 @@ func get_behavior_at(index: int) -> GSTSteeringBehavior:
func _calculate_steering(accel: GSTTargetAcceleration) -> GSTTargetAcceleration:
var threshold_squared := zero_threshold * zero_threshold
last_selected_index = -1
var size := _behaviors.size()
if size > 0:
for i in range(size):
last_selected_index = i
var behavior: GSTSteeringBehavior = _behaviors[i]
behavior.calculate_steering(accel)
if accel.get_magnitude_squared() > threshold_squared:
break
else:
accel.set_zero()
return accel

View File

@ -1,12 +1,13 @@
# Calculates acceleration to take an agent to intersect with where a target agent will be, instead
# of where it currently is.
# Calculates an acceleration to make an agent intercept another based on the
# target agent's movement.
class_name GSTPursue
extends GSTSteeringBehavior
# The target agent that the behavior is trying to intercept
# The target agent that the behavior is trying to intercept.
var target: GSTSteeringAgent
# The maximum amount of time in the future for the behavior to predict the target's position
# The maximum amount of time in the future the behavior predicts the target's
# location.
var predict_time_max: float
@ -21,21 +22,21 @@ func _init(
func _calculate_steering(acceleration: GSTTargetAcceleration) -> GSTTargetAcceleration:
var target_position := target.position
var distance_squared := (target_position - agent.position).length_squared()
var speed_squared := agent.linear_velocity.length_squared()
var predict_time := predict_time_max
if speed_squared > 0:
var 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
return acceleration

View File

@ -1,9 +1,10 @@
# Calculates acceleration to take an agent to a target agent's position directly
# Calculates an acceleration to take an agent to a target agent's position
# directly.
class_name GSTSeek
extends GSTSteeringBehavior
# The target that the behavior aims to move the agent to
# The target that the behavior aims to move the agent to.
var target: GSTAgentLocation
@ -15,5 +16,5 @@ func _calculate_steering(acceleration: GSTTargetAcceleration) -> GSTTargetAccele
acceleration.linear = (
(target.position - agent.position).normalized() * agent.linear_acceleration_max)
acceleration.angular = 0
return acceleration

View File

@ -1,15 +1,16 @@
# Group behavior that produces acceleration that repels the agent from the other neighbors that
# are in the area defined by the given `GSTProximity`.
#
# The produced acceleration is an average of all agents under consideration, multiplied by a
# strength decreasing by the inverse square law in relation to distance, and accumulated.
# Calculates an acceleration that repels the agent from its neighbors in the
# given `GSTProximity`.
#
# 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.
class_name GSTSeparation
extends GSTGroupBehavior
# The coefficient to calculate how fast the separation strength decays with distance.
var decay_coefficient := 1.0
# Container for the calculated acceleration.
var acceleration: GSTTargetAcceleration
@ -35,7 +36,7 @@ func report_neighbor(neighbor: GSTSteeringAgent) -> bool:
var strength := decay_coefficient / distance_squared
if strength > acceleration_max:
strength = acceleration_max
acceleration.linear += to_agent * (strength / sqrt(distance_squared))
return true

View File

@ -1,7 +1,6 @@
# Represents a path made up of Vector3 waypoints, split into path segments for use by path
# following behaviors.
extends Reference
class_name GSTPath
# Represents a path made up of Vector3 waypoints, split into segments path
# follow behaviors can use.
extends Reference class_name GSTPath
# If `false`, the path loops.
@ -27,12 +26,12 @@ 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.")
return
_segments = []
length = 0
var current: Vector3 = waypoints.front()
var previous: Vector3
for i in range(1, waypoints.size(), 1):
previous = current
if i < waypoints.size():
@ -60,16 +59,16 @@ func calculate_distance(agent_current_position: Vector3) -> float:
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
var length_on_path := (
nearest_segment.cumulative_length -
nearest_segment.cumulative_length -
_nearest_point_on_path.distance_to(nearest_segment.end))
return length_on_path
@ -82,19 +81,19 @@ func calculate_target_position(target_distance: float) -> Vector3:
target_distance = length + fmod(target_distance, length)
elif target_distance > length:
target_distance = fmod(target_distance, length)
var desired_segment: GSTSegment
for i in range(_segments.size()):
var segment: GSTSegment = _segments[i]
if segment.cumulative_length >= target_distance:
desired_segment = segment
break
if not desired_segment:
desired_segment = _segments.back()
var distance := desired_segment.cumulative_length - target_distance
return (
(desired_segment.begin - desired_segment.end) *
(distance / desired_segment.length) + desired_segment.end)
@ -117,7 +116,7 @@ func _calculate_point_segment_distance_squared(start: Vector3, end: Vector3, pos
if start_end_length_squared != 0:
var t = (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)
@ -126,8 +125,8 @@ class GSTSegment:
var end: Vector3
var length: float
var cumulative_length: float
func _init(begin: Vector3, end: Vector3) -> void:
self.begin = begin
self.end = end

View File

@ -23,34 +23,34 @@ func _init(agent: GSTSteeringAgent, agents: Array, radius: float).(agent, agents
func find_neighbors(callback: FuncRef) -> int:
var agent_count := agents.size()
var neighbor_count := 0
var current_frame := _scene_tree.get_frame() if _scene_tree else -_last_frame
if current_frame != _last_frame:
_last_frame = current_frame
var owner_position := agent.position
for i in range(agent_count):
var current_agent := agents[i] as GSTSteeringAgent
if current_agent != agent:
var distance_squared := owner_position.distance_squared_to(current_agent.position)
var range_to := radius + current_agent.bounding_radius
if distance_squared < range_to * range_to:
if callback.call_func(current_agent):
current_agent.tagged = true
neighbor_count += 1
continue
current_agent.tagged = false
else:
for i in range(agent_count):
var current_agent = agents[i] as GSTSteeringAgent
if current_agent != agent and current_agent.tagged:
if callback.call_func(current_agent):
neighbor_count += 1
return neighbor_count