1 using System;
2 using
System.Collections;
3 using
UnityEngine;
4 #
if UNITY_EDITOR
5 using
UnityEditor;
6
7 #endif

8
9 namespace
UnityStandardAssets.Utility
10 {
11     
public class WaypointCircuit : MonoBehaviour
12     {
13         
public WaypointList waypointList = new WaypointList();
14         [SerializeField]
private bool smoothRoute = true;
15         
private int numPoints;
16         
private Vector3[] points;
17         
private float[] distances;
18
19         
public float editorVisualisationSubsteps = 100;
20         
public float Length { get; private set; }
21
22         
public Transform[] Waypoints
23         {
24             
get { return waypointList.items; }
25         }
26
27         
//this being here will save GC allocs
28         
private int p0n;
29         
private int p1n;
30         
private int p2n;
31         
private int p3n;
32
33         
private float i;
34         
private Vector3 P0;
35         
private Vector3 P1;
36         
private Vector3 P2;
37         
private Vector3 P3;
38
39         
// Use this for initialization
40         
private void Awake()
41         {
42             
if (Waypoints.Length > 1)
43             {
44                 CachePositionsAndDistances();
45             }
46             numPoints = Waypoints.Length;
47         }
48
49
50         
public RoutePoint GetRoutePoint(float dist)
51         {
52             
// position and direction
53             Vector3 p1 = GetRoutePosition(dist);
54             Vector3 p2 = GetRoutePosition(dist +
0.1f);
55             Vector3 delta = p2 - p1;
56             
return new RoutePoint(p1, delta.normalized);
57         }
58
59
60         
public Vector3 GetRoutePosition(float dist)
61         {
62             
int point = 0;
63
64             
if (Length == 0)
65             {
66                 Length = distances[distances.Length -
1];
67             }
68
69             dist = Mathf.Repeat(dist, Length);
70
71             
while (distances[point] < dist)
72             {
73                 ++point;
74             }
75
76
77             
// get nearest two points, ensuring points wrap-around start & end of circuit
78             p1n = ((point -
1) + numPoints)%numPoints;
79             p2n = point;
80
81             
// found point numbers, now find interpolation value between the two middle points
82
83             i = Mathf.InverseLerp(distances[p1n], distances[p2n], dist);
84
85             
if (smoothRoute)
86             {
87                 
// smooth catmull-rom calculation between the two relevant points
88
89
90                 
// get indices for the surrounding 2 points, because
91                 
// four points are required by the catmull-rom function
92                 p0n = ((point -
2) + numPoints)%numPoints;
93                 p3n = (point +
1)%numPoints;
94
95                 
// 2nd point may have been the 'last' point - a dupe of the first,
96                 
// (to give a value of max track distance instead of zero)
97                 
// but now it must be wrapped back to zero if that was the case.
98                 p2n = p2n%numPoints;
99
100                 P0 = points[p0n];
101                 P1 = points[p1n];
102                 P2 = points[p2n];
103                 P3 = points[p3n];
104
105                 
return CatmullRom(P0, P1, P2, P3, i);
106             }
107             
else
108             {
109                 
// simple linear lerp between the two points:
110
111                 p1n = ((point -
1) + numPoints)%numPoints;
112                 p2n = point;
113
114                 
return Vector3.Lerp(points[p1n], points[p2n], i);
115             }
116         }
117
118
119         
private Vector3 CatmullRom(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float i)
120         {
121             
// comments are no use here... it's the catmull-rom equation.
122             
// Un-magic this, lord vector!
123             
return 0.5f*
124                    ((
2*p1) + (-p0 + p2)*i + (2*p0 - 5*p1 + 4*p2 - p3)*i*i +
125                     (-p0 +
3*p1 - 3*p2 + p3)*i*i*i);
126         }
127
128
129         
private void CachePositionsAndDistances()
130         {
131             
// transfer the position of each point and distances between points to arrays for
132             
// speed of lookup at runtime
133             points =
new Vector3[Waypoints.Length + 1];
134             distances =
new float[Waypoints.Length + 1];
135
136             
float accumulateDistance = 0;
137             
for (int i = 0; i < points.Length; ++i)
138             {
139                 
var t1 = Waypoints[(i)%Waypoints.Length];
140                 
var t2 = Waypoints[(i + 1)%Waypoints.Length];
141                 
if (t1 != null && t2 != null)
142                 {
143                     Vector3 p1 = t1.position;
144                     Vector3 p2 = t2.position;
145                     points[i] = Waypoints[i%Waypoints.Length].position;
146                     distances[i] = accumulateDistance;
147                     accumulateDistance += (p1 - p2).magnitude;
148                 }
149             }
150         }
151
152
153         
private void OnDrawGizmos()
154         {
155             DrawGizmos(
false);
156         }
157
158
159         
private void OnDrawGizmosSelected()
160         {
161             DrawGizmos(
true);
162         }
163
164
165         
private void DrawGizmos(bool selected)
166         {
167             waypointList.circuit =
this;
168             
if (Waypoints.Length > 1)
169             {
170                 numPoints = Waypoints.Length;
171
172                 CachePositionsAndDistances();
173                 Length = distances[distances.Length -
1];
174
175                 Gizmos.color = selected ? Color.yellow :
new Color(1, 1, 0, 0.5f);
176                 Vector3 prev = Waypoints[
0].position;
177                 
if (smoothRoute)
178                 {
179                     
for (float dist = 0; dist < Length; dist += Length/editorVisualisationSubsteps)
180                     {
181                         Vector3 next = GetRoutePosition(dist +
1);
182                         Gizmos.DrawLine(prev, next);
183                         prev = next;
184                     }
185                     Gizmos.DrawLine(prev, Waypoints[
0].position);
186                 }
187                 
else
188                 {
189                     
for (int n = 0; n < Waypoints.Length; ++n)
190                     {
191                         Vector3 next = Waypoints[(n +
1)%Waypoints.Length].position;
192                         Gizmos.DrawLine(prev, next);
193                         prev = next;
194                     }
195                 }
196             }
197         }
198
199
200         
[Serializable]
201         
public class WaypointList
202         {
203             
public WaypointCircuit circuit;
204             
public Transform[] items = new Transform[0];
205         }
206
207         
public struct RoutePoint
208         {
209             
public Vector3 position;
210             
public Vector3 direction;
211
212
213             
public RoutePoint(Vector3 position, Vector3 direction)
214             {
215                 
this.position = position;
216                 
this.direction = direction;
217             }
218         }
219     }
220 }

