首页 游戏 软件 资讯 排行榜 专题
首页
编程语言
C++如何检测两个圆形是否相交 _ 游戏开发几何碰撞检测逻辑【干货】

C++如何检测两个圆形是否相交 _ 游戏开发几何碰撞检测逻辑【干货】

热心网友
61
转载
2026-05-01

C++如何检测两个圆形是否相交 | 游戏开发几何碰撞检测逻辑【干货】

C++如何检测两个圆形是否相交 _ 游戏开发几何碰撞检测逻辑【干货】

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

判断两个圆是否相交,核心逻辑其实就一句话:圆心距的平方是否小于等于半径和的平方。如果严格区分,外离是圆心距平方大于半径和的平方,内含则是圆心距平方小于半径差的平方(这里假设第一个圆半径更大)。至于内切和外切这两种临界状态,在实际编程中必须引入一个容差ε来处理,否则浮点误差会让你抓狂。

用距离平方判断比开方更高效

从几何定义上看,两个圆相交的充要条件是圆心距离小于等于半径之和。但如果你在代码里老老实实地写 sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)),那就掉进性能陷阱了。浮点开方运算开销大,还会引入不必要的精度扰动。要知道,游戏循环里每帧可能要进行成百上千次这样的检测,sqrt 函数能不用就不用。

那怎么办?其实很简单,把不等式两边平方一下就行了。因为距离和半径都是非负数,平方操作不会改变不等号的方向。这样一来,判断逻辑就变成了:

dx = x1 - x2;
dy = y1 - y2;
r_sum = r1 + r2;
if (dx * dx + dy * dy <= r_sum * r_sum) {
    // 相交(含内切、外切、相交、包含)
}

这里有个细节需要注意:这个判断条件会把“一个圆完全包含在另一个圆内部”的情况也归类为“相交”。如果你的游戏逻辑需要严格区分“边缘相交”和“完全包含”,比如子弹必须碰到怪物边缘才算击中,被护盾完全包裹不算受伤,那就得在这个基础上再加一层判断。

区分“相交”“内含”“相离”三种状态

对于复杂的游戏逻辑,光知道“是否相交”往往不够。比如,粒子碰撞后需要反弹,这就得明确是“相交”;而判断一个角色是否被保护罩完全覆盖,则需要识别“内含”状态。这就需要我们把距离与半径的关系拆解得更细致一些:

立即学习“C++免费学习笔记(深入)”;

  • dx*dx + dy*dy > (r1 + r2)*(r1 + r2) → 相离
  • dx*dx + dy*dy < (r1 - r2)*(r1 - r2)(这里假设 r1 >= r2)→ 内含(小圆完全在大圆内,无交点)
  • 其余情况 → 相交(含外切、内切、部分重叠)

这里有个关键点要特别注意:计算半径差的平方时,一定要用绝对值差。直接写 (r1 - r2)*(r1 - r2)r2 > r1 时会得到负值,导致判断出错。稳妥的写法是用 abs(r1 - r2) 取绝对值后再平方,或者直接比较 dx*dx + dy*dy((r1 > r2) ? (r1 - r2) : (r2 - r1)) * ... 的结果。

浮点误差下如何处理“恰好相切”

理论上,外切的条件是圆心距严格等于半径和。但在浮点数的世界里,distance == r1 + r2 这种精确相等几乎不可能发生。如果你用 == 来判断相切,那结果永远是“否”。

正确的做法是引入一个容差(epsilon)。如果你确实需要识别“近似相切”的状态(比如触发一个特殊的接触音效),可以这样判断:

float dist_sq = dx*dx + dy*dy;
float r_sum = r1 + r2;
float diff = fabsf(sqrtf(dist_sq) - r_sum); // 这里 sqrt 不可避免,但仅在极少数判定时用
if (diff < 1e-4f) { /* 近似外切 */ }

不过,更推荐的做法是全程使用平方比较,并设置一个容差区间:

float r_sum_sq = (r1 + r2) * (r1 + r2);
float eps = 1e-6f;
if (dist_sq > r_sum_sq && dist_sq < r_sum_sq + eps) { /* 视为刚接触 */ }

话说回来,对于大多数游戏物理碰撞而言,并不需要如此精细地区分相切状态。只要进入“相交”分支就触发碰撞响应,把相切视为相交的一种临界情况来处理,通常就足够了。

结构体封装与成员函数建议

最后,别再用一堆零散的 x, y, r 变量了。定义一个 struct Circle 结构体,并提供一个 intersects(const Circle& other) const 方法,代码的清晰度和复用性会立刻提升一个档次:

struct Circle {
    float x, y, r;
    bool intersects(const Circle& o) const {
        float dx = x - o.x;
        float dy = y - o.y;
        float r_sum = r + o.r;
        return dx*dx + dy*dy <= r_sum * r_sum;
    }
};

如果你的项目后期需要考虑使用SIMD指令集,或者需要进行大批量的碰撞检测(比如成千上万的子弹对敌人),那么可以将数据结构从AoS(数组结构体)转换为SoA(结构体数组)以优化性能。但对于单次检测,没必要过早优化。

真正容易被忽略,却可能引发诡异Bug的细节是:**半径必须是非负数**。如果构造函数不小心传入了一个负半径,r_sum 的计算就会出错,编译器不会报错,但运行时的行为将不可预测。一个良好的习惯是在构造函数或设置函数中加入 assert(r >= 0) 进行断言检查。

来源:https://www.php.cn/faq/2400081.html
免责声明: 游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

相关攻略

C++如何检测两个圆形是否相交 _ 游戏开发几何碰撞检测逻辑【干货】
编程语言
C++如何检测两个圆形是否相交 _ 游戏开发几何碰撞检测逻辑【干货】

