当前位置: 首页 > news >正文

[Godot] 通过AABB包围盒和射线法检测碰撞

前言

最近需要做鼠标选择单位的功能,所以给大家分享一下我是如何实现的,简单来说,是在鼠标点击后,通过我写的获取单位点集的方法,先通过AABB包围盒进行粗筛,然后再通过射线法进行精确判定,最后根据Y轴的大小得到选中的单位,下面我给大家分享一下我的实现逻辑

效果展示


代码实现

鼠标点击

这里本来没有什么好说的,但是给大家分享一个普遍可能会遇到的问题,就是鼠标点击UI,因为比如我直接用的Input.IsActionJustPressed()方法做点击,这回导致当我们点击UI的时候也会去做点击单位,解决办法如下:

//当点击UI时直接返回if(gameManager.GetViewport().GuiGetHoveredControl()!=null){return;}

这样,我们只需要注意把不会遮挡的UI,比如占满全屏的Control节点的的Mouse-Filter设置为Ignore就好

AABB包围盒

介绍

AABB(Axis-Aligned Bounding Box)的全称是轴对齐包围盒。简单来说,就是找一个最小的矩形,能把你的多边形完全框住,我们先用他进行粗筛,否则如果每次都要对所有单位做精确的多边形判定,CPU会直接原地爆炸

包围盒计算

首先注意,我们要提前设置好单位的点集,因为之后的射线法判断,所以需要按顺序设置,这里我用的是List存的点集,用Rect2存的AABB包围盒,接下来我们需要计算包围盒,注意计算好的包围盒需要跟着单位作为常量

publicRect2CalulateAABB(ListpointSet){floatminX=pointSet[0].X;floatmaxX=pointSet[0].X;floatminY=pointSet[0].Y;floatmaxY=pointSet[0].Y;for(inti=0;i<pointSet.Count;i++){Vector2p=pointSet[i];if(p.X<minX)minX=p.X;elseif(p.X>maxX)maxX=p.X;if(p.Y<minY)minY=p.Y;elseif(p.Y>maxY)maxY=p.Y;}returnnewRect2(newVector2(minX,minY),newVector2(maxX-minX,maxY-minY));}

这里的逻辑就是:不断更新四个边界值,最后用左下角坐标和宽高构造出矩形,因为我们是用的Rect2,所以注意用的两个点分别是positionsize

使用AABB进行粗筛

// 先通过AABB包围盒粗筛foreach(varunitinunitPointSet){if(unit.AABB.HasPoint(mousePos-unit.nowPos)){// 通过射线法判断是否在多边形内if(IsPointInPolygon(mousePos-unit.nowPos,unit.pointSet))tmpUnit.Add(unit);}}

这里有个细节:mousePos - unit.nowPos是把鼠标坐标转换到单位的局部坐标系。因为我们存储的点集都是相对于单位中心(0,0)的,所以需要先做这个转换

射线法精确判定

通过了AABB粗筛之后,就要进入真正的重头戏了:判断点是否在多边形内部

射线法的核心思想

想象你站在某个位置,往右边射出一根无限长的射线。这根射线会和多边形的边界相交。如果交点数量是奇数,说明你在多边形内部;如果是偶数(包括0),说明你在外部

我们可以把它想象成"翻墙"的过程:

  • 你在外面,翻过1道墙,就进去了(奇数)
  • 再翻1道墙,就出来了(偶数)
  • 再翻1道墙,又进去了(奇数)

所以,统计撞击次数的奇偶性,就能判断最终位置是在内还是外

代码实现
//射线法,连接点(顺序),判断碰撞为奇数在多边形内privateboolIsPointInPolygon(Vector2mouse,ListpointSet){boolinside=false;for(inti=0,j=pointSet.Count-1;i<pointSet.Count;j=i++){if((pointSet[i].Y>mouse.Y)!=(pointSet[j].Y>mouse.Y)&&mouse.X<(pointSet[j].X-pointSet[i].X)*(mouse.Y-pointSet[i].Y)/(pointSet[j].Y-pointSet[i].Y)+pointSet[i].X)inside=!inside;}returninside;}

这段代码非常精简,接下来我给大家拆开讲解一下

1) 双指针

首先看循环头部:for (int i = 0, j = pointSet.Count - 1; i < pointSet.Count; j = i++)
这里为什么要用两个指针,而不是直接用(i + 1) % pointSet.Count?

原因如下:

  1. 性能考虑: 模除运算在底层涉及除法,比简单的赋值和自增要慢。在需要对成百上千个单位进行实时判定的游戏中,这种细微差别会累积成可观的性能开销

  2. 逻辑清晰:j = i++这种"指针尾随"的写法是图形学算法的标准写法,一开始就处理封口边,循环体内部非常纯净

2) 坐标计算
  1. 高度判断
(pointSet[i].Y>mouse.Y)!=(pointSet[j].Y>mouse.Y)

这一行是在判断:这条边的两个端点,一个在鼠标上方,一个在鼠标下方
如果两个点都在鼠标上方,或者都在下方,那这条边根本不会和水平射线相交,直接跳过就行

  1. 计算交点X坐标

