UFL13——数学计算工具模块

前置知识点

  1. Unity中向量、四元数 相关知识点(Unity四部曲之Unity基础中)
  2. 射线检测 相关知识点(Unity四部曲之Unity基础中)
  3. 范围检测 相关知识点 (Unity四部曲之Unity基础中)

数学计算工具模块 主要作用

在游戏开发中,经常会进行一些通用的数学计算,为了减少代码冗余
我们往往会把常用的数学计算逻辑,封装到一个工具类中提供给外部使用

因此数学计算工具模块,主要就是将通用的数学计算逻辑封装为方法,供外部使用

数学计算工具模块 基本原理

我们将新建一个数学计算工具类,主要提供以下方法:

  1. 角度和弧度的转换
  2. 得到两个对象在xz平面上的距离
  3. 判断对象是否在屏幕范围外
  4. 判断对象位置是否在xz平面扇形范围内
  5. 射线检测
  6. 范围检测

等等

我们只实现以上方法,更多内容,在实际开发时根据需求加入到工具类中即可

角度和弧度相互转换 的方法

虽然Unity中的 Mathf​ 类中已经有 Mathf.Deg2Rad​ 、 Mathf.Rad2Deg​ 相关的系数
但是为了避免使用错误,我们完全可以将其封装为方法,外部只需传入角度或弧度 便可以得到结果
无需在外部进行乘法运算

让这些常规操作使用起来更加的方便

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/// <summary>
/// 角度转弧度的方法
/// </summary>
/// <param name="deg">要转换的角度</param>
/// <returns>转换出来的弧度</returns>
public static float DegToRad(float deg)
{
return deg * Mathf.Deg2Rad;
}

/// <summary>
/// 弧度转角度的方法
/// </summary>
/// <param name="rad">要转换的弧度</param>
/// <returns>转换出来的弧度</returns>
public static float RadToDeg(float rad)
{
return rad * Mathf.Rad2Deg;
}

使用示例

1
2
3
4
5
6
void Start()
{
float rad = MathUtil.DegToRad(60);
print("弧度:" + rad);
print("角度:" + MathUtil.RadToDeg(rad));
}

输出:image

距离判断相关 的方法

在进行游戏开发时,我们经常要去判断两点之间的距离
但是对于游戏中的对象,我们经常只需要判断它在某一个2D平面上的距离

比如:

  • 部分 2D 游戏中,我们往往只关心 xy 平面上的距离,z的变化不应该影响距离的计算
  • 部分 3D 游戏中,我们往往只关心 xz 平面上的距离,y的变化不应该影响距离的计算

为了方便我们得到对象之间在某个平面的距离,我们可以为其封装一些方法,专门用于计算 XZ、XY 平面上两点的距离

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
/// <summary>
/// 获取 XZ平面上 两点的距离
/// </summary>
/// <param name="srcPos">点1</param>
/// <param name="targetPos">点2</param>
/// <returns>XZ平面上的距离</returns>
public static float GetObjDistanceXZ(Vector3 srcPos, Vector3 targetPos)
{
srcPos.y = 0;
targetPos.y = 0;
return Vector3.Distance(srcPos, targetPos);
}

/// <summary>
/// 判断两点在XZ平面的距离 是否小于目标距离
/// </summary>
/// <param name="srcPos">点1</param>
/// <param name="targetPos">点2</param>
/// <param name="dis">判断距离</param>
/// <returns>是否小于判断距离</returns>
public static bool CheckObjDistanceXZ(Vector3 srcPos, Vector3 targetPos, float dis)
{
return GetObjDistanceXZ(srcPos, targetPos) <= dis;
}

/// <summary>
/// 获取 XY平面上 两点的距离
/// </summary>
/// <param name="srcPos">点1</param>
/// <param name="targetPos">点2</param>
/// <returns>XY平面上的距离</returns>
public static float GetObjDistanceXY(Vector3 srcPos, Vector3 targetPos)
{
srcPos.z = 0;
targetPos.z = 0;
return Vector3.Distance(srcPos, targetPos);
}