C++如何检测两个圆形是否相交 | 游戏开发几何碰撞检测逻辑【干货】 判断两个圆是否相交,核心逻辑其实就一句话:圆心距的平方是否小于等于半径和的平方。如果严格区分,外离是圆心距平方大于半径和的平方,内含则是圆心距平方小于半径差的平方(这里假设第一个圆半径更大)。至于内切和外切这两种临界状态,在实际编

热心网友
05.01
C++如何解决动态库导出符号重名冲突 _ 使用namespace隔离方案【详解】
编程语言
C++如何解决动态库导出符号重名冲突 _ 使用namespace隔离方案【详解】

C++动态库导出符号重名冲突:namespace隔离的真相与实战解决方案 在开发C++动态库时,不少开发者会习惯性地将namespace视为解决符号冲突的“银弹”。然而,一个常见的误解是:只要把不同库的代码放进不同的命名空间,就能高枕无忧。事实果真如此吗? namespace不能直接解决动态库导出符

热心网友
05.01
C++ std::variant类型匹配的高级用法 _ std::visit分发实战【详解】
编程语言
C++ std::variant类型匹配的高级用法 _ std::visit分发实战【详解】

std::variant类型匹配的高级用法:std::visit分发实战【详解】 先明确一个核心的技术边界:std::visit 的设计初衷,并非直接处理多个独立的 std::variant。它的函数签名决定了,其首要参数必须是一个 std::variant 对象,后续才是可调用对象。所以,当你信心

热心网友
05.01
C++如何实现异步延迟回调执行 _ 基于jthread与chrono封装【实战】
编程语言
C++如何实现异步延迟回调执行 _ 基于jthread与chrono封装【实战】

std::jthread + sleep_for:最直接可靠的延迟回调方案 先说一个核心判断:别用 std::async 做延迟回调。 原因很简单,它并不控制执行时机,仅仅负责启动线程。延迟逻辑必须自己写进lambda里,更棘手的是,一旦关联的 std::future 生命周期结束,任务可能被无声无

热心网友
04.30
C++ std::views::join处理嵌套Range容器 _ 管道符操作符实战【详解】
编程语言
C++ std::views::join处理嵌套Range容器 _ 管道符操作符实战【详解】

C++ std::views::join:扁平化嵌套容器的正确姿势与隐藏陷阱 一句话总结:std::views::join 只认“元素本身也是可遍历范围”的嵌套结构。如果你传错了类型,编译器会直接报错,根本不会给你运行到崩溃的机会。 编译失败的典型信号:读懂错误信息 当你兴冲冲地想把一个普通的 st

热心网友
04.30

最新APP

宝宝过生日
宝宝过生日
应用辅助 04-07
台球世界
台球世界
体育竞技 04-07
解绳子
解绳子
休闲益智 04-07
骑兵冲突
骑兵冲突
棋牌策略 04-07
三国真龙传
三国真龙传
角色扮演 04-07

热门推荐

搞笑的妇女节祝福
职业与学业
搞笑的妇女节祝福

你做饭来我洗碗,你铺床来我睡眠 欢欢喜喜又一年,亲爱的,节日快乐,别太三八噢! 专属节日的仪式感 今天是你的节日,我的老婆。这话得落到实处——清晨我会为你做早饭;晚上我们还要一起浪漫!你看,仪式感这不就来了么。 祝福带来的美好氛围 不得不说,时间因祝福而流光溢彩,空气因祝福而芬芳袭人,心情因祝福而花

热心网友
05.01
有恃无恐(典故、出处、释读)
职业与学业
有恃无恐(典故、出处、释读)

有恃无恐:一则源自《左传》的古老智慧 公元前634年的夏天,对鲁国而言是个难熬的季节。灾荒肆虐,国力空虚,这无疑给了邻国一个绝佳的机会。果不其然,齐孝公亲率大军,兵锋直指鲁国。强敌压境,国库空空如也,田野一片荒芜,这局面任谁看都是绝境。然而,历史的戏剧性转折,往往就发生在看似毫无胜算的时刻。 鲁僖公

热心网友
05.01
毛遂自荐(典故、出处、释读)
职业与学业
毛遂自荐(典故、出处、释读)

《史记·平原君列传》记载 故事是这样的:赵王派平原君去楚国求救兵,平原君打算从门下食客中挑选二十位文武兼备的人一同前往。挑来选去,凑足了十九人,最后一位怎么也找不出来了。这时,毛遂主动站出来,向平原君推荐了自己。平原君打量了他一番,说道:“贤士处世,就好比锥子放在布袋里,尖儿立刻就会露出来。可先生在

热心网友
05.01
证券公司个人工作总结100字
办公文书
证券公司个人工作总结100字

以下是由本站提供的关于工作总结的文章,希望对大家有一定的帮助。更多关于工作总结的文章内容尽在本站。 篇一: 过去一年,我们营业部将总体目标锚定在创“一流服务质量、一流管理水平、一流人才队伍、一流工作业绩”上,并以“树金融服务文明形象,展金融服务专业风采”为核心创建主题,积极展开了东阳市级“青年文明号

热心网友
05.01
沉鱼落雁(典故、出处、释读)
职业与学业
沉鱼落雁(典故、出处、释读)

西施:从溪边浣纱女到倾国倾城的一代传奇 说起中国古代的绝色佳人,西施的名字总是最先被提起。这位春秋时期越国(今浙江诸暨一带)的女子,本名施夷光,别名西子。后世形容她“淡妆浓抹总相宜”,更有“沉鱼”之貌的典故流传——据说她在溪边浣纱时,水中的鱼儿都被她的容光所慑,看得入了神,以至于忘记游动而沉入水底。

热心网友
05.01