这是整个算法最核心的数学部分:

mouse.X<(pointSet[j].X-pointSet[i].X)*(mouse.Y-pointSet[i].Y)/(pointSet[j].Y-pointSet[i].Y)+pointSet[i].X

我们把它看作**“相似三角形”或者“线性插值比例”**,就很容易理解了 :

  • 先算垂直进度比例:(mouse.Y - p1.Y) / (p2.Y - p1.Y)。这就是算出当前鼠标的高度,占这条线段总高度的百分之几 。

  • 应用到水平方向:因为点在同一条斜线上,你垂直方向爬了 50%,水平方向也一定移动了总宽度的 50% 。所以用线段总宽度 (p2.X - p1.X) 乘以这个比例,算出 X 轴上的偏移量。

  • 坐标还原:最后加上起点的 p1.X,就是地图上的真实绝对坐标了 。

对应的数学公式表现为:

hitX=ΔX×mouseOffsetΔY+XstarthitX = \Delta X \times \frac{\text{mouseOffset}}{\Delta Y} + X_{\text{start}}hitX=ΔX×ΔYmouseOffset+Xstart

完整判断代码

//注意我这里返回的是我单位的唯一Id,-1就是空,大家根据自己的需要修改publiclongCalculateChooseUnit(Vector2mousePos,List<(longid,List<Vector2>pointSet,Rect2 AABB,Vector2 nowPos)>unitPointSet){List<(pointSet,Rect2 AABB,Vector2 nowPos)>tmpUnit=new();// 先通过AABB包围盒粗筛foreach(varunitinunitPointSet){if(unit.AABB.HasPoint(mousePos-unit.nowPos)){// 通过射线法判断是否在多边形内if(IsPointInPolygon(mousePos-unit.nowPos,unit.pointSet))tmpUnit.Add(unit);}}if(tmpUnit.Count==0)return(-1);// 如果有多个单位重叠,选择Y坐标最大的(最下面的)varmaxY=tmpUnit[0];for(inti=1;i<tmpUnit.Count;i++){if(tmpUnit[i].nowPos.Y>maxY.nowPos.Y)maxY=tmpUnit[i];}return(maxY.id);}

结尾

以上就是我实现鼠标选择,检测碰撞的思路。AABB粗筛保证性能,射线法保证准确性,两者结合能处理各种复杂形状,感谢大家观看

http://www.jsqmd.com/news/406595/

相关文章:

  • 2026最新版 Anaconda 下载与安装全流程详解(超详细图文教程)
  • 【建议只看博客教程学习】顶流工程设计服务 GoEngineer 全解析!解锁 CAD/3D 打印一站式解决方案
  • 爆火实测!Nullspace EM vs HFSS vs CST 电磁仿真三巨头终极对比,选型不踩坑
  • 2026年刮泥机服务商性价比深度评估:这三家值得关注 - 2026年企业推荐榜
  • 基于Django的洗衣服务平台设计与实现
  • 2026年温州地区优质猫玩具激光笔生产商盘点与推荐 - 2026年企业推荐榜
  • python基于GSP网店商品管理系统设计与实现
  • django框架基于协同过滤算法的景点美食可视化分析系统【景点(地区)】
  • springboot基于Hadoop的豆瓣电子图书推荐系统_28r41260
  • 专业视角:2026年猫玩具激光笔可靠供应商盘点与推荐 - 2026年企业推荐榜
  • 金属流水景墙行业深度评估:2026年顶尖厂家推荐与联系指南 - 2026年企业推荐榜
  • AI原生应用开发必知:GPT模型微调技巧大全
  • 2026全铝阳台柜选购指南:如何找到靠谱生产商 - 2026年企业推荐榜
  • Balancing Robustness and Accuracy in Mixture-of-Experts
  • 2026年Q1顶尖全铝焊接大板厂家深度评选与选型指南 - 2026年企业推荐榜
  • Java Web 在线家具商城设计与实现pf系统源码-SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0【含文档】
  • 2026年新疆工程外加剂专业销售公司综合盘点 - 2026年企业推荐榜
  • Making Mixture-of-Experts Robust: A Dual-Model Strategy for Accuracy and Adversarial Defense
  • 春晚音质封神!追觅电视大师声学系统,承包春晚全场景听觉体验
  • 从 FWT 到 FFT
  • API调用还是本地部署?LLM使用策略对比
  • 智平方机器人宣布完成10亿融资:估值超百亿 百度与中车是投资方
  • 大数据领域Doris的动态分区管理技巧
  • 洗车店就在家门口 50 米,我问 AI 怎么去,它说“走过去“—— 深入剖析为什么 AI 会集体翻车?
  • python+uniapp微信小程序的文明城市创建平台设计与实现
  • python+uniapp微信小程序的外卖点餐点单系统 商家协同过滤
  • python+uniapp微信小程序的大悦城地下停车场车位预约收费系统_
  • python+uniapp微信小程序的宠物生活服务预约系统 宠物陪玩遛狗溜猫馆设计与实现 商家_
  • vcs启动verdi单步调试功能
  • python+uniapp微信小程序的体育用品羽毛球购物商城