/// <summary>
/// 判断两点在XY平面的距离 是否小于目标距离
/// </summary>
/// <param name="srcPos">点1</param>
/// <param name="targetPos">点2</param>
/// <param name="dis">判断距离</param>
/// <returns>是否小于判断距离</returns>
public static bool CheckObjDistanceXY(Vector3 srcPos, Vector3 targetPos, float dis)
{
return GetObjDistanceXY(srcPos, targetPos) <= dis;
}

使用示例:

1
2
3
4
5
6
7
8
9
10
void Start()
{
Vector3 src = new Vector3(1, 5, 0);
Vector3 target = new Vector3(0, 19, 1);
print(MathUtil.GetObjDistanceXZ(src, target));
if (MathUtil.CheckObjDistanceXZ(src, target, 5))
{
print("两点间距离小于5");
}
}

输出:image

屏幕外判断 的方法

在进行游戏开发时,我们可能会有判断对象是否在屏幕外的需求
因此我们可以封装一个判断某一个位置是否在屏幕显示范围外的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/// <summary>
/// 判断世界坐标系下的某个点是否在屏幕可见范围外
/// </summary>
/// <param name="pos">世界坐标系下的一个点的位置</param>
/// <returns>如果在可见范围外返回true,否则返回false</returns>
public static bool IsWorldPosOutScreen(Vector3 pos)
{
//将世界坐标转为屏幕坐标
Vector3 screenPos = Camera.main.WorldToScreenPoint(pos);
//判断是否在屏幕内
if (screenPos.x >= 0 && screenPos.x <= Screen.width &&
screenPos.y >= 0 && screenPos.y <= Screen.height)
return false;
return true;
}

扇形范围判断 的方法

在进行游戏开发时,我们经常需要判断目标是否在自己前方的扇形范围内,用于触发下一步的行为,
比如 触发攻击、触发伤害等等,因此我们可以把 扇形范围判断 封装为一个方法,方便外部使用

需求:我们只需要实现判断XZ平面的扇形范围内即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/// <summary>
/// 判断某一个位置在XZ平面上是否在指定的扇形范围内,传入的坐标向量必须是基于同一个坐标系下的!!!
/// </summary>
/// <param name="pos"><扇形中心点位置/param>
/// <param name="forward">自己的面朝向</param>
/// <param name="targetPos">目标对象</param>
/// <param name="radius">半径</param>
/// <param name="angle">扇形的角度</param>
/// <returns>目标是否在扇形范围内</returns>
public static bool isInSectorRangeXZ(Vector3 pos, Vector3 forward, Vector3 targetPos, float radius, float angle)
{
pos.y = 0;
forward.y = 0;
targetPos.y = 0;
//距离 + 角度
return Vector3.Distance(forward, targetPos) <= radius && Vector3.Angle(forward, targetPos - pos) <= angle / 2f;
}

使用示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void Start()
{
Vector3 pos = Vector3.zero;
Vector3 forward = Vector3.forward;
Vector3 targetPos = new Vector3(1f, 0f, 1f);
if (MathUtil.isInSectorRangeXZ(pos, forward, targetPos, 2, 90))
{
print("目标在扇形范围内");
}
else
{
print("目标不在扇形范围内");
}
if (MathUtil.isInSectorRangeXZ(pos, forward, targetPos, 2, 60))
{
print("目标在扇形范围内");
}
else
{
print("目标不在扇形范围内");
}
}

输出:image

射线检测 相关方法

射线检测是在游戏开发中经常会使用的功能,我们完全可以基于Unity的射线检测进行二次封装
让其使用起来更加的方便,可以直接通过委托回调的形式对获取到的内容进行操作

