forked from walterellisfun/ConeCast
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathPhysics2DCones.cs
193 lines (176 loc) · 12.8 KB
/
Physics2DCones.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
using System;
using System.Collections.Generic;
using UnityEngine;
public static class Physics2DCones
{
/// <summary>
/// Similar to RayCast, but searches within a 2D cone (well, a pie-wedge I guess) instead of just along a straight line.
/// This method works by using a <see cref="Physics2D.CircleCast"/> and then comparing the returned hit points to the edge of the cone.
/// This method defines a cone by its length and the angle between its center and its edge.
/// </summary>
/// <param name="origin">origin of the cone</param>
/// <param name="direction">direction of the center of the cone</param>
/// <param name="coneAngle">the angle between the center of the cone and the edge</param>
/// <param name="maxDistance">the maximum distance to search. Must not be infinite.</param>
/// <param name="mask">Only search on the layers given by the mask</param>
/// <param name="minDepth">Only include objects with a Z coordinate (depth) greater than or equal to this value.</param>
/// <param name="maxDepth">Only include objects with a Z coordinate (depth) less than or equal to this value.</param>
/// <returns></returns>
public static RaycastHit2D ConeCast(Vector2 origin, Vector2 direction, float coneAngle, float maxDistance,
int mask = Physics2D.DefaultRaycastLayers, float minDepth = Mathf.NegativeInfinity, float maxDepth = Mathf.Infinity)
{
float maxRadius = Mathf.Abs(Mathf.Tan(coneAngle * Mathf.Deg2Rad) * maxDistance);
return ConeCast(origin, maxRadius, direction, maxDistance, mask, minDepth, maxDepth);
}
/// <summary>
/// Similar to RayCast, but searches within a 2D cone (well, a pie-wedge I guess) instead of just along a straight line.
/// This method works by using a <see cref="Physics2D.CircleCast"/> and then comparing the returned hit points to the edge of the cone.
/// This method defines a cone by its length and maximum radius.
/// </summary>
/// <param name="origin">origin of the cone</param>
/// <param name="maxRadius">the largest radius of the cone</param>
/// <param name="direction">direction of the center of the cone</param>
/// <param name="maxDistance">the maximum distance to search. Must not be infinite.</param>
/// <param name="mask">Only search on the layers given by the mask</param>
/// <param name="minDepth">Only include objects with a Z coordinate (depth) greater than or equal to this value.</param>
/// <param name="maxDepth">Only include objects with a Z coordinate (depth) less than or equal to this value.</param>
/// <returns></returns>
public static RaycastHit2D ConeCast(Vector2 origin, float maxRadius, Vector2 direction, float maxDistance,
int mask = Physics2D.DefaultRaycastLayers, float minDepth = Mathf.NegativeInfinity, float maxDepth = Mathf.Infinity)
{
// if maxDistance is infinite then we can't figure out a max radius size.
// I don't think that physics2D.CircleCast would be happy with an infinite radius.
if (float.IsInfinity(maxDistance)) throw new ArgumentException("Must be finite");
float coneAngle = Mathf.Atan(maxRadius / maxDistance) * Mathf.Rad2Deg;
// get everything inside our cone plus some and then pair the list down to the actual matches
RaycastHit2D[] results = Physics2D.CircleCastAll(origin, maxRadius, direction, maxDistance, mask, minDepth, maxDepth);
var numResults = ConsolidateConeMatches(results, results.Length, origin, direction, coneAngle);
if (numResults > 0)
{
return results[0];
}
else
{
return default; // nothing was found
}
}
/// <summary>
/// Similar to ConeCast, but returns all colliders within the cone, not just the first. Hits are sorted from the closest to the origin to the farthest.
/// This method works by using a <see cref="Physics2D.CircleCast"/> and then comparing the returned hit points to the edge of the cone.
/// This method defines a cone by its length and the angle between its center and its edge.
/// </summary>
/// <param name="origin">origin of the cone</param>
/// <param name="direction">direction of the center of the cone</param>
/// <param name="coneAngle">the angle between the center of the cone and the edge</param>
/// <param name="maxDistance">the maximum distance to search. Must not be infinite.</param>
/// <param name="mask">Only search on the layers given by the mask</param>
/// <param name="minDepth">Only include objects with a Z coordinate (depth) greater than or equal to this value.</param>
/// <param name="maxDepth">Only include objects with a Z coordinate (depth) less than or equal to this value.</param>
/// <returns></returns>
public static RaycastHit2D[] ConeCastAll(Vector2 origin, Vector2 direction, float coneAngle, float maxDistance,
int mask = Physics2D.DefaultRaycastLayers, float minDepth = Mathf.NegativeInfinity, float maxDepth = Mathf.Infinity)
{
float maxRadius = Mathf.Abs(Mathf.Tan(coneAngle * Mathf.Deg2Rad) * maxDistance);
return ConeCastAll(origin, maxRadius, direction, maxDistance, mask, minDepth, maxDepth);
}
/// <summary>
/// Similar to ConeCast, but returns all colliders within the cone, not just the first. Hits are sorted from the closest to the origin to the farthest.
/// This method works by using a <see cref="Physics2D.CircleCast"/> and then comparing the returned hit points to the edge of the cone.
/// This method defines a cone by its length and maximum radius.
/// </summary>
/// <param name="origin">origin of the cone</param>
/// <param name="maxRadius">the largest radius of the cone</param>
/// <param name="direction">direction of the center of the cone</param>
/// <param name="maxDistance">the maximum distance to search. Must not be infinite.</param>
/// <param name="mask">Only search on the layers given by the mask</param>
/// <param name="minDepth">Only include objects with a Z coordinate (depth) greater than or equal to this value.</param>
/// <param name="maxDepth">Only include objects with a Z coordinate (depth) less than or equal to this value.</param>
/// <returns></returns>
public static RaycastHit2D[] ConeCastAll(Vector2 origin, float maxRadius, Vector2 direction, float maxDistance,
int mask = Physics2D.DefaultRaycastLayers, float minDepth = Mathf.NegativeInfinity, float maxDepth = Mathf.Infinity)
{
// if maxDistance is infinite then we can't figure out a max radius size.
// I don't think that physics2D.CircleCast would be happy with an infinite radius.
if (float.IsInfinity(maxDistance)) throw new ArgumentException("Must be finite");
float coneAngle = Mathf.Atan(maxRadius / maxDistance) * Mathf.Rad2Deg;
// get everything inside our cone plus some and then pair the list down to the actual matches
RaycastHit2D[] results = Physics2D.CircleCastAll(origin, maxRadius, direction, maxDistance, mask, minDepth, maxDepth);
var numResults = ConsolidateConeMatches(results, results.Length, origin, direction, coneAngle);
// Resize the array to match our new results set
Array.Resize(ref results, numResults);
return results;
}
/// <summary>
/// Similar to ConeCastAll, but uses a provided array (<paramref name="results"/>) instead of allocating memory for an array.
/// This allows you to allocate an array once and use it repeatedly in tight loops to prevent the need for garbage collection.
/// This method defines a cone by its length and the angle between its center and its edge.
/// </summary>
/// <param name="origin">origin of the cone</param>
/// <param name="direction">direction of the center of the cone</param>
/// <param name="coneAngle">the angle between the center of the cone and the edge</param>
/// <param name="results">an array in which hits will be stored. Contents will be overwritten, but bounds will be respected. You must ensure it is big enough to get the hits you are looking for.</param>
/// <param name="maxDistance">the maximum distance to search. Must not be infinite.</param>
/// <param name="mask">Only search on the layers given by the mask</param>
/// <param name="minDepth">Only include objects with a Z coordinate (depth) greater than or equal to this value.</param>
/// <param name="maxDepth">Only include objects with a Z coordinate (depth) less than or equal to this value.</param>
/// <returns></returns>
public static int ConeCastNonAlloc(Vector2 origin, Vector2 direction, float coneAngle, RaycastHit2D[] results, float maxDistance,
int mask = Physics2D.DefaultRaycastLayers, float minDepth = Mathf.NegativeInfinity, float maxDepth = Mathf.Infinity)
{
float maxRadius = Mathf.Abs(Mathf.Tan(coneAngle * Mathf.Deg2Rad) * maxDistance);
return ConeCastNonAlloc(origin, maxRadius, direction, results, maxDistance, mask, minDepth, maxDepth);
}
/// <summary>
/// Similar to ConeCastAll, but uses a provided array (<paramref name="results"/>) instead of allocating memory for an array.
/// This allows you to allocate an array once and use it repeatedly in tight loops to prevent the need for garbage collection.
/// This method defines a cone by its length and maximum radius.
/// </summary>
/// <param name="origin">origin of the cone</param>
/// <param name="maxRadius">the largest radius of the cone</param>
/// <param name="direction">direction of the center of the cone</param>
/// <param name="results">an array in which hits will be stored. Contents will be overwritten, but bounds will be respected. You must ensure it is big enough to get the hits you are looking for.</param>
/// <param name="maxDistance">the maximum distance to search. Must not be infinite.</param>
/// <param name="mask">Only search on the layers given by the mask</param>
/// <param name="minDepth">Only include objects with a Z coordinate (depth) greater than or equal to this value.</param>
/// <param name="maxDepth">Only include objects with a Z coordinate (depth) less than or equal to this value.</param>
/// <returns></returns>
public static int ConeCastNonAlloc(Vector2 origin, float maxRadius, Vector2 direction, RaycastHit2D[] results, float maxDistance,
int mask = Physics2D.DefaultRaycastLayers, float minDepth = Mathf.NegativeInfinity, float maxDepth = Mathf.Infinity)
{
// if maxDistance is infinite then we can't figure out a max radius size.
// I don't think that physics2D.CircleCast would be happy with an infinite radius.
if (float.IsInfinity(maxDistance)) throw new ArgumentException("Must be finite");
float coneAngle = Mathf.Atan(maxRadius / maxDistance) * Mathf.Rad2Deg;
// get everything inside our cone plus some and then pair the list down to the actual matches
int numResults = Physics2D.CircleCastNonAlloc(origin, maxRadius, direction, results, maxDistance, mask, minDepth, maxDepth);
numResults = ConsolidateConeMatches(results, numResults, origin, direction, coneAngle);
return numResults;
}
/// <summary>
/// This method does the hard work. It compares the angle to each hit point to the cone's angle and copies the ones that match to the beginning of the array.
/// It preserves the nearest to farthest order or the items.
/// Doing all this within the array allows this method to work on all three cast methodologies.
/// </summary>
/// <param name="results">Array containing all potential matches</param>
/// <param name="numResults">The number of actual potential matches in <paramref name="results"/>, in case the array is a partially filled buffer.</param>
/// <param name="origin">The tip of the cone</param>
/// <param name="direction">the direction of the cone</param>
/// <param name="coneAngle">the angle between the center of the cone and the edge of the cone.</param>
/// <returns>The number of hits actually within the cone</returns>
private static int ConsolidateConeMatches(RaycastHit2D[] results, int numResults, Vector2 origin, Vector2 direction, float coneAngle)
{
int j = 0; // variable to keep track of the number of accepted results
for (int i = 0; i < numResults; i++)
{
Vector2 hitPoint = results[i].point;
Vector2 directionToHit = hitPoint - origin;
float angleToHit = Vector2.Angle(direction, directionToHit);
if (angleToHit < coneAngle)
{
results[j] = results[i];
j++;
}
}
return j;
}
}