AABB碰撞响应(推回碰撞物体)

3
我在我的C++游戏中遇到了碰撞检测问题。我最初尝试使用AABB检测,成功地检测到了碰撞,但问题是我想能够相互推动,所以我采用了一个基本的解决方案,将两个物体都向后推动穿透距离的一半。但这只把物体推向了彼此成45度角的方向。链接到GIF解释 于是,我认为我需要一个推动物体的方向,所以我尝试修改了一些来自教程的代码。该方向是两个物体中心之间的归一化向量。然后将碰撞深度的一半乘以归一化向量,以使它们相互推开。这有效,但它使用了圆形碰撞(它使用半径+半径来计算碰撞深度),这意味着我不能使用不同宽度和高度的矩形,例如32x64 48x32(我必须使用32x32、48x48、64x64等等)。这是一个大问题,因为我需要能够使用不同宽度和高度的纹理 所以我又回到了AABB,并让它能够工作,而不能互相推动,所以你就会完全停止。但这里的问题是,当我在一个轴上发生碰撞时,我不能在另一个轴上移动。例如,如果我同时按下向上和向左键,在左侧的墙上发生碰撞时,我会完全停止,而不是继续向上走,那里没有墙。 所以我需要帮助的是 要么使用AABB碰撞,使物体能够正确方向上相互推动穿透距离的一半,要么只是纯粹的AABB,你可以在与一个轴碰撞时完全停止,而在非碰撞的轴上可以行走,例如同时按下向上和向下将导致这个结果

2
有趣的问题;但是为什么要加上“java”标签呢? - GhostCat
如果这不仅仅是一个学习练习的话,我建议你熟悉一下 Bullet(物理库)。它非常适合处理所有这些事情。 - Robinson
@EddyG 如果我使用标签不正确,我很抱歉。 AABB 不是在 JAVA 游戏中使用的吗? 我可能错了。 Java 标签已删除。 - DeeLaY
@Robinson 我宁愿不使用物理库来处理这个问题,因为我觉得仅仅为了碰撞而使用它是不必要的,而且我想学习如何自己实现。 - DeeLaY
1个回答

4
以下是关于AABB碰撞检测的一些思考:
假设您有两个盒子对象:O1(x,y,width,height)和O2(x,y,width,height),其中“x”和“y”是盒子对象左下角的位置。
假设您使用步长S(x,y)移动O1。
为了避免完全停止碰撞,您应该将碰撞检查分为两个部分 - 一次水平碰撞,一次垂直碰撞。
因此,移动后获得原点差异:diff = vec2(O1.x + S.x - O2.x, O1.y + S.y - O2.y) 在继续之前,您应该检查两个对象是否重叠: if ((diff.x < -O1.width || diff.x > O2.width) && (diff.y < -O1.height || diff.y > O2.height)) return; 如果它们从未重叠,则无需限制运动,我们只需跳过碰撞即可。
然后我们开始限制我们的水平运动:
1. 如果diff.x小于-O1.width,则O1在最左侧。它可以在水平方向上自由移动。因此,S.x保持不变。 2. 否则,如果diff.x大于O2.width,则O1在最右侧,并且可以在水平方向上自由移动。因此,S.x保持不变。 3. 否则,我们有一个情况,即O1在水平方向上与O2重叠。所以只需检查if(diff.x<0) S.x -= O1.width+diff.x;和相应的if(diff.x>=0) S.x += O2.width-diff.x; 您的水平运动应该已经准备好了S.x。现在,按照同样的原则,处理您的垂直运动:
1. 如果diff.y小于-O1.height,则O1很低。它可以在垂直方向上自由移动。因此,S.y保持不变。 2. 否则,如果diff.y大于O2.height,则O1很高,并且可以在垂直方向上自由移动。因此,S.y保持不变。 3. 否则,我们有一个情况,即O1在垂直方向上与O2重叠。所以只需检查if(diff.y<0) S.y -= O1.height+diff.y;和相应的if(diff.y>=0) S.y += O2.height-diff.y;

简而言之,最终你会得到一个包含新“安全”移动步骤的 S(x,y)。 你所要做的就是按照S的值将O1移动一次,并在下一步重复这个过程。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接