-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathColliderHelper.cs
399 lines (364 loc) · 15.8 KB
/
ColliderHelper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
using System.Collections.Generic;
using UnityEngine;
public class ColliderHelper : MonoBehaviour
{
public static Vector2[] GetBoxColliderVertices2D(BoxCollider2D collider)
{
Mesh colliderMesh = collider.CreateMesh(true, true);
Vector2[] vertices = new Vector2[colliderMesh.vertexCount];
for (int i = 0; i < colliderMesh.vertexCount; i++)
{
if (i % 2 == 0)
{
vertices[i / 2] = new Vector2(colliderMesh.vertices[i].x, colliderMesh.vertices[i].y);
}
else
{
vertices[(colliderMesh.vertexCount + i) / 2] = new Vector2(colliderMesh.vertices[i].x, colliderMesh.vertices[i].y);
}
}
return vertices;
}
public static Vector2[] GetCapsuleColliderVertices2D(CapsuleCollider2D collider)
{
if (
collider.transform.rotation.eulerAngles.x != 0 ||
collider.transform.rotation.eulerAngles.y != 0 ||
collider.transform.rotation.eulerAngles.z != 0
)
{
Debug.LogWarning("Capsule colliders cannot be rotated when getting vertices.", collider);
return null;
}
Mesh colliderMesh = collider.CreateMesh(false, false);
Vector2[] vertices = new Vector2[colliderMesh.vertexCount];
for (int i = 0; i < colliderMesh.vertexCount; i++)
{
// apply transformations
Vector2 vertex = colliderMesh.vertices[i];
if (i % 2 == 0)
{
vertices[i / 2] = vertex;
}
else
{
vertices[colliderMesh.vertexCount - (i / 2) - 1] = vertex;
}
}
return vertices;
}
public static Vector2[] GetCircleColliderVertices2D(CircleCollider2D collider)
{
Mesh colliderMesh = collider.CreateMesh(true, true);
Vector2[] vertices = new Vector2[colliderMesh.vertexCount];
for (int i = 0; i < colliderMesh.vertexCount; i++)
{
// apply transformations
Vector2 vertex = colliderMesh.vertices[i] - collider.transform.position;
if (collider.transform.lossyScale.x < 0 && collider.transform.lossyScale.y < 0)
{
vertex *= new Vector2(
collider.transform.lossyScale.x,
collider.transform.lossyScale.y
);
}
else if (Mathf.Abs(collider.transform.lossyScale.x) < Mathf.Abs(collider.transform.lossyScale.y))
{
vertex *= new Vector2(
collider.transform.lossyScale.x / collider.transform.lossyScale.y,
1
);
}
else
{
vertex *= new Vector2(
1,
collider.transform.lossyScale.y / collider.transform.lossyScale.x
);
}
vertex = (collider.transform.rotation * vertex) + collider.transform.position;
if (i % 2 == 0)
{
vertices[i / 2] = vertex;
}
else
{
vertices[colliderMesh.vertexCount - (i / 2) - 1] = vertex;
}
}
return vertices;
}
public static Vector2[] GetPolygonColliderVertices2D(PolygonCollider2D collider)
{
Vector2[] vertices = new Vector2[collider.points.Length];
for (int i = 0; i < collider.points.Length; i++)
{
// apply transformations
vertices[i] = (collider.transform.rotation * (collider.points[i] * collider.transform.lossyScale)) + collider.transform.position;
}
return vertices;
}
public static Vector2[] GetColliderVertices2D(Collider2D collider)
{
if (collider as BoxCollider2D)
{
return GetBoxColliderVertices2D(collider as BoxCollider2D);
}
else if (collider as CapsuleCollider2D)
{
return GetCapsuleColliderVertices2D(collider as CapsuleCollider2D);
}
else if (collider as CircleCollider2D)
{
return GetCircleColliderVertices2D(collider as CircleCollider2D);
}
else if (collider as PolygonCollider2D)
{
return GetPolygonColliderVertices2D(collider as PolygonCollider2D);
}
Debug.LogWarning("Unsupported collider type.", collider);
return null;
}
public struct CastResult2D
{
public bool success;
public Vector2 ray;
public Vector2 hitPoint;
public Collider2D hitCollider;
public CastResult2D(bool _success = false, Vector2 _ray = new Vector2(), Vector2 _hitPoint = new Vector2(), Collider2D _hitCollider = null)
{
success = _success;
ray = _ray;
hitPoint = _hitPoint;
hitCollider = _hitCollider;
}
}
public static CastResult2D LinesIntersect2D(Vector2 line1point1, Vector2 line1point2, Vector2 line2point1, Vector2 line2point2)
{
// Using the classic y=mx+b, we can set the equation for each line equal (m1x+b1=m2x+b2).
// Then with a bit of algebra, the point which the lines cross can be found.
CastResult2D result = new CastResult2D();
float line1xDiff = line1point1.x - line1point2.x;
float line2xDiff = line2point1.x - line2point2.x;
float line1slope = (line1point1.y - line1point2.y) / line1xDiff;
float line2slope = (line2point1.y - line2point2.y) / line2xDiff;
float resultX = 0;
float resultY = 0;
// Vertical lines need to be dealt with separately because their slopes are undefined.
if (Mathf.Approximately(line1xDiff, 0))
{
if (!Mathf.Approximately(line2xDiff, 0))
{
resultX = line1point1.x;
resultY = (line2slope * line1point1.x) + line2point1.y - (line2slope * line2point1.x);
}
else
{
return result;
}
}
else if (Mathf.Approximately(line2xDiff, 0))
{
resultX = line2point1.x;
resultY = (line1slope * line2point1.x) + line1point1.y - (line1slope * line1point1.x);
}
else
{
// If the lines are parallel, their slopes will be equal and the lines will not cross.
float slopeDiff = line1slope - line2slope;
if (!Mathf.Approximately(slopeDiff, 0))
{
float b1 = line1point1.y - (line1slope * line1point1.x);
resultX = (line2point1.y - (line2slope * line2point1.x) - b1) / slopeDiff;
resultY = (line1slope * resultX) + b1;
}
else
{
return result;
}
}
// After finding the intersection point, it needs to be tested to see if it lies between both sets of endpoints.
float line1minX = Mathf.Min(line1point1.x, line1point2.x);
float line1maxX = Mathf.Max(line1point1.x, line1point2.x);
float line2minX = Mathf.Min(line2point1.x, line2point2.x);
float line2maxX = Mathf.Max(line2point1.x, line2point2.x);
float line1minY = Mathf.Min(line1point1.y, line1point2.y);
float line1maxY = Mathf.Max(line1point1.y, line1point2.y);
float line2minY = Mathf.Min(line2point1.y, line2point2.y);
float line2maxY = Mathf.Max(line2point1.y, line2point2.y);
if (
(line1minX < resultX || Mathf.Approximately(line1minX, resultX)) &&
(line1maxX > resultX || Mathf.Approximately(line1maxX, resultX)) &&
(line2minX < resultX || Mathf.Approximately(line2minX, resultX)) &&
(line2maxX > resultX || Mathf.Approximately(line2maxX, resultX)) &&
(line1minY < resultY || Mathf.Approximately(line1minY, resultY)) &&
(line1maxY > resultY || Mathf.Approximately(line1maxY, resultY)) &&
(line2minY < resultY || Mathf.Approximately(line2minY, resultY)) &&
(line2maxY > resultY || Mathf.Approximately(line2maxY, resultY))
)
{
result.success = true;
result.hitPoint = new Vector2(resultX, resultY);
}
return result;
}
public static bool PolygonOverlapPoint2D(Vector2[] polygon, Vector2 point)
{
if (polygon.Length < 3)
{
return false;
}
// A ray, starting from the point, pointing straight up (positive y), will intersect the edges of the polygon an even number of times if the point is inside the polygon.
int crossings = 0;
float direction = Mathf.Sign(polygon[1].x - polygon[0].x);
for (int i = 0; i < polygon.Length; i++)
{
Vector2 pPoint1 = polygon[i];
Vector2 pPoint2 = polygon[(i + 1) % polygon.Length];
CastResult2D lineCrossing = LinesIntersect2D(point, new Vector2(point.x, Mathf.Infinity), pPoint1, pPoint2);
if (lineCrossing.success)
{
crossings++;
}
}
return crossings % 2 == 1;
}
public static CastResult2D PolygonsAreTouching2D(Vector2[] polygon1, Vector2[] polygon2)
{
// If any edges from polygon1 cross any edges from polygon2, the two polygons are touching.
// The only other way the polygons could touch is if one polygon is entirely inside the other.
// In this case, only one vertex from each polygon needs to be tested for overlap.
if (polygon2.Length > 0 && PolygonOverlapPoint2D(polygon1, polygon2[0]))
{
return new CastResult2D(true, default, polygon2[0]);
}
if (polygon1.Length > 0 && PolygonOverlapPoint2D(polygon2, polygon1[0]))
{
return new CastResult2D(true, default, polygon1[0]);
}
for (int p1PointIndex = 0; p1PointIndex < polygon1.Length; p1PointIndex++)
{
Vector2 p1Point1 = polygon1[p1PointIndex];
Vector2 p1Point2 = polygon1[(p1PointIndex + 1) % polygon1.Length];
float p1PointsSlope = (p1Point1.y - p1Point2.y) / (p1Point1.x - p1Point2.x);
for (int p2PointIndex = 0; p2PointIndex < polygon2.Length; p2PointIndex++)
{
Vector2 p2Point1 = polygon2[p2PointIndex];
Vector2 p2Point2 = polygon2[(p2PointIndex + 1) % polygon2.Length];
CastResult2D lineCrossing = LinesIntersect2D(p1Point1, p1Point2, p2Point1, p2Point2);
if (lineCrossing.success)
{
return lineCrossing;
}
}
}
return new CastResult2D();
}
public static CastResult2D PolygonCast2D(Vector2[] polygon1, Vector2[] polygon2, Vector2 ray)
{
CastResult2D polygonsTouching = PolygonsAreTouching2D(polygon1, polygon2);
if (polygonsTouching.success)
{
return new CastResult2D(true, default, polygonsTouching.hitPoint);
}
// The only points which could cause the cast to hit something are the vertices of both polygons.
// Therefore, only the vertices need to be tested to see if they would be hit in the cast.
CastResult2D result = new CastResult2D(false, ray);
for (int p1PointIndex = 0; p1PointIndex < polygon1.Length; p1PointIndex++)
{
Vector2 p1Point1 = polygon1[p1PointIndex];
Vector2 p1Point2 = polygon1[(p1PointIndex + 1) % polygon1.Length];
for (int p2PointIndex = 0; p2PointIndex < polygon2.Length; p2PointIndex++)
{
Vector2 p2Point1 = polygon2[p2PointIndex];
Vector2 p2Point2 = polygon2[(p2PointIndex + 1) % polygon2.Length];
// cast ray from p1Point1 to the line between p2Point1 and p2Point2
CastResult2D rayHit = LinesIntersect2D(p1Point1, p1Point1 + result.ray, p2Point1, p2Point2);
Vector2 newRay = rayHit.hitPoint - p1Point1;
if (rayHit.success)
{
result.success = true;
result.ray = newRay;
result.hitPoint = rayHit.hitPoint;
}
// cast ray from p2Point1 to the line between p1Point1 and p1Point2
rayHit = LinesIntersect2D(p1Point1, p1Point2, p2Point1, p2Point1 - result.ray);
newRay = p2Point1 - rayHit.hitPoint;
if (rayHit.success)
{
result.success = true;
result.ray = newRay;
result.hitPoint = p2Point1;
}
}
}
return result;
}
public static CastResult2D ColliderCast2D(Collider2D collider1, Collider2D collider2, Vector2 ray)
{
CastResult2D result = PolygonCast2D(GetColliderVertices2D(collider1), GetColliderVertices2D(collider2), ray);
result.hitCollider = collider2;
return result;
}
public static CastResult2D[] PolygonCast2D(Vector2[] polygon1, Vector2[][] polygon2s, Vector2 ray)
{
CastResult2D[] results = new CastResult2D[polygon2s.Length];
for (int i = 0; i < polygon2s.Length; i++)
{
results[i] = PolygonCast2D(polygon1, polygon2s[i], ray);
}
return results;
}
public static CastResult2D[] ColliderCast2D(Collider2D collider1, Collider2D[] collider2s, Vector2 ray, bool filter = false, bool sort = false, bool includeSelf = true)
{
Vector2[] vertices = GetColliderVertices2D(collider1);
List<CastResult2D> results = new List<CastResult2D>();
for (int i = 0; i < collider2s.Length; i++)
{
if (includeSelf || collider2s[i] != collider1)
{
CastResult2D castResult = PolygonCast2D(vertices, GetColliderVertices2D(collider2s[i]), ray);
if (castResult.success || !filter)
{
castResult.hitCollider = collider2s[i];
results.Add(castResult);
}
}
}
if (sort)
{
results.Sort(delegate (CastResult2D result1, CastResult2D result2) { return result1.ray.magnitude > result2.ray.magnitude ? 1 : -1; });
}
return results.ToArray();
}
public static Collider2D[] GetAllColliders()
{
List<Collider2D> colliders = new List<Collider2D>();
GameObject[] gameObjects = UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects();
foreach (GameObject gameObject in gameObjects)
{
colliders.AddRange(gameObject.GetComponentsInChildren<Collider2D>());
}
return colliders.ToArray();
}
public static CastResult2D[] ColliderCastAll2D(Collider2D collider1, Vector2 ray, bool filter = false, bool sort = false, bool includeSelf = true)
{
return ColliderCast2D(collider1, GetAllColliders(), ray, filter, sort, includeSelf);
}
public static CastResult2D[] ColliderIsTouchingAll2D(Collider2D collider, bool filter = false)
{
Vector2[] vertices = GetColliderVertices2D(collider);
Collider2D[] allColliders = GetAllColliders();
List<CastResult2D> results = new List<CastResult2D>();
for (int i = 0; i < allColliders.Length; i++)
{
CastResult2D result = PolygonsAreTouching2D(vertices, GetColliderVertices2D(allColliders[i]));
if (result.success || !filter)
{
result.hitCollider = allColliders[i];
results.Add(result);
}
}
return results.ToArray();
}
}