【问题标题】:2D Platformer Collision Handling2D 平台游戏碰撞处理
【发布时间】:2011-07-06 04:38:23
【问题描述】:

我正在尝试创建一个 2D 平台游戏(Mario 类型),但在正确处理碰撞时遇到了一些问题。我正在用 C++ 编写这个游戏,使用 SDL 进行输入、图像加载、字体加载等。我还通过 FreeGLUT 库结合 SDL 使用 OpenGL 来显示图形。

我的碰撞检测方法是 AABB(Axis-Aligned Bounding Box),这是我真正需要开始的全部。我需要一种简单的方法来检测碰撞发生在哪一侧并正确处理碰撞。因此,基本上,如果玩家与平台顶部发生碰撞,则将他重新定位到顶部;如果两侧发生碰撞,请将玩家重新定位到物体的一侧;如果底部发生碰撞,请将播放器重新定位到平台下方。

我已经尝试了许多不同的方法来做到这一点,例如尝试找到穿透深度并将玩家向后重新定位穿透深度。可悲的是,我尝试过的任何方法似乎都无法正常工作。玩家的移动最终会变得非常有问题,并在我不想要的时候重新定位玩家。部分原因可能是因为我觉得这很简单,但我想多了。

如果有人认为他们可以提供帮助,请查看下面的代码,如果可以的话,请帮助我尝试改进。如果可能的话,我想避免使用库来处理这个问题(因为我想自己学习)或者像 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;
    }
}
}

解释一下这段代码的含义:

blocks 变量基本上是一个整数,用于存储块或平台的数量。我正在使用 for 循环检查所有块,循环当前所在的数字由整数 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

如果图片中的解释没有太多意义,除非我跳过它,否则球不能向左移动超过对象的角。然而,球可以向右移动,但它会在移动时重新定位到对象的右侧,这是不需要的。这实质上会产生一个跳跃运动,当我向右移动时,它会出现在球跳过物体的一半左右时。如果这没有意义,请问我,我会尽力澄清更多。

【问题讨论】:

  • 这个问题可能更适合gamedev.stackexchange.com,但是......那里的人口似乎在各个方面都缺乏。毕竟在这里可能会更好。
  • 不管怎样,平台游戏中的移动代码确实包含很多手动调整和故障。甚至有一群人对它们进行逆向工程以制作“工具辅助的快速运行”。
  • 我以前听说过“工具辅助快速运行”。感谢您对平台游戏移动代码的提醒。看来我需要进行 lot 的手动调整才能使其正常工作。希望有人对我如何解决这个问题有一些想法。

标签: c++ opengl 2d sdl collision-detection


【解决方案1】:

您的代码的一个问题是您只能检测到这样的情况:

如果圆圈恰好完全在块内,则根本不需要重新定位。这是一个问题。

您试图将模拟视为连续的,但请记住它是离散的。一般来说,如果你看球的当前状态,你真的无法知道它撞到了哪一边。看看这两种可能性:

想到的第一个解决方案是同时查看球的最后位置;更准确地说,查看 delta 向量。查看 delta 向量是否与墙壁相交。如果是这样,请在轴对齐的方向上重新定位到与 delta 向量相交的墙壁。

编辑:当我说“增量向量”时,我忘记了您移动的是正方形而不是单个点。所以,如果你只看左上角的增量向量,那是不够的,因为它可能无法检测到球的那部分进入了一个块。相反,您可以查看所有 4 个角的 delta 向量。

【讨论】:

  • +1 用于说明解决方案。我自己也在这个过程中。
  • 我明白了。因此,如果我正确理解这一点,请检查球在实际碰撞之前的位置并尝试相应地处理它,对吗?不过,我遇到的一个大问题是在多轴上处理,以及当物体撞到角落时。
  • @defender-zone:你对我的理解是正确的。我不知道您的意思是“在多轴上处理”- delta 矢量解决方案在 x 和 y 方向上都可以正常工作。关于角落案例 - 你希望你的球在物理模拟中是圆形的,还是只是一个轴对齐的正方形?
  • @Stefan Monov:我想如果物体撞到角落,那将是一些不同的场景。谢谢你为我解开这个疑惑。另外,我的球只是一个轴对齐的正方形。我将尝试开始将您的建议合并到我的代码中。非常感谢你的帮助。我很感激。
  • @defender-zone:在纸上画这些东西——应该更清楚。对于角落的情况——这样想:如果发生角落碰撞,新算法会做什么?它会选择一堵墙来滑动,它会滑动它。它会从 2 个角墙中选择哪一个?在纸上画几个案例,你会发现它选择了正确的墙(有点难以解释,但你可以自己严格证明,或者如果你不能寻求更多帮助)。此外,我编辑了我的答案以包含更多信息。
猜你喜欢
  • 1970-01-01
  • 2015-05-20
  • 1970-01-01
  • 2012-06-02
  • 1970-01-01
  • 1970-01-01
  • 2014-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多