抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

简介

射线检测可以用于拾取物体、判断前方是否有障碍物、判断是否碰撞等场景,本文介绍射线与圆的检测原理。

原理

射线与圆的检测,有以下几点判断:

  1. 射线方向是否与射线起点与圆的方向相反,是则不相交;

  2. 射线是否过短,是则不相交;

    如图所示,射线 R 在射线原点与圆心方向的投影加上圆 C 的半径长度之和小于射线原点与圆心的距离 RC,射线过短。(黑线表示距离和,蓝线表示 RC)

  3. 射线是否在园内,是则必定相交;

  4. 当射线与圆相交的时候,如下图所示:

    我们投影射线原点与圆心的距离 d 到射线 R 上,记为 p,并通过圆心作一条垂直于射线 R 的垂线 g,我们可以得到如下关系: f1 : $p^2+g^2=d^2$

    我们把交点到射线原点的距离记为 t,到垂线 g 的距离记为 s,线段 s 满足以下关系:f2 : $s^2+g^2=r^2$

    我们合并两个三角形方程f1f2,得到方程 $p^2+g^2-s^2+g^2=d^2-r^2$,化简得到:f3 : $s^2=p^2-d^2+r^2$

    由此可知,如果射线与圆相交,则以上等式恒成立,即如果以上等式不成立的情况下,射线与圆不相交。因此我们判断方程f3的情况是否成立,开根号得到: f4 : $s=\pm\sqrt[]{p^2-d^2+r^2}$,那么我们只要判断$p^2-d^2+r^2>=0$就知道射线与圆是否相交,p、d、r 均为已知数。

    当我们要求交点的时候,我们只要带入上文已求的 p 和 s,得到近点距离为:$t=p-s$;远点距离为:$t=p+s$,然后以射线原点出发,加上方向向量的单位向量乘以距离,即可求出交点。

对于三维空间来说,我们求的也是射线与球的一个面,即圆的关系。因此算法和二维空间一样。

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
//包围盒数据结构
public class CollisionData : MonoBehaviour
{
public Vector3 center = Vector3.zero;
public float radius = 1.0f;
public Vector3 direction = Vector3.zero;
}
---------------------------------------------
/// <summary>
/// 射线和球检测
/// <param name="data1"></param>
/// <param name="data2"></param>
/// </summary>
private bool CollisionRay2Circle(CollisionData data1,CollisionData data2)
{

Vector3 centerDis = data2.center - data1.center;
Vector3 direction = data1.direction;

float projection = Vector3.Dot(centerDis, direction);
float r2 = Mathf.Pow(data2.radius, 2);
float f = Mathf.Pow(projection, 2) + r2 - centerDis.sqrMagnitude;

//方向相反
bool checkDirection = projection < 0;
//射线过短
bool checkDistance = centerDis.sqrMagnitude > Mathf.Pow(data1.radius + data2.radius, 2);
//射线起点在球内部
bool checkNotInside = centerDis.sqrMagnitude > r2;
//不相交
bool checkNotCollide = f < 0;

if (checkNotInside && (checkDirection || checkDistance || checkNotCollide))
{
return false;
}

float dis = projection - Mathf.Sqrt(f) * (checkNotInside ? 1 : -1);
Vector3 point = data1.center + data1.direction * dis;
ConsoleUtils.Log("碰撞点", point);

return true;
}

碰撞检测示例工程

评论