Skip to content

Commit 18804cd

Browse files
committed
Add new FollowPath script
1 parent 4304862 commit 18804cd

File tree

2 files changed

+198
-0
lines changed

2 files changed

+198
-0
lines changed

Runtime/FollowPath.cs

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
using UnityEngine;
2+
3+
namespace Zigurous.Animation
4+
{
5+
/// <summary>
6+
/// Moves an object along a predefined path.
7+
/// </summary>
8+
public sealed class FollowPath : MonoBehaviour
9+
{
10+
/// <summary>
11+
/// A type of looping behavior.
12+
/// </summary>
13+
public enum LoopType
14+
{
15+
/// <summary>
16+
/// Turns off looping.
17+
/// </summary>
18+
None,
19+
20+
/// <summary>
21+
/// Restarts the object from the beginning of the path after it
22+
/// reaches the end. The object will jump to the position of the
23+
/// first node.
24+
/// </summary>
25+
Restart,
26+
27+
/// <summary>
28+
/// After reaching the end of the path, the object will traverse
29+
/// back to the first node and continue with the next loop.
30+
/// </summary>
31+
Circular,
32+
33+
/// <summary>
34+
/// The object traverses along the path forwards then backwards then
35+
/// forwards then backwards, etc.
36+
/// </summary>
37+
PingPong,
38+
}
39+
40+
/// <summary>
41+
/// The object follows the path comprised of all the children of this
42+
/// transform.
43+
/// </summary>
44+
[Tooltip("The object follows the path comprised of all the children of this transform.")]
45+
public Transform path;
46+
47+
/// <summary>
48+
/// The transform of the node that the object is currently moving from.
49+
/// </summary>
50+
public Transform nodeFrom { get; private set; }
51+
52+
/// <summary>
53+
/// The transform of the node that the object is currently moving to.
54+
/// </summary>
55+
public Transform nodeTo { get; private set; }
56+
57+
/// <summary>
58+
/// The index of the node that the object is currently moving to.
59+
/// </summary>
60+
public int currentIndex { get; private set; }
61+
62+
/// <summary>
63+
/// How quickly the object moves between nodes. Small numbers make the
64+
/// object more responsive. Larger numbers make the object respond more
65+
/// slowly.
66+
/// </summary>
67+
[Tooltip("How quickly the object moves between nodes. Small numbers make the object more responsive. Larger numbers make the object respond more slowly.")]
68+
public float damping = 1.0f;
69+
70+
/// <summary>
71+
/// The maximum speed the object can move between nodes.
72+
/// </summary>
73+
[Tooltip("The maximum speed the object can move between nodes.")]
74+
public float maxSpeed = Mathf.Infinity;
75+
76+
/// <summary>
77+
/// Once the object is less than this distance to the current node, then
78+
/// it will advance to the next one.
79+
/// </summary>
80+
[Tooltip("Once the object is less than this distance to the current node, then it will advance to the next one.")]
81+
public float minProximity = 0.1f;
82+
83+
/// <summary>
84+
/// The rate at which the object is moving.
85+
/// </summary>
86+
private Vector3 _velocity;
87+
88+
/// <summary>
89+
/// The looping behavior, if desired.
90+
/// </summary>
91+
[Tooltip("The looping behavior, if desired.")]
92+
public LoopType looping;
93+
94+
/// <summary>
95+
/// Moves the object between nodes in reverse.
96+
/// </summary>
97+
[Tooltip("Moves the object between nodes in reverse.")]
98+
public bool reversed;
99+
100+
private void Start()
101+
{
102+
this.currentIndex = 0;
103+
104+
SetCurrentSegment();
105+
}
106+
107+
private void Update()
108+
{
109+
if (this.nodeTo == null) {
110+
return;
111+
}
112+
113+
this.transform.position = Vector3.SmoothDamp(
114+
current: this.transform.position,
115+
target: this.nodeTo.position,
116+
currentVelocity: ref _velocity,
117+
smoothTime: this.damping,
118+
maxSpeed: this.maxSpeed);
119+
120+
Vector3 vector = this.transform.position - this.nodeTo.position;
121+
122+
if (vector.sqrMagnitude < this.minProximity) {
123+
Next();
124+
}
125+
}
126+
127+
private void Next()
128+
{
129+
if (this.path == null) {
130+
return;
131+
}
132+
133+
if (this.reversed) {
134+
this.currentIndex--;
135+
} else {
136+
this.currentIndex++;
137+
}
138+
139+
if (this.currentIndex >= this.path.childCount || this.currentIndex < 0)
140+
{
141+
Loop();
142+
SetCurrentSegment();
143+
144+
if (this.looping == LoopType.Restart && this.nodeTo != null) {
145+
this.transform.position = this.nodeTo.position;
146+
}
147+
}
148+
else
149+
{
150+
SetCurrentSegment();
151+
}
152+
}
153+
154+
private void Loop()
155+
{
156+
int lastIndex = Mathf.Max(this.path.childCount - 1, 0);
157+
158+
switch (this.looping)
159+
{
160+
case LoopType.None:
161+
this.currentIndex = Mathf.Clamp(this.currentIndex, 0, lastIndex);
162+
break;
163+
164+
case LoopType.Restart:
165+
case LoopType.Circular:
166+
this.currentIndex = this.reversed ? lastIndex : 0;
167+
break;
168+
169+
case LoopType.PingPong:
170+
this.reversed = !this.reversed;
171+
this.currentIndex = this.reversed ? lastIndex : 0;
172+
break;
173+
}
174+
}
175+
176+
private void SetCurrentSegment()
177+
{
178+
this.nodeFrom = this.nodeTo;
179+
180+
if (this.currentIndex >= 0 && this.currentIndex < this.path.childCount) {
181+
this.nodeTo = this.path.GetChild(this.currentIndex);
182+
}
183+
}
184+
185+
}
186+
187+
}

Runtime/FollowPath.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)