微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C++/SDL2 - 球一起弹跳/故障

如何解决C++/SDL2 - 球一起弹跳/故障

我试图使用 SDL2 用 C++ 编写一些弹球程序。我很难让速度交换正确,但到目前为止它工作得非常好。我现在唯一的问题是球有时会出现故障/粘在一起,几秒钟后它们又会自己松开。

Two Balls stucking together

这是我的 update() 函数,每帧都会调用

void Game::update() {
    updateFPS();
    checkBallCollision();
    updateCanCollide();


    int newtime = SDL_GetTicks();
    int diff = newtime - lasttime;

    if (diff > 10)
        diff = 10;

    for (Ball *ball : balls) {
        ball->x = ball->x + ball->veLocity->x * (float) diff / 100;
        ball->y = ball->y + ball->veLocity->y * (float) diff / 100;

        checkBorderCollision(ball);
    }

    lasttime = newtime;
}

我猜球会接近并且不会在球的边界反弹。因此,我试图给每个球一个布尔值 canCollide,除非球发生碰撞,否则它始终为真。然后它一直是假的,直到两个球不再重叠。

这是我的 checkBallCollision()updateCanCollide() 函数:`

void Game::updateCanCollide() {
    Ball **ballArr = &balls[0];
    int length = balls.size();

    for (int i = 0; i < length; i++) {
        if (ballArr[i]->canCollide)
            continue;

        bool updatedCollide = true;

        for (int k = i + 1; k < length; k++) {
            Ball *ball1 = ballArr[i];
            Ball *ball2 = ballArr[k];

            int xdiff = abs(ball1->x - ball2->x);
            int ydiff = abs(ball1->y - ball2->y);

            float distance = sqrt(xdiff * xdiff + ydiff * ydiff);

            if (distance <= ball1->radius + ball2->radius) {
                updatedCollide = false;
            }
        }

        ballArr[i]->canCollide = updatedCollide;
    }
}

// do all collision checks and update the veLocity
void Game::checkBallCollision() {
    Ball **ballArr = &balls[0];
    int length = balls.size();

    for (int i = 0; i < length; i++) {
        if (!ballArr[i]->canCollide)
            continue;

        for (int k = i + 1; k < length; k++) {
            if (!ballArr[k]->canCollide)
                continue;

            Ball *ball1 = ballArr[i];
            Ball *ball2 = ballArr[k];

            int xdiff = abs(ball1->x - ball2->x);
            int ydiff = abs(ball1->y - ball2->y);

            float distance = sqrt(xdiff * xdiff + ydiff * ydiff);

            if (distance <= ball1->radius + ball2->radius) {
                // ball1 and ball2 are colliding
                // update the veLocity of both balls

                float m1 = ball1->radius * ball1->radius * 3.14159;
                float m2 = ball2->radius * ball2->radius * 3.14159;

                Vector2D *v1 = new Vector2D(ball1->veLocity->x,ball1->veLocity->x);
                Vector2D *v2 = new Vector2D(ball2->veLocity->x,ball2->veLocity->x);

                ball1->veLocity->x = ((v1->x * (m1 - m2) + 2 * m2 * v2->x) / (m1 + m2));
                ball1->veLocity->y = ((v1->y * (m1 - m2) + 2 * m2 * v2->y) / (m1 + m2));

                ball2->veLocity->x = ((v2->x * (m2 - m1) + 2 * m1 * v1->x) / (m1 + m2));
                ball2->veLocity->y = ((v2->y * (m2 - m1) + 2 * m1 * v1->y) / (m1 + m2));

                ball1->canCollide = false;
                ball2->canCollide = false;
            }
        }
    }
}

解决方法

正确的修复

主要问题是你让球相互重叠,然后更新它们的速度。但是,如果下一个时间步长比前一个时间步长,则可能是更新了它们的位置后,它们仍然重叠。然后您认为它们再次碰撞,并更新它们的速度,但这很可能会导致它们再次靠得更近。这就解释了为什么他们会卡住。

解决这个问题的正确等待是计算两个移动的球碰撞的确切时间点。这可以通过分析来完成,例如通过将时间视为第三维,然后计算 line-sphere intersection。如果在时间步中发生这种情况,则将时间提前到发生碰撞的点,然后更新速度,然后执行该步的其余部分。如果您有两个以上的球,那么请注意,您可以在同一时间步长内让两个以上的球相互碰撞。这也是可以解决的,只需计算所有发生碰撞的时间点,选择最早的一个,更新该点的速度,然后重新计算碰撞次数,依此类推,直到时间步内没有碰撞为止。

解决方法

您的解决方法可能会修复两个相互粘连的球,但结果在物理上并不准确。当您开始增加球的密度时,它会崩溃,因为在某些时候,一对应该碰撞的球中至少有一个在前一个时间步发生碰撞的可能性非常高,然后它们都将开始通过一直通过对方。

另一个问题是您必须检查 *** Caught unhandled unknown exception; terminating 中所有可能的球对,这效率不高。这个问题有一个更简单和更常见的解决方法:当两个球碰撞时,在更新它们的速度后,立即更新它们的位置,这样球不再碰撞。您可以尝试准确计算移动它们的大小,使它们不再重叠,或者如果您不想涉及数学,您可以使用 while 循环执行一小步,直到它们不再重叠。

代码中的其他问题

请注意,您的代码中还有其他一些可以改进的地方:

  • 不要updateCanCollide()临时new,只需在堆栈中声明它即可。如果由于某种原因这是不可能的,至少 Vector2D deletev1 之后。
  • 如果您要对结果求平方,则无需调用 v2
  • 使用 std::hypot() 计算距离。
  • abs() 是您自己编写的还是来自图书馆?如果是后者,也许它已经具有反映两个二维向量的功能?如果是前者,请考虑使用 GLM 之类的库,即使您没有使用 OpenGL。
  • 使用适当的 π 值。一个简单、可移植的解决方案是声明 static constexpr pi = std::atan(1) * 4

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。