-
-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathGSAIPath.gd
More file actions
133 lines (103 loc) · 3.7 KB
/
GSAIPath.gd
File metadata and controls
133 lines (103 loc) · 3.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# Represents a path made up of Vector3 waypoints, split into segments path
# follow behaviors can use.
# @category - Base types
class_name GSAIPath
extends Reference
# If `false`, the path loops.
var is_open: bool
# Total length of the path.
var length: float
var _segments: Array
var _nearest_point_on_segment: Vector3
var _nearest_point_on_path: Vector3
func _init(waypoints: Array, _is_open := false) -> void:
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.
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():
current = waypoints[i]
elif is_open:
break
else:
current = waypoints[0]
var 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.
func calculate_distance(agent_current_position: Vector3) -> float:
if _segments.size() == 0:
return 0.0
var smallest_distance_squared: float = INF
var nearest_segment: GSAISegment
for i in range(_segments.size()):
var segment: GSAISegment = _segments[i]
var 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
var 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`.
func calculate_target_position(target_distance: float) -> Vector3:
if is_open:
target_distance = clamp(target_distance, 0, length)
else:
if target_distance < 0:
target_distance = length + fmod(target_distance, length)
elif target_distance > length:
target_distance = fmod(target_distance, length)
var desired_segment: GSAISegment
for i in range(_segments.size()):
var segment: GSAISegment = _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
)
# Returns the position of the first point on the path.
func get_start_point() -> Vector3:
return _segments.front().begin
# Returns the position of the last point on the path.
func get_end_point() -> Vector3:
return _segments.back().end
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:
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)
class GSAISegment:
var begin: Vector3
var end: Vector3
var length: float
var cumulative_length: float
func _init(_begin: Vector3, _end: Vector3) -> void:
self.begin = _begin
self.end = _end
length = _begin.distance_to(_end)