SHOW:
|
|
- or go back to the newest paste.
1 | - | //#define ASTARDEBUG |
1 | + | |
2 | using System.Collections; | |
3 | using System.Collections.Generic; | |
4 | using Pathfinding; | |
5 | ||
6 | - | using Pathfinding.RVO; |
6 | + | /** Simple movement script. |
7 | * This movement script will follow the path exactly, it uses linear interpolation to move between the waypoints in the path. | |
8 | - | /** AI for 2D top down games. |
8 | + | * This is desirable for some types of games. |
9 | - | * This is one of the default movement scripts which comes with the A* Pathfinding Project. |
9 | + | * It also works in 2D. |
10 | - | * It is in no way required by the rest of the system, so feel free to write your own. But I hope this script will make it easier |
10 | + | |
11 | - | * to set up movement for the characters in your game. |
11 | + | * For nicer movement, I recommend adding the Simple Smooth Modifier to the GameObject as well. |
12 | - | * \n |
12 | + | |
13 | - | * This script will try to follow a target transform, and at regular intervals the path to that target will be recalculated. |
13 | + | |
14 | */ | |
15 | - | * \section variables Quick overview of the variables |
15 | + | |
16 | - | * In the inspector in Unity, you will see a bunch of variables. You can view detailed information further down, but here's a quick overview.\n |
16 | + | [AddComponentMenu("Pathfinding/AI/AISimpleLerp (2D,3D generic)")] |
17 | - | * The #repathRate determines how often it will search for new paths, if you have fast moving targets, you might want to set it to a lower value.\n |
17 | + | public class AISimpleLerp : MonoBehaviour |
18 | - | * The #target variable is where the AI will try to move, it can be a point on the ground where the player has clicked in an RTS for example. |
18 | + | { |
19 | - | * Or it can be the player object in a zombie game.\n |
19 | + | |
20 | - | * The speed is self-explanatory, so is turningSpeed, however #slowdownDistance might require some explanation. |
20 | + | /** Determines how often it will search for new paths. |
21 | - | * It is the approximate distance from the target where the AI will start to slow down. Note that this doesn't only affect the end point of the path |
21 | + | * If you have fast moving targets or AIs, you might want to set it to a lower value. |
22 | - | * but also any intermediate points, so be sure to set #forwardLook and #pickNextWaypointDist to a higher value than this.\n |
22 | + | * The value is in seconds between path requests. |
23 | - | * #pickNextWaypointDist is simply determines within what range it will switch to target the next waypoint in the path.\n |
23 | + | |
24 | - | * #forwardLook will try to calculate an interpolated target point on the current segment in the path so that it has a distance of #forwardLook from the AI\n |
24 | + | public float repathRate = 0.5F; |
25 | - | * Below is an image illustrating several variables as well as some internal ones, but which are relevant for understanding how it works. |
25 | + | |
26 | - | * Note that the #forwardLook range will not match up exactly with the target point practically, even though that's the goal. |
26 | + | /** Target to move towards. |
27 | - | * \shadowimage{aipath_variables.png} |
27 | + | * The AI will try to follow/move towards this target. |
28 | - | * This script can use a Rigidbody2D for movement or it can simply use transform.position. |
28 | + | * It can be a point on the ground where the player has clicked in an RTS for example, or it can be the player object in a zombie game. |
29 | - | * If it finds a Rigidbody2D attached to the same GameObject, it will start to use it automatically. |
29 | + | |
30 | public Transform target; | |
31 | ||
32 | /** Enables or disables searching for paths. | |
33 | * Setting this to false does not stop any active path requests from being calculated or stop it from continuing to follow the current path. | |
34 | - | [AddComponentMenu("Pathfinding/AI/AIPath (2D top down)")] |
34 | + | * \see #canMove |
35 | - | public class AIPath2DTopDown : AIPathBase { |
35 | + | |
36 | public bool canSearch = true; | |
37 | - | /** Cached Rigidbody component */ |
37 | + | |
38 | - | protected Rigidbody2D rigid; |
38 | + | /** Enables or disables movement. |
39 | * \see #canSearch */ | |
40 | public bool canMove = true; | |
41 | ||
42 | /** Speed in world units */ | |
43 | - | protected override void Awake () { |
43 | + | public float speed = 3; |
44 | - | base.Awake (); |
44 | + | |
45 | /** If true, the AI will rotate to face the movement direction */ | |
46 | - | //Cache some other components (not all are necessarily there) |
46 | + | public bool enableRotation = true; |
47 | - | rigid = GetComponent<Rigidbody2D>(); |
47 | + | |
48 | /** If true, rotation will only be done along the Z axis */ | |
49 | public bool rotationIn2D = false; | |
50 | - | public override Vector3 GetFeetPosition () { |
50 | + | |
51 | /** How quickly to rotate */ | |
52 | public float rotationSpeed = 10; | |
53 | ||
54 | - | public virtual void Update () { |
54 | + | /** If true, some interpolation will be done when a new path has been calculated. |
55 | * This is used to avoid short distance teleportation. | |
56 | - | if (!canMove) { return; } |
56 | + | |
57 | public bool interpolatePathSwitches = true; | |
58 | - | Vector2 dir = CalculateVelocity (GetFeetPosition()); |
58 | + | |
59 | /** How quickly to interpolate to the new path */ | |
60 | - | //Rotate towards targetDirection (filled in by CalculateVelocity) |
60 | + | public float switchPathInterpolationSpeed = 5; |
61 | - | RotateTowards (targetDirection); |
61 | + | |
62 | /** Cached Seeker component */ | |
63 | - | if (rigid != null) { |
63 | + | protected Seeker seeker; |
64 | - | //rigid.AddForce (dir, ForceMode2D.Impulse); |
64 | + | |
65 | - | rigid.MovePosition (rigid.position + dir*Time.deltaTime); |
65 | + | /** Cached Transform component */ |
66 | protected Transform tr; | |
67 | - | transform.Translate (dir*Time.deltaTime, Space.World); |
67 | + | |
68 | /** Time when the last path request was sent */ | |
69 | protected float lastRepath = -9999; | |
70 | ||
71 | - | /** Point to where the AI is heading. |
71 | + | /** Current path which is followed */ |
72 | - | * Filled in by #CalculateVelocity */ |
72 | + | protected ABPath path; |
73 | - | protected Vector2 targetPoint; |
73 | + | |
74 | - | /** Relative direction to where the AI is heading. |
74 | + | /** Current index in the path which is current target */ |
75 | - | * Filled in by #CalculateVelocity */ |
75 | + | protected int currentWaypointIndex = 0; |
76 | - | protected Vector2 targetDirection; |
76 | + | |
77 | /** How far the AI has moved on the current segment */ | |
78 | - | /** Calculates desired velocity. |
78 | + | protected float lerpTime = 0; |
79 | - | * Finds the target path segment and returns the forward direction, scaled with speed. |
79 | + | |
80 | - | * A whole bunch of restrictions on the velocity is applied to make sure it doesn't overshoot, does not look too far ahead, |
80 | + | /** Holds if the end-of-path is reached |
81 | - | * and slows down when close to the target. |
81 | + | * \see TargetReached */ |
82 | - | * /see speed |
82 | + | protected bool targetReached = false; |
83 | - | * /see endReachedDistance |
83 | + | |
84 | - | * /see slowdownDistance |
84 | + | /** Only when the previous path has been returned should be search for a new path */ |
85 | - | * /see CalculateTargetPoint |
85 | + | protected bool canSearchAgain = true; |
86 | - | * /see targetPoint |
86 | + | |
87 | - | * /see targetDirection |
87 | + | /** When a new path was returned, the AI was moving along this ray. |
88 | - | * /see currentWaypointIndex |
88 | + | * Used to smoothly interpolate between the previous movement and the movement along the new path. |
89 | * The speed is equal to movement direction. | |
90 | - | protected Vector2 CalculateVelocity (Vector2 currentPosition) { |
90 | + | |
91 | - | if (path == null || path.vectorPath == null || path.vectorPath.Count == 0) return Vector3.zero; |
91 | + | protected Vector3 previousMovementOrigin; |
92 | protected Vector3 previousMovementDirection; | |
93 | protected float previousMovementStartTime = -9999; | |
94 | ||
95 | /** Returns if the end-of-path has been reached | |
96 | * \see targetReached */ | |
97 | - | if (vPath.Count == 1) { |
97 | + | public bool TargetReached { |
98 | - | vPath.Insert (0,currentPosition); |
98 | + | get { |
99 | return targetReached; | |
100 | } | |
101 | } | |
102 | ||
103 | /** Holds if the Start function has been run. | |
104 | - | // Possibly pick the next segment |
104 | + | * Used to test if coroutines should be started in OnEnable to prevent calculating paths |
105 | * in the awake stage (or rather before start on frame 0). | |
106 | - | if (currentWaypointIndex < vPath.Count-1) { |
106 | + | |
107 | - | //There is a "next path segment" |
107 | + | private bool startHasRun = false; |
108 | - | Vector2 nextWaypointDir = (Vector2)vPath[currentWaypointIndex] - currentPosition; |
108 | + | |
109 | - | float dist = nextWaypointDir.sqrMagnitude; |
109 | + | |
110 | - | //Mathfx.DistancePointSegmentStrict (vPath[currentWaypointIndex+1],vPath[currentWaypointIndex+2],currentPosition); |
110 | + | |
111 | - | if (dist < pickNextWaypointDist*pickNextWaypointDist) { |
111 | + | |
112 | - | lastFoundWaypointPosition = currentPosition; |
112 | + | protected virtual void Awake () { |
113 | - | lastFoundWaypointTime = Time.time; |
113 | + | seeker = GetComponent<Seeker>(); |
114 | - | currentWaypointIndex++; |
114 | + | |
115 | //This is a simple optimization, cache the transform component lookup | |
116 | tr = transform; | |
117 | } | |
118 | - | } else { |
118 | + | |
119 | - | break; |
119 | + | /** Starts searching for paths. |
120 | * If you override this function you should in most cases call base.Start () at the start of it. | |
121 | * \see OnEnable | |
122 | * \see RepeatTrySearchPath | |
123 | - | // Calculate the point we should move towards |
123 | + | |
124 | - | Vector2 targetPosition = CalculateTargetPoint (currentPosition,vPath[currentWaypointIndex-1] , vPath[currentWaypointIndex]); |
124 | + | protected virtual void Start () { |
125 | startHasRun = true; | |
126 | - | // Vector to the target position |
126 | + | OnEnable (); |
127 | - | Vector2 dir = targetPosition-currentPosition; |
127 | + | |
128 | - | float targetDist = dir.magnitude; |
128 | + | |
129 | /** Run at start and when reenabled. | |
130 | - | float slowdown = Mathf.Clamp01 (targetDist / slowdownDistance); |
130 | + | * Starts RepeatTrySearchPath. |
131 | * | |
132 | - | this.targetDirection = dir; |
132 | + | * \see Start |
133 | - | this.targetPoint = targetPosition; |
133 | + | |
134 | protected virtual void OnEnable () { | |
135 | - | if (currentWaypointIndex == vPath.Count-1 && targetDist <= endReachedDistance) { |
135 | + | |
136 | - | if (!targetReached) { targetReached = true; OnTargetReached (); } |
136 | + | lastRepath = -9999; |
137 | canSearchAgain = true; | |
138 | - | //Send a move request, this ensures gravity is applied |
138 | + | |
139 | - | return Vector3.zero; |
139 | + | if (startHasRun) { |
140 | //Make sure we receive callbacks when paths complete | |
141 | seeker.pathCallback += OnPathComplete; | |
142 | - | // The Y axis is forward in 2D space |
142 | + | |
143 | - | Vector2 forward = -tr.up; |
143 | + | StartCoroutine (RepeatTrySearchPath ()); |
144 | - | float dot = Vector2.Dot (dir.normalized,forward); |
144 | + | |
145 | - | float sp = speed * Mathf.Max (dot,minMoveScale) * slowdown; |
145 | + | |
146 | ||
147 | - | #if ASTARDEBUG |
147 | + | public void OnDisable () { |
148 | - | Debug.DrawLine (vPath[currentWaypointIndex-1] , vPath[currentWaypointIndex],Color.black); |
148 | + | // Abort calculation of path |
149 | - | Debug.DrawLine (GetFeetPosition(),targetPosition,Color.red); |
149 | + | if (seeker != null && !seeker.IsDone()) seeker.GetCurrentPath().Error(); |
150 | - | Debug.DrawRay (targetPosition,Vector3.up, Color.red); |
150 | + | |
151 | - | Debug.DrawRay (GetFeetPosition(),dir,Color.yellow); |
151 | + | // Release current path |
152 | - | Debug.DrawRay (GetFeetPosition(),forward*sp,Color.cyan); |
152 | + | if (path != null) path.Release (this); |
153 | - | #endif |
153 | + | path = null; |
154 | ||
155 | - | // Make sure we don't overshoot the target |
155 | + | //Make sure we receive callbacks when paths complete |
156 | - | // when the framerate is low |
156 | + | seeker.pathCallback -= OnPathComplete; |
157 | - | if (Time.deltaTime > 0) { |
157 | + | |
158 | - | sp = Mathf.Clamp (sp,0,targetDist/(Time.deltaTime*2)); |
158 | + | |
159 | /** Tries to search for a path every #repathRate seconds. | |
160 | - | return forward*sp; |
160 | + | * \see TrySearchPath |
161 | */ | |
162 | protected IEnumerator RepeatTrySearchPath () { | |
163 | - | /** Rotates in the specified direction. |
163 | + | |
164 | - | * Rotates around the Y-axis. |
164 | + | float v = TrySearchPath (); |
165 | - | * \see turningSpeed |
165 | + | yield return new WaitForSeconds (v); |
166 | } | |
167 | - | protected void RotateTowards (Vector2 dir) { |
167 | + | |
168 | ||
169 | - | if (dir == Vector2.zero) return; |
169 | + | /** Tries to search for a path. |
170 | * Will search for a new path if there was a sufficient time since the last repath and both | |
171 | - | // Figure out the angle so that the Y axis faces dir |
171 | + | * #canSearchAgain and #canSearch are true and there is a target. |
172 | - | float angle = Mathf.Atan2 (dir.x, -dir.y)*Mathf.Rad2Deg; |
172 | + | * |
173 | * \returns The time to wait until calling this function again (based on #repathRate) | |
174 | - | Vector3 rot = tr.eulerAngles; |
174 | + | |
175 | - | rot.z = Mathf.LerpAngle (rot.z, angle, turningSpeed * Time.deltaTime); |
175 | + | public float TrySearchPath () { |
176 | - | tr.eulerAngles = rot; |
176 | + | if (Time.time - lastRepath >= repathRate && canSearchAgain && canSearch && target != null) { |
177 | SearchPath (); | |
178 | return repathRate; | |
179 | - | /** Calculates target point from the current line segment. |
179 | + | |
180 | - | * \param p Current position |
180 | + | float v = repathRate - (Time.time-lastRepath); |
181 | - | * \param a Line segment start |
181 | + | return v < 0 ? 0 : v; |
182 | - | * \param b Line segment end |
182 | + | |
183 | - | * The returned point will lie somewhere on the line segment. |
183 | + | |
184 | - | * \see #forwardLook |
184 | + | |
185 | - | * \todo This function uses .magnitude quite a lot, can it be optimized? |
185 | + | /** Requests a path to the target. |
186 | * Some inheriting classes will prevent the path from being requested immediately when | |
187 | - | protected Vector2 CalculateTargetPoint (Vector2 p, Vector2 a, Vector2 b) { |
187 | + | * this function is called, for example when the AI is currently traversing a special path segment |
188 | * in which case it is usually a bad idea to search for a new path. | |
189 | - | float magn = (a-b).magnitude; |
189 | + | */ |
190 | - | if (magn == 0) return a; |
190 | + | public virtual void SearchPath () { |
191 | ForceSearchPath (); | |
192 | - | float closest = AstarMath.Clamp01 (AstarMath.NearestPointFactor (a, b, p)); |
192 | + | |
193 | - | Vector2 point = (b-a)*closest + a; |
193 | + | |
194 | - | float distance = (point-p).magnitude; |
194 | + | /** Requests a path to the target. |
195 | * Bypasses 'is-it-a-good-time-to-request-a-path' checks. | |
196 | - | float lookAhead = Mathf.Clamp (forwardLook - distance, 0.0F, forwardLook); |
196 | + | */ |
197 | public virtual void ForceSearchPath () { | |
198 | - | float offset = lookAhead / magn; |
198 | + | if (target == null) throw new System.InvalidOperationException ("Target is null"); |
199 | - | offset = Mathf.Clamp (offset+closest,0.0F,1.0F); |
199 | + | |
200 | - | return (b-a)*offset + a; |
200 | + | lastRepath = Time.time; |
201 | //This is where we should search to | |
202 | Vector3 targetPosition = target.position; | |
203 | ||
204 | canSearchAgain = false; | |
205 | ||
206 | //Alternative way of requesting the path | |
207 | //ABPath p = ABPath.Construct (GetFeetPosition(),targetPoint,null); | |
208 | //seeker.StartPath (p); | |
209 | ||
210 | //We should search from the current position | |
211 | seeker.StartPath (GetFeetPosition(), targetPosition); | |
212 | } | |
213 | ||
214 | /** The end of the path has been reached. | |
215 | * If you want custom logic for when the AI has reached it's destination | |
216 | * add it here | |
217 | * You can also create a new script which inherits from this one | |
218 | * and override the function in that script. | |
219 | */ | |
220 | public virtual void OnTargetReached () { | |
221 | ||
222 | } | |
223 | ||
224 | /** Called when a requested path has finished calculation. | |
225 | * A path is first requested by #SearchPath, it is then calculated, probably in the same or the next frame. | |
226 | * Finally it is returned to the seeker which forwards it to this function.\n | |
227 | */ | |
228 | public virtual void OnPathComplete (Path _p) { | |
229 | ABPath p = _p as ABPath; | |
230 | if (p == null) throw new System.Exception ("This function only handles ABPaths, do not use special path types"); | |
231 | ||
232 | canSearchAgain = true; | |
233 | ||
234 | //Claim the new path | |
235 | p.Claim (this); | |
236 | ||
237 | // Path couldn't be calculated of some reason. | |
238 | // More info in p.errorLog (debug string) | |
239 | if (p.error) { | |
240 | p.Release (this); | |
241 | return; | |
242 | } | |
243 | ||
244 | if (interpolatePathSwitches) { | |
245 | ConfigurePathSwitchInterpolation (); | |
246 | } | |
247 | ||
248 | //Release the previous path | |
249 | if (path != null) path.Release (this); | |
250 | ||
251 | //Replace the old path | |
252 | path = p; | |
253 | ||
254 | // Just for the rest of the code to work, if there is only one waypoint in the path | |
255 | // add another one | |
256 | if (path.vectorPath != null && path.vectorPath.Count == 1) { | |
257 | path.vectorPath.Insert (0,GetFeetPosition()); | |
258 | } | |
259 | ||
260 | targetReached = false; | |
261 | ||
262 | //Reset some variables | |
263 | ConfigureNewPath (); | |
264 | } | |
265 | ||
266 | protected virtual void ConfigurePathSwitchInterpolation () { | |
267 | if (path != null && path.vectorPath != null && path.vectorPath.Count > 1) { | |
268 | ||
269 | List<Vector3> vPath = path.vectorPath; | |
270 | ||
271 | // Make sure we stay inside valid ranges | |
272 | currentWaypointIndex = Mathf.Clamp (currentWaypointIndex, 1, vPath.Count - 1); | |
273 | ||
274 | // Current segment vector | |
275 | Vector3 segment = vPath [currentWaypointIndex] - vPath [currentWaypointIndex - 1]; | |
276 | float segmentLength = segment.magnitude; | |
277 | ||
278 | // Find the approximate length of the path that is left on the current path | |
279 | float approximateLengthLeft = segmentLength * Mathf.Clamp01 (1 - lerpTime); | |
280 | for (int i = currentWaypointIndex; i < vPath.Count-1; i++) { | |
281 | approximateLengthLeft += (vPath [i + 1] - vPath [i]).magnitude; | |
282 | } | |
283 | ||
284 | previousMovementOrigin = GetFeetPosition (); | |
285 | previousMovementDirection = segment.normalized * approximateLengthLeft; | |
286 | previousMovementStartTime = Time.time; | |
287 | } else { | |
288 | previousMovementOrigin = Vector3.zero; | |
289 | previousMovementDirection = Vector3.zero; | |
290 | previousMovementStartTime = -9999; | |
291 | } | |
292 | } | |
293 | ||
294 | public virtual Vector3 GetFeetPosition () { | |
295 | return tr.position; | |
296 | } | |
297 | ||
298 | /** Finds the closest point on the current path. | |
299 | * Sets #currentWaypointIndex and #lerpTime to the appropriate values. | |
300 | */ | |
301 | protected virtual void ConfigureNewPath () { | |
302 | var points = path.vectorPath; | |
303 | ||
304 | var currentPosition = GetFeetPosition (); | |
305 | ||
306 | float bestFactor = 0; | |
307 | float bestDist = float.PositiveInfinity; | |
308 | int bestIndex = 0; | |
309 | ||
310 | for (int i = 0; i < points.Count-1; i++) { | |
311 | float factor = AstarMath.NearestPointFactor (points[i], points[i+1], currentPosition); | |
312 | Vector3 point = Vector3.Lerp (points[i], points[i+1], factor); | |
313 | float dist = (currentPosition - point).sqrMagnitude; | |
314 | ||
315 | if (dist < bestDist) { | |
316 | bestDist = dist; | |
317 | bestFactor = factor; | |
318 | bestIndex = i+1; | |
319 | } | |
320 | } | |
321 | ||
322 | currentWaypointIndex = bestIndex; | |
323 | lerpTime = bestFactor; | |
324 | } | |
325 | ||
326 | protected virtual void Update () { | |
327 | if (canMove) { | |
328 | Vector3 direction; | |
329 | Vector3 nextPos = CalculateNextPosition (out direction); | |
330 | ||
331 | // Rotate unless we are really close to the target | |
332 | if (enableRotation && direction != Vector3.zero) { | |
333 | ||
334 | if (rotationIn2D) { | |
335 | ||
336 | float angle = Mathf.Atan2(-direction.x, direction.y) * Mathf.Rad2Deg + 180; | |
337 | Vector3 euler = tr.eulerAngles; | |
338 | euler.z = Mathf.LerpAngle (euler.z, angle, Time.deltaTime * rotationSpeed); | |
339 | tr.eulerAngles = euler; | |
340 | } else { | |
341 | ||
342 | Quaternion rot = tr.rotation; | |
343 | Quaternion desiredRot = Quaternion.LookRotation (direction); | |
344 | ||
345 | tr.rotation = Quaternion.Slerp (rot, desiredRot, Time.deltaTime * rotationSpeed); | |
346 | } | |
347 | } | |
348 | ||
349 | tr.position = nextPos; | |
350 | } | |
351 | } | |
352 | ||
353 | /** Calculate the AI's next position (one frame in the future). | |
354 | * \param direction The direction of the segment the AI is currently traversing. Not normalized. | |
355 | */ | |
356 | protected virtual Vector3 CalculateNextPosition ( out Vector3 direction ) { | |
357 | ||
358 | if (path == null || path.vectorPath == null || path.vectorPath.Count == 0) { | |
359 | direction = Vector3.zero; | |
360 | return Vector3.zero; | |
361 | } | |
362 | ||
363 | List<Vector3> vPath = path.vectorPath; | |
364 | ||
365 | // Make sure we stay inside valid ranges | |
366 | currentWaypointIndex = Mathf.Clamp (currentWaypointIndex, 1, vPath.Count - 1); | |
367 | ||
368 | // Current segment vector | |
369 | Vector3 segment = vPath[currentWaypointIndex] - vPath[currentWaypointIndex - 1]; | |
370 | float segmentLength = segment.magnitude; | |
371 | ||
372 | if (segmentLength > 0) { | |
373 | // Move forwards | |
374 | // lerpTime is between 0 and 1 | |
375 | lerpTime += Time.deltaTime * speed / segmentLength; | |
376 | } else { | |
377 | // Make sure zero length segments are handled correctly | |
378 | lerpTime = 1; | |
379 | } | |
380 | ||
381 | // Pick the next segment if we have traversed the current one completely | |
382 | if (lerpTime > 1 && currentWaypointIndex < vPath.Count-1) { | |
383 | float overshootDistance = (lerpTime-1) * segmentLength; | |
384 | ||
385 | while (true) { | |
386 | currentWaypointIndex++; | |
387 | ||
388 | // Next segment vector | |
389 | Vector3 nextSegment = vPath[currentWaypointIndex] - vPath[currentWaypointIndex - 1]; | |
390 | float nextSegmentLength = segment.magnitude; | |
391 | ||
392 | if (overshootDistance <= nextSegmentLength || currentWaypointIndex == vPath.Count-1) { | |
393 | segment = nextSegment; | |
394 | segmentLength = nextSegmentLength; | |
395 | lerpTime = Mathf.Clamp01 (overshootDistance/nextSegmentLength); | |
396 | break; | |
397 | } else { | |
398 | overshootDistance -= nextSegmentLength; | |
399 | } | |
400 | } | |
401 | } | |
402 | ||
403 | if (lerpTime >= 1 && currentWaypointIndex == vPath.Count - 1) { | |
404 | if (!targetReached) { | |
405 | OnTargetReached (); | |
406 | } | |
407 | targetReached = true; | |
408 | } | |
409 | ||
410 | // Find our position along the path using a simple linear interpolation | |
411 | Vector3 positionAlongCurrentPath = segment * Mathf.Clamp01 (lerpTime) + vPath [currentWaypointIndex - 1]; | |
412 | ||
413 | direction = segment; | |
414 | ||
415 | if (interpolatePathSwitches) { | |
416 | // Find the approximate position we would be at if we | |
417 | // would have continued to follow the previous path | |
418 | Vector3 positionAlongPreviousPath = previousMovementOrigin + Vector3.ClampMagnitude (previousMovementDirection, speed * (Time.time - previousMovementStartTime)); | |
419 | ||
420 | // Use this to debug | |
421 | //Debug.DrawLine (previousMovementOrigin, positionAlongPreviousPath, Color.yellow); | |
422 | ||
423 | ||
424 | return Vector3.Lerp (positionAlongPreviousPath, positionAlongCurrentPath, switchPathInterpolationSpeed * (Time.time - previousMovementStartTime)); | |
425 | } else { | |
426 | return positionAlongCurrentPath; | |
427 | } | |
428 | } | |
429 | } |