需求:通过委托回调的形式获取到射线检测得到的对象
可以有几种重载,分别获取到RaycastHit​、GameObject​、指定脚本

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
/// <summary>
/// 指定距离 指定层级 的射线检测,获取一个对象
/// </summary>
/// <param name="ray">射线</param>
/// <param name="callBack">回调函数(会把碰到的RaycastHit消息传递出去)</param>
/// <param name="maxDistance">最大距离</param>
/// <param name="layerMask">层级筛选</param>
public void Raycast(Ray ray, UnityAction<RaycastHit> callBack, float maxDistance, int layerMask)
{
if (Physics.Raycast(ray, out RaycastHit hitInfo, maxDistance, layerMask))
{
callBack(hitInfo);
}
}

/// <summary>
/// 指定距离 指定层级 的射线检测,获取一个对象
/// </summary>
/// <param name="ray">射线</param>
/// <param name="callBack">回调函数(会把碰到的GameObject消息传递出去)</param>
/// <param name="maxDistance">最大距离</param>
/// <param name="layerMask">层级筛选</param>
public void Raycast(Ray ray, UnityAction<GameObject> callBack, float maxDistance, int layerMask)
{
if (Physics.Raycast(ray, out RaycastHit hitInfo, maxDistance, layerMask))
{
callBack(hitInfo.collider.gameObject);
}
}

/// <summary>
/// 指定距离 指定层级 的射线检测,获取一个对象
/// </summary>
/// <param name="ray">射线</param>
/// <param name="callBack">回调函数(会把碰到的对象上指定的脚本传递出去)</param>
/// <param name="maxDistance">最大距离</param>
/// <param name="layerMask">层级筛选</param>
public void Raycast<T>(Ray ray, UnityAction<T> callBack, float maxDistance, int layerMask)
{
if (Physics.Raycast(ray, out RaycastHit hitInfo, maxDistance, layerMask))
{
callBack(hitInfo.collider.gameObject.GetComponent<T>());
}
}

/// <summary>
/// 指定距离 指定层级 的射线检测,可获取多个对象,会执行多次委托以传递
/// </summary>
/// <param name="ray">射线</param>
/// <param name="callBack">回调函数(会把碰到的RaycastHit消息传递出去)</param>
/// <param name="maxDistance">最大距离</param>
/// <param name="layerMask">层级筛选</param>
public void RayCastAll(Ray ray, UnityAction<RaycastHit> callBack, float maxDistance, int layerMask)
{
RaycastHit[] hitInfos = Physics.RaycastAll(ray, maxDistance, layerMask);
for (int i = 0; i < hitInfos.Length; i++)
{
callBack.Invoke(hitInfos[i]);
}
}

/// <summary>
/// 指定距离 指定层级 的射线检测,可获取多个对象,会执行多次委托以传递
/// </summary>
/// <param name="ray">射线</param>
/// <param name="callBack">回调函数(会把碰到的GameObject消息传递出去)</param>
/// <param name="maxDistance">最大距离</param>
/// <param name="layerMask">层级筛选</param>
public void RayCastAll(Ray ray, UnityAction<GameObject> callBack, float maxDistance, int layerMask)
{
RaycastHit[] hitInfos = Physics.RaycastAll(ray, maxDistance, layerMask);
for (int i = 0; i < hitInfos.Length; i++)
{
callBack.Invoke(hitInfos[i].collider.gameObject);
}
}

/// <summary>
/// 指定距离 指定层级 的射线检测,可获取多个对象,会执行多次委托以传递
/// </summary>
/// <param name="ray">射线</param>
/// <param name="callBack">回调函数(会把碰到的对象上指定的脚本传递出去)</param>
/// <param name="maxDistance">最大距离</param>
/// <param name="layerMask">层级筛选</param>
public void RayCastAll<T>(Ray ray, UnityAction<T> callBack, float maxDistance, int layerMask)
{
RaycastHit[] hitInfos = Physics.RaycastAll(ray, maxDistance, layerMask);
for (int i = 0; i < hitInfos.Length; i++)
{
callBack.Invoke(hitInfos[i].collider.gameObject.GetComponent<T>());
}
}

使用示例:

