【问题标题】:Collision detection in a game loop?游戏循环中的碰撞检测?
【发布时间】:2026-01-30 11:45:01
【问题描述】:

我正在开发一个简单的 2D 游戏(鸟瞰图)。我有弹丸,它们是圆形的。游戏关卡周围有障碍物,都是矩形(轴对齐)。我希望射弹在碰撞时从障碍物上反弹。我不确定如何在我的游戏循环中实现这一点。估计是这样的:

void gameLoop() {

    Projectile p = ..;

    p.updateLocation(p.getVelocity(), p.getDirection());

    Barrier intersected = p.intersects(barriers);
    if (intersected != null) {
        // We hit a barrier after moving on this time tick.
        // Figure out where our reflected location off the 
        // barrier should be now.
        Point ptNew = intersected.reflect(p.getLastPoint(), p.getVelocity(),
            p.getDirection());

        // Our new location after getting bounced off the barrier.
        ptNew.setLocation(ptNew);
    }
}

所以在我们移动弹丸之后,我们可以检查我们是否与其中一个障碍相交(或完全在其中)。如果是这样,根据我们的起点和速度/方向进行计算,以确定我们的反射位置应该离开障碍物的位置。

谢谢

------------ 更新------------

更具体一点 - 考虑到 Erik 的 cmets,我确实需要确保弹丸正确反弹,如果它们的速度恰好在单个游戏循环中穿过障碍物,我无法让它们穿过障碍物迭代。

在这种情况下,我想我需要针对所有障碍(可能重复)测试起点、速度、方向,直到不再有速度的交叉点。比如:

void gameLoop() {
    Projectile p = ...;

    Barrier barrier = moveProjectile(p, barriers);
    while (barrier != null && p.stillHasVelocityThisFrame()) {
        barrier = moveProjectile(p, barriers);
    }

    // at this point, the projectile is done moving / bouncing around.
}

void moveProjectile(Projectile projectile,
                    List<Barrier> barriers)
{
    for (Barrier barrier : barriers) {
        // tbd
        Barrier intersected = test2dVectorAgainstRectangle(
            projectile.getPosition(),
            projectile.get2dVector());
        // the above would test the projectile's vector
        // to see if it intersects any barrier, reflect
        // off of it if necessary, modify the projectile's
        // position, and reduce the available distance it
        // can still travel for this frame.
        if (intersected) {
            return intersected;
        }
    }

    // no intersections.
    return null;
}

是的,这已经有点棘手了。

谢谢

【问题讨论】:

  • 您需要帮助的哪一部分?碰撞本身还是反射?或者两者兼而有之?
  • 好吧,我也不知道该怎么做(代码示例或链接会很棒),但更多的是寻找一般的是/否,如果这是这样做的。
  • 这是一个非常开放的问题

标签: collision-detection


【解决方案1】:

您想使用矢量数学进行反射和碰撞检查。检测您是否在 1 个 tic 上的一条线的一侧(世界矩形的边缘)以及接下来在该线的另一侧是非常简单的。同样的检查也会给你碰撞点,只需一点点数学。然后你有一个点可以进行反射,再次使用向量。绘制与其他对象的碰撞有点棘手,但仍然可以完成。列出一个循环中的所有碰撞,并尝试找到沿着它们的移动路径发生的第一个碰撞 - 再次重用一些点积魔法。或者,如果您检测到多个碰撞,您始终可以将每个对象备份到它之前的 tic 位置并从那里应用碰撞。

你永远不想做的一件事是让一个对象留在另一个对象内。这可能会导致它们相互卡住并且行为非常不稳定 - 取决于您如何处理碰撞代码。

【讨论】:

    【解决方案2】:

    碰撞检测很难做到正确。这真的取决于你想要的东西有多“完美”。

    有几点需要考虑:
    1)如果 p 移动得如此之快以至于在一次更新中直接穿过你的屏障而没有相交会发生什么?您确实需要检查 p 的路径是否与障碍相交。

    2) 如果 p 在更新滴答声中途与障碍物相交,但在更新滴答声完成之前以新的速度与另一个障碍物(或另一个 p)相交,会发生什么?您必须在单个更新滴答中多次更新 p 的速度。

    或者你可以忽略这些情况并希望事情能解决/不要经常发生;)

    【讨论】:

    • 是的,完美,这些都是我需要警告的问题!
    【解决方案3】:

    对于碰撞... 您正在寻找的是圆心与矩形上任意点之间的距离。如果任何给定点之间的距离小于圆的半径,那么就会发生碰撞。您可以使用勾股定理。

    If (sqrt((circle.x - rect.x)² + (circle.y - rect.y)²) <= circle.radius)
        circle.reflect();
    

    至于反射... 有一点很好知道是入射角等于反射角,但为负。因此,以 45 度角撞击表面的弹丸将以 -45 度弹跳。这将要求您知道弹丸移动的方向(角度)以及表面的角度。如果你总结这些,你会得到你的入射角。只需将其乘以 -1,然后通过一些触发重新计算弹丸的方向。

    circle.velocity.x = cos(angleInc); circle.velocity.y = sin(angleInc);

    【讨论】:

    • @Michael Dorgan 好点,同意。我假设您只是指反射本身而不是碰撞检测?
    【解决方案4】:

    如果障碍物的位置是固定的,您可以使用它的速度计算下一个游戏循环的球的位置,如果它的位置在下一个循环中超出障碍物,您可以设置一个布尔值来设置球在下一个循环开始时紧挨其中一个障碍物,并在检查它们是否有效碰撞后进行相应的计算。

    我不敢打赌这是最有效的方法,但如果它是 2d 游戏,它根本不会妨碍你的游戏。

    此外,请始终尝试对球的最大速度设置限制,如果它们在每次通过游戏循环时变得越来越快,那么您就有麻烦了。

    【讨论】: