Tanoda
WaypointCircuit.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using UnityEngine;
5#if UNITY_EDITOR
6using UnityEditor;
7
8#endif
9
10namespace Waypoint
11{
12 public class WaypointCircuit : MonoBehaviour
13 {
14 #region FIELDS
15
16 [Serializable]
17 public class WaypointList
18 {
19 [SerializeField] public WaypointCircuit _circuit;
20
21 [SerializeField] public Transform[] _items = new Transform[0];
22 }
23
24 [SerializeField] private RouteType _routeType;
25
26 [SerializeField] private bool _circuit = true;
27
28 [Range(0.0f, 100.0f)] [SerializeField] private float _entryDistance = 2f;
29
30 [Range(0.0f, 100.0f)] [SerializeField] private float _exitDistance = 2f;
31
32 [Range(4, 50)] [SerializeField] private int _curveSmoothing = 50;
33
34 [Range(4, 50)] [SerializeField] private int _curveSmoothingInternal = 10;
35
36 [SerializeField] private bool _isAlwaysDraw;
37
38 private float _length;
39 private int _numPoints;
40 private Vector3[] _points;
41 private float[] _distances;
42 private Vector3 _curveStartPoint;
43 private Vector3 _curveEndPoint;
44
45 private int _p0N;
46 private int _p1N;
47 private int _p2N;
48 private int _p3N;
49
50 private float _i;
51 private Vector3 _p0;
52 private Vector3 _p1;
53 private Vector3 _p2;
54 private Vector3 _p3;
55
57 public List<Vector3> _curvePoints = new List<Vector3>();
58
59 #endregion
60
61 #region PROPERTIES
62
63 private Transform[] WaypointData => _waypointList._items;
64
65 public bool Circuit
66 {
67 get => _circuit;
68 set => _circuit = value;
69 }
70
71 #endregion
72
73 #region UNITY_METHODS
74
75 private void Awake()
76 {
77 if (WaypointData.Length > 1) CachePositionsAndDistances();
78
79 _numPoints = WaypointData.Length;
80
81 CreateData();
82 }
83
84#if UNITY_EDITOR
85 private void OnDrawGizmos()
86 {
87 DrawGizmos(_isAlwaysDraw);
88
89 foreach (var way in WaypointData)
90 if (Selection.Contains(way.gameObject))
91 DrawGizmos(true);
92 }
93
94 private void OnDrawGizmosSelected()
95 {
96 DrawGizmos(true);
97 }
98
99#endif
100
101 #endregion
102
103 #region PUBLIC_METHODS
104
105 public RoutePoint GetRoutePoint(float dist)
106 {
107 var p1 = GetRoutePosition(dist);
108 var p2 = GetRoutePosition(dist + 0.1f);
109 var delta = p2 - p1;
110 return new RoutePoint(p1, delta.normalized);
111 }
112
113 #endregion
114
115 #region PRIVATE_METHODS
116
117 private Vector3 GetRoutePosition(float dist)
118 {
119 var point = 0;
120 dist = Mathf.Repeat(dist, _length);
121
122 while (_distances[point] < dist) ++point;
123
124 _p1N = (point - 1 + _numPoints) % _numPoints;
125 _p2N = point;
126
127 _i = Mathf.InverseLerp(_distances[_p1N], _distances[_p2N], dist);
128
129 if (_routeType == RouteType.External)
130 {
131 _p0N = (point - 2 + _numPoints) % _numPoints;
132 _p3N = (point + 1) % _numPoints;
133
134 _p2N = _p2N % _numPoints;
135
136 _p0 = _points[_p0N];
137 _p1 = _points[_p1N];
138 _p2 = _points[_p2N];
139 _p3 = _points[_p3N];
140
141 return CatMullRom(_p0, _p1, _p2, _p3, _i);
142 }
143
144 _p1N = (point - 1 + _numPoints) % _numPoints;
145 _p2N = point;
146
147 return Vector3.Lerp(_points[_p1N], _points[_p2N], _i);
148 }
149
150 private static Vector3 CatMullRom(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float i)
151 {
152 return 0.5f *
153 (2 * p1 + (-p0 + p2) * i + (2 * p0 - 5 * p1 + 4 * p2 - p3) * i * i +
154 (-p0 + 3 * p1 - 3 * p2 + p3) * i * i * i);
155 }
156
157 private void CachePositionsAndDistances()
158 {
159 _points = new Vector3[WaypointData.Length + 1];
160 _distances = new float[WaypointData.Length + 1];
161
162 float accumulateDistance = 0;
163
164 for (var i = 0; i < _points.Length; i++)
165 {
166 var t1 = WaypointData[i % WaypointData.Length];
167 var t2 = WaypointData[(i + 1) % WaypointData.Length];
168
169 if (t1 != null && t2 != null)
170 {
171 var p1 = t1.position;
172 var p2 = t2.position;
173 _points[i] = WaypointData[i % WaypointData.Length].position;
174 _distances[i] = accumulateDistance;
175 accumulateDistance += (p1 - p2).magnitude;
176 }
177 }
178 }
179
180 private void DrawGizmos(bool selected)
181 {
182 if (!selected) return;
183
184 _waypointList._circuit = this;
185
186 foreach (var child in WaypointData)
187 {
188 Gizmos.color = Color.red;
189 Gizmos.DrawCube(child.transform.position, new Vector3(0.5f, 0.5f, 0.5f));
190 }
191
192 if (WaypointData.Length > 1)
193 {
194 _numPoints = WaypointData.Length;
195
196 CachePositionsAndDistances();
197 _length = Circuit ? _distances[_distances.Length - 1] : _distances[_distances.Length - 2];
198
199 switch (_routeType)
200 {
201 case RouteType.External:
202 CreateExternal();
203 break;
204 case RouteType.Internal:
205 CreateInternal();
206 break;
207 case RouteType.Plain:
208 CreatePlainLine();
209 break;
210 }
211 }
212 }
213
214 private void CreateExternal()
215 {
216 Gizmos.color = Color.yellow;
217
218 var prev = WaypointData[0].position;
219 for (float dist = 0; dist < _length; dist += _length / _curveSmoothing)
220 {
221 var next = GetRoutePosition(dist);
222 Gizmos.DrawLine(prev, next);
223 prev = next;
224 }
225
226 CreatePlainLine();
227 }
228
229 private void CreateInternal()
230 {
231 for (var n = 0; n < WaypointData.Length; n++)
232 {
233 if (!Circuit && n == 0) continue;
234
235 var currentIndex = n;
236 var nextIndex = n + 1;
237 var prevIndex = n - 1;
238
239 if (n - 1 < 0) prevIndex = WaypointData.Length - 1;
240
241 if (n + 1 >= WaypointData.Length) nextIndex = Circuit ? 0 : WaypointData.Length - 1;
242
243 var currentPoint = WaypointData[currentIndex].position;
244
245 _curveStartPoint = Vector3.MoveTowards(currentPoint, WaypointData[nextIndex].position, _entryDistance);
246 _curveEndPoint = Vector3.MoveTowards(currentPoint, WaypointData[prevIndex].position, _exitDistance);
247
248 var prev = _curveStartPoint;
249
250 for (var i = 0; i < _curveSmoothingInternal; i++)
251 {
252 var t = i / (_curveSmoothingInternal - 1.0f);
253 var position = (1.0f - t) * (1.0f - t) * _curveStartPoint + 2.0f * (1.0f - t) * t * currentPoint +
254 t * t * _curveEndPoint;
255
256 Gizmos.color = Color.yellow;
257 Gizmos.DrawLine(prev, position);
258 prev = position;
259 }
260 }
261
262 CreatePlainLine();
263 }
264
265 private void CreatePlainLine()
266 {
267 Gizmos.color = Color.blue;
268
269 for (var n = 0; n < WaypointData.Length; n++)
270 {
271 var currentIndex = n;
272 var nextIndex = n + 1;
273
274 if (nextIndex >= WaypointData.Length) nextIndex = Circuit ? 0 : WaypointData.Length - 1;
275
276 var prev = WaypointData[currentIndex].position;
277 var next = WaypointData[nextIndex].position;
278
279 Gizmos.DrawLine(prev, next);
280 }
281 }
282
283 private void CreateData()
284 {
285 if (WaypointData.Length > 1)
286 {
287 _numPoints = WaypointData.Length;
288 CachePositionsAndDistances();
289 _length = Circuit ? _distances[_distances.Length - 1] : _distances[_distances.Length - 2];
290
291 switch (_routeType)
292 {
293 case RouteType.External:
294 _curvePoints = GetPointExternal();
295 break;
296 case RouteType.Internal:
297 _curvePoints = GetPointInternal();
298 break;
299 case RouteType.Plain:
300 _curvePoints = GetPointPlain();
301 break;
302 }
303 }
304 }
305
306 private List<Vector3> GetPointExternal()
307 {
308 var list = new List<Vector3>();
309
310 for (float dist = 0; dist < _length; dist += _length / _curveSmoothing)
311 {
312 var point = GetRoutePosition(dist);
313 list.Add(point);
314 }
315
316 return list;
317 }
318
319 private List<Vector3> GetPointInternal()
320 {
321 var list = new List<Vector3>();
322 for (var n = 0; n < WaypointData.Length; n++)
323 {
324 if (!Circuit && n == 0) continue;
325
326 var currentIndex = n;
327 var nextIndex = n + 1;
328 var prevIndex = n - 1;
329
330 if (n - 1 < 0) prevIndex = WaypointData.Length - 1;
331
332 if (n + 1 >= WaypointData.Length) nextIndex = Circuit ? 0 : WaypointData.Length - 1;
333
334 var currentPoint = WaypointData[currentIndex].position;
335
336 _curveStartPoint = Vector3.MoveTowards(currentPoint, WaypointData[nextIndex].position, _entryDistance);
337 _curveEndPoint = Vector3.MoveTowards(currentPoint, WaypointData[prevIndex].position, _exitDistance);
338
339 var buffer = new List<Vector3>();
340
341 for (var i = 0; i < _curveSmoothingInternal; i++)
342 {
343 var t = i / (_curveSmoothingInternal - 1.0f);
344 var position = (1.0f - t) * (1.0f - t) * _curveStartPoint + 2.0f * (1.0f - t) * t * currentPoint +
345 t * t * _curveEndPoint;
346
347 buffer.Add(position);
348 }
349
350 buffer.Reverse();
351 list.AddRange(buffer);
352 }
353
354 return list;
355 }
356
357 private List<Vector3> GetPointPlain()
358 {
359 var list = new List<Vector3>();
360 list.AddRange(WaypointData.Select(t => t.position));
361
362 return list;
363 }
364
365 #endregion
366 }
367}
UnityEngine.Color Color
Definition: TestScript.cs:32
List< Vector3 > _curvePoints
RoutePoint GetRoutePoint(float dist)