假设要通过射线检测是否点击到了场景上的怪物层对象

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Update()
{
if (Input.GetMouseButtonDown(0))
{
MathUtil.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition),
GetRayCastObj,
1000,
1 << LayerMask.NameToLayer("Monster"));
}
}

private void GetRayCastObj(GameObject obj)
{
print(obj.name);
}

点击到指定对象后输出:image

假设要检测多个对象

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void Update()
{
if (Input.GetMouseButtonDown(0))
{
MathUtil.RaycastAll(Camera.main.ScreenPointToRay(Input.mousePosition),
GetRayCastObj,
1000,
1 << LayerMask.NameToLayer("Monster"));
}
}

private void GetRayCastObj(GameObject obj)
{
print(obj.name);
}

点击到两个指定对象后输出:image

范围检测 相关方法

范围检测是在游戏开发中经常会使用的功能,我们完全可以基于Unity的范围检测进行二次封装
让其使用起来更加的方便,可以直接通过委托回调的形式对获取到的内容进行操作

需求:
通过委托回调的形式获取到范围检测得到的对象
主要封装 盒装检测和球体检测 即可(胶囊检测使用很少)
可以指定获取到Collider​、GameObject​、指定脚本

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
/// <summary>
/// 进行盒状检测
/// </summary>
/// <typeparam name="T">要获取的对象类型</typeparam>
/// <param name="center">盒子中心点</param>
/// <param name="rotation">盒子的角度</param>
/// <param name="halfExtents">长宽高的一半</param>
/// <param name="layerMask">层级筛选</param>
/// <param name="callBack">回调函数</param>
public static void OverlapBox<T>(Vector3 center, Quaternion rotation, Vector3 halfExtents, int layerMask, UnityAction<T> callBack) where T : cl
{
Type type = typeof(T);
Collider[] colliders = Physics.OverlapBox(center, halfExtents, rotation, layerMask, QueryTriggerInteraction.Collide);
for (int i = 0; i < colliders.Length; i++)
{
if (type == typeof(Collider))
{
callBack.Invoke(colliders[i] as T);
}
else if (type == typeof(GameObject))
{
callBack.Invoke(colliders[i].gameObject as T);
}
else
{
callBack.Invoke(colliders[i].gameObject.GetComponent<T>());
}
}
}

/// <summary>
/// 进行球状检测
/// </summary>
/// <typeparam name="T">要获取的对象类型</typeparam>
/// <param name="center">球体中心点</param>
/// <param name="radius">球体半径</param>
/// <param name="layerMask">层级筛选</param>
/// <param name="callBack">回调函数</param>
public static void OverlapSphere<T>(Vector3 center, float radius, int layerMask, UnityAction<T> callBack) where T : class
{
Type type = typeof(T);
Collider[] colliders = Physics.OverlapSphere(center, radius, layerMask, QueryTriggerInteraction.Collide);
for (int i = 0; i < colliders.Length; i++)
{
if (type == typeof(Collider))
{
callBack.Invoke(colliders[i] as T);
}
else if (type == typeof(GameObject))
{
callBack.Invoke(colliders[i].gameObject as T);
}
else
{
callBack.Invoke(colliders[i].gameObject.GetComponent<T>());
}
}
}

/// <summary>
/// 进行胶囊体检测
/// </summary>
/// <typeparam name="T">要获取的对象类型</typeparam>
/// <param name="point0">胶囊体端点一</param>
/// <param name="point1">胶囊体端点二</param>
/// <param name="radius">胶囊体半径</param>
/// <param name="layerMask">层级筛选</param>
/// <param name="callBack">回调函数</param>
public static void OverlapCapsule<T>(Vector3 point0, Vector3 point1, float radius, int layerMask, UnityAction<T> callBack) where T : class
{
Type type = typeof(T);
Collider[] colliders = Physics.OverlapCapsule(point0, point1, radius, layerMask, QueryTriggerInteraction.Collide);
for (int i = 0; i < colliders.Length; i++)
{
if (type == typeof(Collider))
{
callBack.Invoke(colliders[i] as T);
}
else if (type == typeof(GameObject))
{
callBack.Invoke(colliders[i].gameObject as T);
}
else
{
callBack.Invoke(colliders[i].gameObject.GetComponent<T>());
}
}
}

