news 2026/10/12 4:25:23

直线与圆碰撞检测:游戏开发必知技巧

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
直线与圆碰撞检测:游戏开发必知技巧

游戏中,直线与圆的关系经常用来处理:

  • 子弹会不会击中角色;
  • 激光是否碰到护盾;
  • 移动路线会不会穿过危险区域。

我们先讨论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 敌人 ◯ 下一帧位置 B

A 在敌人外面,B 也在敌人外面。

如果只检查这两个位置,就可能认为:

上一帧没碰到 下一帧也没碰到 → 没命中

但子弹实际已经从敌人身体中穿过了。

解决办法

把这一帧内的移动看成线段:

A ─────────── ◯ ─────────── B

检查A 到 B 的整段路径是否接触圆形碰撞区域,而不是只检查终点。

对于这一帧内沿直线运动的点状子弹,以及位置不变的圆形目标,这能避免这种漏检。


五、实际检测:找圆心到线段的最近点

设:

  • A:子弹上一帧位置;
  • B:子弹下一帧位置;
  • O:敌人圆心;
  • r:敌人碰撞半径。

判断思路:

  1. 在线段AB上找到离圆心O最近的点P;
  2. 计算O到P的距离;
  3. 如果距离不大于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 米

最后记住两句话:

数学上,看圆心到直线的距离,与半径比较。
游戏里,还必须考虑发射方向、射程,以及物体本身的大小。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/12 4:22:41

DDPG容错控制:关节锁死时机械臂仍能画圆

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 4:21:43

YOLO26改进 - 卷积Conv | StripBlockStripBlock大条带卷积块,通过顺序条带卷积捕获方向性长程依赖,强化高长宽比目标特征提取 | AAAI 2026

前言 本文介绍了面向细长目标特征建模的条带卷积模块——StripBlock,作为传统方形卷积感受野不足的有效补充。该方法通过深度卷积提取局部信息,并利用连续的横向与纵向长条卷积增强长距离空间依赖,生成空间注意力权重对原特征进行门控重标定,从而更精准捕获高纵横比目标的…

作者头像 李华
网站建设 2026/10/12 4:20:52

关系数据库分布式大数据集成冲突消解算法与落地实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 4:19:32

利用PyTorch手写MobileNetV2:从倒残差结构到移动端部署实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/12 4:18:58

研华IPC-510工业数据采集系统部署与稳定性优化实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华