我正在尝试创建一个 2D 平台游戏(马里奥类型)游戏,我在正确处理碰撞方面遇到了一些问题。我正在用 C++ 编写这个游戏,使用 SDL 进行输入、图像加载、字体加载等。我还通过 FreeGLUT 库结合 SDL 使用 OpenGL 来显示图形。
我的碰撞检测方法是 AABB(轴对齐边界框),这确实是我需要的全部开始。我需要的是一种既能检测碰撞发生在哪一侧又能正确处理碰撞的简单方法。所以,基本上,如果玩家与平台顶部发生碰撞,请将他重新定位到顶部;如果侧面发生碰撞,请将玩家重新定位到物体的侧面;如果底部发生碰撞,请将播放器重新定位在平台下方。
我已经尝试了许多不同的方法来做到这一点,例如尝试找到穿透深度并根据穿透深度向后重新定位玩家。可悲的是,我尝试过的任何事情似乎都无法正常工作。玩家的移动最终变得非常不稳定,并在我不希望的时候重新定位玩家。部分原因可能是因为我觉得这很简单,但我想多了。
如果有人认为他们可以提供帮助,请查看下面的代码,如果可以的话,请帮助我尝试对此进行改进。如果可能的话,我想避免使用库来处理这个问题(因为我想自己学习)或类似 SAT(分离轴定理)的东西。预先感谢您的帮助!
void world1Level1CollisionDetection()
{
for(int i; i < blocks; i++)
{
if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==true)
{
de2dObj ballPrev;
ballPrev.coords[0] = ball.coords[0];
ballPrev.coords[1] = ball.coords[1];
ballPrev.coords[2] = ball.coords[2];
ballPrev.coords[3] = ball.coords[3];
ballPrev.coords[0] -= ball.xspeed;
ballPrev.coords[1] -= ball.yspeed;
ballPrev.coords[2] -= ball.xspeed;
ballPrev.coords[3] -= ball.yspeed;
int up = 0;
int left = 0;
int right = 0;
int down = 0;
if (ballPrev.coords[0] < block[i].coords[0] && ballPrev.coords[2] < block[i].coords[0] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1])) || ((ball.coords[1] < block[i].coords[3]) || ball.coords[3] < block[i].coords[3])))
{
left = 1;
}
if (ballPrev.coords[0] > block[i].coords[2] && ballPrev.coords[2] > block[i].coords[2] && (((ball.coords[1] < block[i].coords[1]) || (ball.coords[3] < ball.coords[1])) || ((ball.coords[1] < block[i].coords[3]) || (ball.coords[3] < block[i].coords[3]))))
{
right = 1;
}
if(ballPrev.coords[1] < block[i].coords[1] && block[i].coords[1] < ballPrev.coords[3] && ballPrev.coords[3] < block[i].coords[3])
{
up = 1;
}
if(block[i].coords[1] < ballPrev.coords[1] && ballPrev.coords[1] < block[i].coords[3] && block[i].coords[3] < ballPrev.coords[3])
{
down = 1;
}
cout << left << ", " << right << ", " << up << ", " << down << ", " << endl;
if (left == 1)
{
ball.coords[0] = block[i].coords[0] - 18.0f;
ball.coords[2] = block[i].coords[0] - 2.0f;
}
else if (right == 1)
{
ball.coords[0] = block[i].coords[2] + 2.0f;
ball.coords[2] = block[i].coords[2] + 18.0f;
}
else if (down == 1)
{
ball.coords[1] = block[i].coords[3] + 4.0f;
ball.coords[3] = block[i].coords[3] + 20.0f;
}
else if (up == 1)
{
ball.yspeed = 0.0f;
ball.gravity = 0.0f;
ball.coords[1] = block[i].coords[1] - 17.0f;
ball.coords[3] = block[i].coords[1] - 1.0f;
}
}
if (de2dCheckCollision(ball,block[i],0.0f,0.0f)==false)
{
ball.gravity = -0.5f;
}
}
}
解释这段代码的含义:
block 变量基本上是一个整数,用于存储 block 或平台的数量。我正在使用 for 循环检查所有 block ,循环当前所在的数字由整数 i 表示。 坐标系可能看起来有点奇怪,所以值得解释一下。 coords[0] 表示对象的 x 位置(左)(它在 x 轴上开始的位置)。 coords[1] 表示对象的 y 位置(顶部)(它在 y 轴上开始的位置)。 coords[2] 表示对象的宽度加上 coords[0](右)。 coords[3] 表示物体的高度加上 coords[1](底部)。 de2dCheckCollision 执行 AABB 碰撞检测。 向上是负 y,向下是正 y,就像在大多数游戏中一样。
希望我已经提供了足够的信息让某人成功地帮助我。如果我遗漏了可能至关重要的内容,请告诉我,我会提供必要的信息。最后,对于任何可以提供帮助的人,提供代码将非常有帮助并且非常感谢。
再次感谢您的帮助!
编辑 2:我用一种新算法更新了我的代码,该算法检查球在碰撞之前的位置。边角案例现在可以在那个单一平台上正常工作,当我有一堵物体墙时,我现在可以正确地靠着它滑动。唯一剩下的问题是当我在地面上时会发生一个小的抖动效果,球不断地上下移动,好像它被重力拉动然后又落回物体中。
编辑:这是一个图像的 URL,试图显示我遇到的各种问题: http://img8.imageshack.us/img8/4603/collisionproblem.png
如果图片中的解释没有太多意义,除非我跳过它,否则球不能向左移动通过对象的角。然而,球可以向右移动,但它会在移动时重新定位到对象的右侧,这是不需要的。这实质上创建了一个跳跃运动,当我向右移动时,球似乎跳过了一半左右的物体。如果这没有意义,请问我,我会尽力澄清更多。
最佳答案
您的代码的一个问题是您只能检测到这样的情况:
如果圆恰好完全位于 block 内,则根本不需要重新定位。这是一个问题。
您试图将您的模拟视为连续的,但请记住它是离散的。一般来说,如果你只看球的当前状态,你真的无法知道它撞到了哪一边。看看这两种可能性:
想到的第一个解决方案是同时查看球的最后位置;更准确地说,看看 delta vector 。查看增量 vector 是否与墙相交。如果是,则沿轴对齐方向重新定位到与增量 vector 相交的墙。
编辑:当我说“增量 vector ”时,我忘记了您要移动的是一个正方形而不是一个点。所以,如果你只看左上角的 delta vector ,那是不够的,因为它可能无法检测到球的那部分进入了一个方 block 。相反,您可以查看所有 4 个角的增量 vector 。
关于c++ - 2D 平台碰撞处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5149548/