使用示例:

假设要检测原点位置附近的Monster层级对象:

image

1
2
3
4
5
6
7
8
9
10
11
12
13
void Update()
{
if (Input.GetMouseButtonDown(1))
{
MathUtil.OverlapBox<GameObject>(Vector3.zero,
Quaternion.identity,
0.5f * Vector3.one,
1 << LayerMask.NameToLayer("Monster"),
(obj) => {
print(obj.name);
});
}
}

输出:image

具体代码

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
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class MathUtil : MonoBehaviour
{
#region 位置判断相关
/// <summary>
/// 判断世界坐标系下的某个点是否在屏幕可见范围外
/// </summary>
/// <param name="pos">世界坐标系下的一个点的位置</param>
/// <returns>如果在可见范围外返回true,否则返回false</returns>
public static bool IsWorldPosOutScreen(Vector3 pos)
{
//将世界坐标转为屏幕坐标
Vector3 screenPos = Camera.main.WorldToScreenPoint(pos);
//判断是否在屏幕内
if (screenPos.x >= 0 && screenPos.x <= Screen.width &&
screenPos.y >= 0 && screenPos.y <= Screen.height)
return false;
return true;
}

/// <summary>
/// 判断某一个位置在XZ平面上是否在指定的扇形范围内,传入的坐标向量必须是基于同一个坐标系下的!!!
/// </summary>
/// <param name="pos"><扇形中心点位置/param>
/// <param name="forward">自己的面朝向</param>
/// <param name="targetPos">目标对象</param>
/// <param name="radius">半径</param>
/// <param name="angle">扇形的角度</param>
/// <returns>目标是否在扇形范围内</returns>
public static bool isInSectorRangeXZ(Vector3 pos, Vector3 forward, Vector3 targetPos, float radius, float angle)
{
pos.y = 0;
forward.y = 0;
targetPos.y = 0;
//距离 + 角度
return Vector3.Distance(forward, targetPos) <= radius && Vector3.Angle(forward, targetPos - pos) <= angle / 2f;
}
#endregion

#region 距离计算相关
/// <summary>
/// 获取 XZ平面上 两点的距离
/// </summary>
/// <param name="srcPos">点1</param>
/// <param name="targetPos">点2</param>
/// <returns>XZ平面上的距离</returns>
public static float GetObjDistanceXZ(Vector3 srcPos, Vector3 targetPos)
{
srcPos.y = 0;
targetPos.y = 0;
return Vector3.Distance(srcPos, targetPos);
}

/// <summary>
/// 判断两点在XZ平面的距离 是否小于目标距离
/// </summary>
/// <param name="srcPos">点1</param>
/// <param name="targetPos">点2</param>
/// <param name="dis">判断距离</param>
/// <returns>是否小于判断距离</returns>
public static bool CheckObjDistanceXZ(Vector3 srcPos, Vector3 targetPos, float dis)
{
return GetObjDistanceXZ(srcPos, targetPos) <= dis;
}

/// <summary>
/// 获取 XY平面上 两点的距离
/// </summary>
/// <param name="srcPos">点1</param>
/// <param name="targetPos">点2</param>
/// <returns>XY平面上的距离</returns>
public static float GetObjDistanceXY(Vector3 srcPos, Vector3 targetPos)
{
srcPos.z = 0;
targetPos.z = 0;
return Vector3.Distance(srcPos, targetPos);
}

/// <summary>
/// 判断两点在XY平面的距离 是否小于目标距离
/// </summary>
/// <param name="srcPos">点1</param>
/// <param name="targetPos">点2</param>
/// <param name="dis">判断距离</param>
/// <returns>是否小于判断距离</returns>
public static bool CheckObjDistanceXY(Vector3 srcPos, Vector3 targetPos, float dis)
{
return GetObjDistanceXY(srcPos, targetPos) <= dis;
}
#endregion

#region 角度转弧度
/// <summary>
/// 角度转弧度的方法
/// </summary>
/// <param name="deg">要转换的角度</param>
/// <returns>转换出来的弧度</returns>
public static float DegToRad(float deg)
{
return deg * Mathf.Deg2Rad;
}

/// <summary>
/// 弧度转角度的方法
/// </summary>
/// <param name="rad">要转换的弧度</param>
/// <returns>转换出来的弧度</returns>
public static float RadToDeg(float rad)
{
return rad * Mathf.Rad2Deg;
}
#endregion

#region 射线检测相关
/// <summary>
/// 指定距离 指定层级 的射线检测,获取一个对象
/// </summary>
/// <param name="ray">射线</param>
/// <param name="callBack">回调函数(会把碰到的RaycastHit消息传递出去)</param>
/// <param name="maxDistance">最大距离</param>
/// <param name="layerMask">层级筛选</param>
public static void Raycast(Ray ray, UnityAction<RaycastHit> callBack, float maxDistance, int layerMask)
{
if (Physics.Raycast(ray, out RaycastHit hitInfo, maxDistance, layerMask))
{
callBack(hitInfo);
}
}

/// <summary>
/// 指定距离 指定层级 的射线检测,获取一个对象
/// </summary>
/// <param name="ray">射线</param>
/// <param name="callBack">回调函数(会把碰到的GameObject消息传递出去)</param>
/// <param name="maxDistance">最大距离</param>
/// <param name="layerMask">层级筛选</param>
public static void Raycast(Ray ray, UnityAction<GameObject> callBack, float maxDistance, int layerMask)
{
if (Physics.Raycast(ray, out RaycastHit hitInfo, maxDistance, layerMask))
{
callBack(hitInfo.collider.gameObject);
}
}

/// <summary>
/// 指定距离 指定层级 的射线检测,获取一个对象
/// </summary>
/// <param name="ray">射线</param>
/// <param name="callBack">回调函数(会把碰到的对象上指定的脚本传递出去)</param>
/// <param name="maxDistance">最大距离</param>
/// <param name="layerMask">层级筛选</param>
public static void Raycast<T>(Ray ray, UnityAction<T> callBack, float maxDistance, int layerMask)
{
if (Physics.Raycast(ray, out RaycastHit hitInfo, maxDistance, layerMask))
{
callBack(hitInfo.collider.gameObject.GetComponent<T>());
}
}

/// <summary>
/// 指定距离 指定层级 的射线检测,可获取多个对象,会执行多次委托以传递
/// </summary>
/// <param name="ray">射线</param>
/// <param name="callBack">回调函数(会把碰到的RaycastHit消息传递出去)</param>
/// <param name="maxDistance">最大距离</param>
/// <param name="layerMask">层级筛选</param>
public static void RaycastAll(Ray ray, UnityAction<RaycastHit> callBack, float maxDistance, int layerMask)
{
RaycastHit[] hitInfos = Physics.RaycastAll(ray, maxDistance, layerMask);
for (int i = 0; i < hitInfos.Length; i++)
{
callBack.Invoke(hitInfos[i]);
}
}

/// <summary>
/// 指定距离 指定层级 的射线检测,可获取多个对象,会执行多次委托以传递
/// </summary>
/// <param name="ray">射线</param>
/// <param name="callBack">回调函数(会把碰到的GameObject消息传递出去)</param>
/// <param name="maxDistance">最大距离</param>
/// <param name="layerMask">层级筛选</param>
public static void RaycastAll(Ray ray, UnityAction<GameObject> callBack, float maxDistance, int layerMask)
{
RaycastHit[] hitInfos = Physics.RaycastAll(ray, maxDistance, layerMask);
for (int i = 0; i < hitInfos.Length; i++)
{
callBack.Invoke(hitInfos[i].collider.gameObject);
}
}

/// <summary>
/// 指定距离 指定层级 的射线检测,可获取多个对象,会执行多次委托以传递
/// </summary>
/// <param name="ray">射线</param>
/// <param name="callBack">回调函数(会把碰到的对象上指定的脚本传递出去)</param>
/// <param name="maxDistance">最大距离</param>
/// <param name="layerMask">层级筛选</param>
public static void RaycastAll<T>(Ray ray, UnityAction<T> callBack, float maxDistance, int layerMask)
{
RaycastHit[] hitInfos = Physics.RaycastAll(ray, maxDistance, layerMask);
for (int i = 0; i < hitInfos.Length; i++)
{
callBack.Invoke(hitInfos[i].collider.gameObject.GetComponent<T>());
}
}
#endregion

#region 范围检测相关
/// <summary>
/// 进行盒状检测
/// </summary>
/// <typeparam name="T">要获取的对象类型</typeparam>
/// <param name="center">盒子中心点</param>
/// <param name="rotation">盒子的角度</param>
/// <param name="halfExtents">长宽高的一半</param>
/// <param name="layerMask">层级筛选</param>
/// <param name="callBack">回调函数</param>
public static void OverlapBox<T>(Vector3 center, Quaternion rotation, Vector3 halfExtents, int layerMask, UnityAction<T> callBack) where T : class
{
Type type = typeof(T);
Collider[] colliders = Physics.OverlapBox(center, halfExtents, rotation, layerMask, QueryTriggerInteraction.Collide);
for (int i = 0; i < colliders.Length; i++)
{
if (type == typeof(Collider))
{
callBack.Invoke(colliders[i] as T);
}
else if (type == typeof(GameObject))
{
callBack.Invoke(colliders[i].gameObject as T);
}
else
{
callBack.Invoke(colliders[i].gameObject.GetComponent<T>());
}
}
}

/// <summary>
/// 进行球状检测
/// </summary>
/// <typeparam name="T">要获取的对象类型</typeparam>
/// <param name="center">球体中心点</param>
/// <param name="radius">球体半径</param>
/// <param name="layerMask">层级筛选</param>
/// <param name="callBack">回调函数</param>
public static void OverlapSphere<T>(Vector3 center, float radius, int layerMask, UnityAction<T> callBack) where T : class
{
Type type = typeof(T);
Collider[] colliders = Physics.OverlapSphere(center, radius, layerMask, QueryTriggerInteraction.Collide);
for (int i = 0; i < colliders.Length; i++)
{
if (type == typeof(Collider))
{
callBack.Invoke(colliders[i] as T);
}
else if (type == typeof(GameObject))
{
callBack.Invoke(colliders[i].gameObject as T);
}
else
{
callBack.Invoke(colliders[i].gameObject.GetComponent<T>());
}
}
}

/// <summary>
/// 进行胶囊体检测
/// </summary>
/// <typeparam name="T">要获取的对象类型</typeparam>
/// <param name="point0">胶囊体端点一</param>
/// <param name="point1">胶囊体端点二</param>
/// <param name="radius">胶囊体半径</param>
/// <param name="layerMask">层级筛选</param>
/// <param name="callBack">回调函数</param>
public static void OverlapCapsule<T>(Vector3 point0, Vector3 point1, float radius, int layerMask, UnityAction<T> callBack) where T : class
{
Type type = typeof(T);
Collider[] colliders = Physics.OverlapCapsule(point0, point1, radius, layerMask, QueryTriggerInteraction.Collide);
for (int i = 0; i < colliders.Length; i++)
{
if (type == typeof(Collider))
{
callBack.Invoke(colliders[i] as T);
}
else if (type == typeof(GameObject))
{
callBack.Invoke(colliders[i].gameObject as T);
}
else
{
callBack.Invoke(colliders[i].gameObject.GetComponent<T>());
}
}
}
#endregion
}