游戏中,直线与圆的关系经常用来处理:
- 子弹会不会击中角色;
- 激光是否碰到护盾;
- 移动路线会不会穿过危险区域。
我们先讨论2D 游戏,或把场景投影到地面的情况。在 3D 中,对应的问题通常是直线与球体的关系。
一、案例:激光会不会碰到圆形护盾?
假设角色周围有一个圆形护盾:
- 圆心
O:角色的位置; - 半径
r:护盾的大小; - 直线:激光所在的路线。
直线与圆有三种位置关系。
1. 相离:激光从旁边经过
激光 ───────────────── ◯ 护盾直线与圆没有公共点,不会碰到护盾边界。
2. 相切:激光刚好擦到护盾
直线刚好碰到圆的边缘,只有一个公共点,这个点叫切点。
在数学上属于接触;游戏是否把“擦边”算命中,可以按规则决定。
3. 相交:激光穿过护盾所在区域
圆形护盾 / \ 激光 ────P────O────Q──── \ /直线与圆有两个公共点:
P:一侧的交点;Q:另一侧的交点。
如果激光从左向右、从圆外射来,那么P就是首次碰到护盾的位置。
注意:相交不要求经过圆心,偏离圆心但穿过圆内部也算相交。
二、最简单的判断:比较距离与半径
关键不是先找交点,而是求:
圆心到直线的最短距离
d。
这个距离是垂直距离:
O 圆心 │ │ d │ ──────────H────────── 直线然后比较:
| 条件 | 关系 | 游戏中的直观含义 |
|---|---|---|
d > r | 相离 | 从旁边经过 |
d = r | 相切 | 刚好擦边 |
d < r | 相交 | 穿过圆形区域 |
具体数值
护盾半径是2 米:
圆心离激光 3 米 → 没碰到 圆心离激光 2 米 → 刚好擦边 圆心离激光 1 米 → 穿过护盾这就是最核心的判断方法。
如果直线方程是:
[
ax+by+c=0
]
圆心是 ((x_0,y_0)),那么:
[
d=\frac{|ax_0+by_0+c|}{\sqrt{a2+b2}}
]
其中a、b不能同时为零。
三、游戏中的关键区别:直线、射线、线段
直线会碰到圆,不代表子弹真的能碰到。
1. 直线:两头无限延伸
适合用来分析位置关系,但多数实际攻击不是无限直线。
2. 射线:从起点出发,只沿一个方向
例如:
护盾 ◯ 枪口 S ─────────→护盾在枪口后方。
即使把弹道向后延长能碰到护盾,向右发射的子弹也不会击中它。
所以,射线检测还要确认:交点是不是在发射方向的前方。
3. 线段:有起点,也有终点
例如:
枪口 S ─────── E ◯ 护盾 射程终点弹道延长后能碰到护盾,但实际射程不够。
所以,线段检测还要确认:碰撞位置是否在起点与终点之间。
四、案例:子弹太快,为什么会“穿人”?
假设每帧只检查一次子弹位置:
上一帧位置 A 敌人 ◯ 下一帧位置 BA 在敌人外面,B 也在敌人外面。
如果只检查这两个位置,就可能认为:
上一帧没碰到 下一帧也没碰到 → 没命中但子弹实际已经从敌人身体中穿过了。
解决办法
把这一帧内的移动看成线段:
A ─────────── ◯ ─────────── B检查A 到 B 的整段路径是否接触圆形碰撞区域,而不是只检查终点。
对于这一帧内沿直线运动的点状子弹,以及位置不变的圆形目标,这能避免这种漏检。
五、实际检测:找圆心到线段的最近点
设:
A:子弹上一帧位置;B:子弹下一帧位置;O:敌人圆心;r:敌人碰撞半径。
判断思路:
- 在线段
AB上找到离圆心O最近的点P; - 计算
O到P的距离; - 如果距离不大于
r,就接触或进入了圆形区域。
Unity 2D 可以这样写:
boolSegmentHitsCircle(Vector2a,Vector2b,Vector2center,floatradius){Vector2ab=b-a;floatlengthSquared=ab.sqrMagnitude;// 子弹没有移动,退化为点与圆形区域的检测if(lengthSquared==0f)return(a-center).sqrMagnitude<=radius*radius;floatt=Vector2.Dot(center-a,ab)/lengthSquared;// 限制最近点必须在线段上t=Mathf.Clamp01(t);Vector2closest=a+t*ab;return(closest-center).sqrMagnitude<=radius*radius;}这里比较的是距离的平方,不用开平方也能判断。
注意,这检测的是实心的圆形区域:即使整条线段都在圆内部,也会返回true。它不只是检查有没有穿过圆周。
六、子弹本身也有大小怎么办?
如果子弹也是圆形的,半径为r₁,敌人半径为r₂,可以把敌人的检测半径扩大为:
[
R=r_1+r_2
]
然后把子弹中心当成一个点,检查它的移动线段是否接触这个扩大后的圆形区域。
例如:
子弹半径:0.1 米 敌人半径:0.5 米 用于检测的半径:0.6 米最后记住两句话:
数学上,看圆心到直线的距离,与半径比较。
游戏里,还必须考虑发射方向、射程,以及物体本身的大小。