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 }


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