221
222 namespace
UnityStandardAssets.Utility.Inspector
223 {
224 #
if UNITY_EDITOR
225     
[CustomPropertyDrawer(typeof (WaypointCircuit.WaypointList))]
226     
public class WaypointListDrawer : PropertyDrawer
227     {
228         
private float lineHeight = 18;
229         
private float spacing = 4;
230
231
232         
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
233         {
234             EditorGUI.BeginProperty(position, label, property);
235
236             
float x = position.x;
237             
float y = position.y;
238             
float inspectorWidth = position.width;
239
240             
// Draw label
241
242
243             
// Don't make child fields be indented
244             
var indent = EditorGUI.indentLevel;
245             EditorGUI.indentLevel =
0;
246
247             
var items = property.FindPropertyRelative("items");
248             
var titles = new string[] {"Transform", "", "", ""};
249             
var props = new string[] {"transform", "^", "v", "-"};
250             
var widths = new float[] {.7f, .1f, .1f, .1f};
251             
float lineHeight = 18;
252             
bool changedLength = false;
253             
if (items.arraySize > 0)
254             {
255                 
for (int i = -1; i < items.arraySize; ++i)
256                 {
257                     
var item = items.GetArrayElementAtIndex(i);
258
259                     
float rowX = x;
260                     
for (int n = 0; n < props.Length; ++n)
261                     {
262                         
float w = widths[n]*inspectorWidth;
263
264                         
// Calculate rects
265                         Rect rect =
new Rect(rowX, y, w, lineHeight);
266                         rowX += w;
267
268                         
if (i == -1)
269                         {
270                             EditorGUI.LabelField(rect, titles[n]);
271                         }
272                         
else
273                         {
274                             
if (n == 0)
275                             {
276                                 EditorGUI.ObjectField(rect, item.objectReferenceValue,
typeof (Transform), true);
277                             }
278                             
else
279                             {
280                                 
if (GUI.Button(rect, props[n]))
281                                 {
282                                     
switch (props[n])
283                                     {
284                                         
case "-":
285                                             items.DeleteArrayElementAtIndex(i);
286                                             items.DeleteArrayElementAtIndex(i);
287                                             changedLength =
true;
288                                             
break;
289                                         
case "v":
290                                             
if (i > 0)
291                                             {
292                                                 items.MoveArrayElement(i, i +
1);
293                                             }
294                                             
break;
295                                         
case "^":
296                                             
if (i < items.arraySize - 1)
297                                             {
298                                                 items.MoveArrayElement(i, i -
1);
299                                             }
300                                             
break;
301                                     }
302                                 }
303                             }
304                         }
305                     }
306
307                     y += lineHeight + spacing;
308                     
if (changedLength)
309                     {
310                         
break;
311                     }
312                 }
313             }
314             
else
315             {
316                 
// add button
317                 
var addButtonRect = new Rect((x + position.width) - widths[widths.Length - 1]*inspectorWidth, y,
318                                              widths[widths.Length -
1]*inspectorWidth, lineHeight);
319                 
if (GUI.Button(addButtonRect, "+"))
320                 {
321                     items.InsertArrayElementAtIndex(items.arraySize);
322                 }
323
324                 y += lineHeight + spacing;
325             }
326
327             
// add all button
328             
var addAllButtonRect = new Rect(x, y, inspectorWidth, lineHeight);
329             
if (GUI.Button(addAllButtonRect, "Assign using all child objects"))
330             {
331                 
var circuit = property.FindPropertyRelative("circuit").objectReferenceValue as WaypointCircuit;
332                 
var children = new Transform[circuit.transform.childCount];
333                 
int n = 0;
334                 
foreach (Transform child in circuit.transform)
335                 {
336                     children[n++] = child;
337                 }
338                 Array.Sort(children,
new TransformNameComparer());
339                 circuit.waypointList.items =
new Transform[children.Length];
340                 
for (n = 0; n < children.Length; ++n)
341                 {
342                     circuit.waypointList.items[n] = children[n];
343                 }
344             }
345             y += lineHeight + spacing;
346
347             
// rename all button
348             
var renameButtonRect = new Rect(x, y, inspectorWidth, lineHeight);
349             
if (GUI.Button(renameButtonRect, "Auto Rename numerically from this order"))
350             {
351                 
var circuit = property.FindPropertyRelative("circuit").objectReferenceValue as WaypointCircuit;
352                 
int n = 0;
353                 
foreach (Transform child in circuit.waypointList.items)
354                 {
355                     child.name =
"Waypoint " + (n++).ToString("000");
356                 }
357             }
358             y += lineHeight + spacing;
359
360             
// Set indent back to what it was
361             EditorGUI.indentLevel = indent;
362             EditorGUI.EndProperty();
363         }
364
365
366         
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
367         {
368             SerializedProperty items = property.FindPropertyRelative(
"items");
369             
float lineAndSpace = lineHeight + spacing;
370             
return 40 + (items.arraySize*lineAndSpace) + lineAndSpace;
371         }
372
373
374         
// comparer for check distances in ray cast hits
375         
public class TransformNameComparer : IComparer
376         {
377             
public int Compare(object x, object y)
378             {
379                 
return ((Transform) x).name.CompareTo(((Transform) y).name);
380             }
381         }
382     }
383 #endif
384 }


this being here will save GC allocs

Use this for initialization

position and direction

get nearest two points, ensuring points wrap-around start & end of circuit

found point numbers, now find interpolation value between the two middle points

smooth catmull-rom calculation between the two relevant points

get indices for the surrounding 2 points, because

four points are required by the catmull-rom function

2nd point may have been the 'last' point - a dupe of the first,

(to give a value of max track distance instead of zero)

but now it must be wrapped back to zero if that was the case.

simple linear lerp between the two points:

comments are no use here... it's the catmull-rom equation.

Un-magic this, lord vector!

transfer the position of each point and distances between points to arrays for

speed of lookup at runtime

Draw label

Don't make child fields be indented

Calculate rects

add button

add all button

rename all button

Set indent back to what it was

comparer for check distances in ray cast hits



Gõ tìm kiếm nhanh...