2020-01-29 17:04:04 +01:00
|
|
|
# Represents a path made up of Vector3 waypoints, split into segments path
|
|
|
|
# follow behaviors can use.
|
2020-02-11 18:33:25 +01:00
|
|
|
class_name GSAIPath
|
2020-01-29 22:53:57 +01:00
|
|
|
extends Reference
|
2020-01-09 18:26:35 +01:00
|
|
|
|
2020-01-28 00:31:10 +01:00
|
|
|
# If `false`, the path loops.
|
2020-01-29 22:53:57 +01:00
|
|
|
var is_open: bool
|
2020-01-28 00:31:10 +01:00
|
|
|
# Total length of the path.
|
2020-01-15 20:42:24 +01:00
|
|
|
var length: float
|
2020-01-09 18:26:35 +01:00
|
|
|
|
2020-01-10 18:15:50 +01:00
|
|
|
var _segments: Array
|
|
|
|
|
2020-01-29 15:26:20 +01:00
|
|
|
var _nearest_point_on_segment: Vector3
|
|
|
|
var _nearest_point_on_path: Vector3
|
2020-01-09 18:26:35 +01:00
|
|
|
|
2020-01-29 16:04:47 +01:00
|
|
|
|
2020-02-11 20:36:06 +01:00
|
|
|
func _init(waypoints: Array, _is_open := false) -> void:
|
|
|
|
self.is_open = _is_open
|
2020-01-29 15:26:20 +01:00
|
|
|
create_path(waypoints)
|
|
|
|
_nearest_point_on_segment = waypoints[0]
|
|
|
|
_nearest_point_on_path = waypoints[0]
|
2020-01-09 18:26:35 +01:00
|
|
|
|
|
|
|
|
2020-01-28 00:31:10 +01:00
|
|
|
# Creates a path from a list of waypoints.
|
2020-01-10 18:15:50 +01:00
|
|
|
func create_path(waypoints: Array) -> void:
|
|
|
|
if not waypoints or waypoints.size() < 2:
|
2020-01-29 15:26:20 +01:00
|
|
|
printerr("Waypoints cannot be null and must contain at least two (2) waypoints.")
|
|
|
|
return
|
2020-01-29 17:04:04 +01:00
|
|
|
|
2020-01-29 15:26:20 +01:00
|
|
|
_segments = []
|
|
|
|
length = 0
|
|
|
|
var current: Vector3 = waypoints.front()
|
|
|
|
var previous: Vector3
|
2020-01-29 17:04:04 +01:00
|
|
|
|
2020-01-10 18:15:50 +01:00
|
|
|
for i in range(1, waypoints.size(), 1):
|
2020-01-29 15:26:20 +01:00
|
|
|
previous = current
|
|
|
|
if i < waypoints.size():
|
|
|
|
current = waypoints[i]
|
2020-01-29 22:53:57 +01:00
|
|
|
elif is_open:
|
2020-01-29 15:26:20 +01:00
|
|
|
break
|
|
|
|
else:
|
|
|
|
current = waypoints[0]
|
2020-02-11 18:33:25 +01:00
|
|
|
var segment := GSAISegment.new(previous, current)
|
2020-01-29 15:26:20 +01:00
|
|
|
length += segment.length
|
|
|
|
segment.cumulative_length = length
|
2020-01-10 18:15:50 +01:00
|
|
|
_segments.append(segment)
|
2020-01-09 18:26:35 +01:00
|
|
|
|
|
|
|
|
2020-01-28 00:31:10 +01:00
|
|
|
# Returns the distance from `agent_current_position` to the next waypoint.
|
2020-01-27 18:57:51 +01:00
|
|
|
func calculate_distance(agent_current_position: Vector3) -> float:
|
2020-01-15 20:42:24 +01:00
|
|
|
if _segments.size() == 0:
|
2020-01-29 15:26:20 +01:00
|
|
|
return 0.0
|
|
|
|
var smallest_distance_squared: float = INF
|
2020-02-11 18:33:25 +01:00
|
|
|
var nearest_segment: GSAISegment
|
2020-01-29 15:26:20 +01:00
|
|
|
for i in range(_segments.size()):
|
2020-02-11 18:33:25 +01:00
|
|
|
var segment: GSAISegment = _segments[i]
|
2020-01-29 15:26:20 +01:00
|
|
|
var distance_squared := _calculate_point_segment_distance_squared(
|
2020-02-14 17:35:18 +01:00
|
|
|
segment.begin, segment.end, agent_current_position
|
|
|
|
)
|
2020-01-29 17:04:04 +01:00
|
|
|
|
2020-01-09 18:26:35 +01:00
|
|
|
if distance_squared < smallest_distance_squared:
|
2020-01-10 18:15:50 +01:00
|
|
|
_nearest_point_on_path = _nearest_point_on_segment
|
2020-01-29 15:26:20 +01:00
|
|
|
smallest_distance_squared = distance_squared
|
|
|
|
nearest_segment = segment
|
2020-01-29 17:04:04 +01:00
|
|
|
|
2020-01-16 09:44:44 +01:00
|
|
|
var length_on_path := (
|
2020-02-14 17:35:18 +01:00
|
|
|
nearest_segment.cumulative_length
|
|
|
|
- _nearest_point_on_path.distance_to(nearest_segment.end)
|
|
|
|
)
|
2020-01-29 17:04:04 +01:00
|
|
|
|
2020-01-09 18:26:35 +01:00
|
|
|
return length_on_path
|
|
|
|
|
|
|
|
|
2020-01-28 00:31:10 +01:00
|
|
|
# Calculates a target position from the path's starting point based on the `target_distance`.
|
2020-01-27 18:57:51 +01:00
|
|
|
func calculate_target_position(target_distance: float) -> Vector3:
|
2020-01-29 22:53:57 +01:00
|
|
|
if is_open:
|
2020-01-29 15:26:20 +01:00
|
|
|
target_distance = clamp(target_distance, 0, length)
|
|
|
|
else:
|
2020-01-09 18:26:35 +01:00
|
|
|
if target_distance < 0:
|
2020-01-29 15:26:20 +01:00
|
|
|
target_distance = length + fmod(target_distance, length)
|
|
|
|
elif target_distance > length:
|
2020-01-15 20:42:24 +01:00
|
|
|
target_distance = fmod(target_distance, length)
|
2020-01-29 17:04:04 +01:00
|
|
|
|
2020-02-11 18:33:25 +01:00
|
|
|
var desired_segment: GSAISegment
|
2020-01-29 15:26:20 +01:00
|
|
|
for i in range(_segments.size()):
|
2020-02-11 18:33:25 +01:00
|
|
|
var segment: GSAISegment = _segments[i]
|
2020-01-29 15:26:20 +01:00
|
|
|
if segment.cumulative_length >= target_distance:
|
|
|
|
desired_segment = segment
|
|
|
|
break
|
2020-01-29 17:04:04 +01:00
|
|
|
|
2020-01-15 20:42:24 +01:00
|
|
|
if not desired_segment:
|
|
|
|
desired_segment = _segments.back()
|
2020-01-29 17:04:04 +01:00
|
|
|
|
2020-01-16 09:44:44 +01:00
|
|
|
var distance := desired_segment.cumulative_length - target_distance
|
2020-01-29 17:04:04 +01:00
|
|
|
|
2020-01-09 18:26:35 +01:00
|
|
|
return (
|
2020-02-14 17:35:18 +01:00
|
|
|
((desired_segment.begin - desired_segment.end) * (distance / desired_segment.length))
|
|
|
|
+ desired_segment.end
|
|
|
|
)
|
2020-01-09 18:26:35 +01:00
|
|
|
|
|
|
|
|
2020-01-28 00:31:10 +01:00
|
|
|
# Returns the position of the first point on the path.
|
2020-01-10 18:15:50 +01:00
|
|
|
func get_start_point() -> Vector3:
|
|
|
|
return _segments.front().begin
|
|
|
|
|
|
|
|
|
2020-01-28 00:31:10 +01:00
|
|
|
# Returns the position of the last point on the path.
|
2020-01-10 18:15:50 +01:00
|
|
|
func get_end_point() -> Vector3:
|
|
|
|
return _segments.back().end
|
|
|
|
|
|
|
|
|
2020-01-29 15:26:20 +01:00
|
|
|
func _calculate_point_segment_distance_squared(start: Vector3, end: Vector3, position: Vector3) -> float:
|
|
|
|
_nearest_point_on_segment = start
|
|
|
|
var start_end := end - start
|
|
|
|
var start_end_length_squared := start_end.length_squared()
|
|
|
|
if start_end_length_squared != 0:
|
2020-01-10 18:15:50 +01:00
|
|
|
var t = (position - start).dot(start_end) / start_end_length_squared
|
|
|
|
_nearest_point_on_segment += start_end * clamp(t, 0, 1)
|
2020-01-29 17:04:04 +01:00
|
|
|
|
2020-01-10 18:15:50 +01:00
|
|
|
return _nearest_point_on_segment.distance_squared_to(position)
|
2020-01-09 18:26:35 +01:00
|
|
|
|
|
|
|
|
2020-02-11 18:33:25 +01:00
|
|
|
class GSAISegment:
|
2020-01-29 15:26:20 +01:00
|
|
|
var begin: Vector3
|
|
|
|
var end: Vector3
|
|
|
|
var length: float
|
|
|
|
var cumulative_length: float
|
2020-01-29 17:04:04 +01:00
|
|
|
|
2020-02-11 20:36:06 +01:00
|
|
|
func _init(_begin: Vector3, _end: Vector3) -> void:
|
|
|
|
self.begin = _begin
|
|
|
|
self.end = _end
|
|
|
|
length = _begin.distance_to(_end)
|