【问题标题】:Tilemap collision resolution between two tiles两个瓦片之间的瓦片图碰撞分辨率
【发布时间】:2014-07-21 01:55:13
【问题描述】:

我的 tilemap 冲突解决有问题。我有一个球撞到瓷砖上。碰撞工作正常,除非碰撞发生在两个瓷砖之间。然后我的碰撞解决方案出现故障,球飞向错误的方向。我的球是长方形的,瓷砖是长方形的。

这是一个演示问题的 .gif:

球应该一次一个地从瓷砖上反射出来。

算法是这样工作的:

  1. 应用移动,然后检查并解决冲突。
  2. 找出球重叠的瓷砖。
  3. 如果正在检查的图块无法通过:
    • 找出球与瓷砖的 X 轴和 Y 轴重叠的程度。
    • 仅在浅轴上将球移出瓷砖来解决碰撞问题。 (哪个轴渗透最少)。
    • 转到下一个图块。
  4. 如果正在检查的图块可以通过,则什么也不做。

我已经读到这种最小位移技术的问题在于,在解决碰撞后,球可能会被移动到另一个碰撞中。修复应该是,第二次运行算法。我试过这个,但没有帮助。这是我的 C++ 碰撞处理代码:

void PlayState::handleCollisions() {

// Get ball bounding box.
sf::FloatRect ballBounds = ball.sprite.getGlobalBounds();

// Find out the nearby tiles.
int leftTile = (int)floor((float)ballBounds.left / level1.TILE_WIDTH);
int rightTile = (int)ceil(((float)(ballBounds.left + ballBounds.width) / level1.TILE_WIDTH)) - 1;
int topTile = (int)floor((float)ballBounds.top / level1.TILE_HEIGHT);
int bottomTile = (int)ceil(((float)(ballBounds.top + ballBounds.height) / level1.TILE_HEIGHT)) - 1;

// For each potentially colliding tile,
for(int y = topTile; y <= bottomTile; ++y) {
    for(int x = leftTile; x <= rightTile; ++x) {

        // If this tile is collidable,
        TileCollision collision = getCollision(x, y);
        if(collision == Impassable) {

            // Determine collision depth (with direction) and magnitude.
            sf::FloatRect tileBounds = getTileBounds(x, y);
            sf::Vector2f depth = Collision::getIntersectionDepth(ballBounds, tileBounds);

            if(depth != sf::Vector2f(0, 0)) {
                float absDepthX = std::abs(depth.x);
                float absDepthY = std::abs(depth.y);

                // Resolve the collision along the shallow axis.
                if(absDepthY < absDepthX) {

                    // Resolve the collision along the Y axis.
                    ball.sprite.setPosition(ball.sprite.getPosition().x, ball.sprite.getPosition().y + depth.y);

                    // Perform further collisions with the new bounds.
                    sf::FloatRect ballBounds = ball.sprite.getGlobalBounds();

                    // Y-distance to the tile center.
                    if(distanceY < 0) {
                        std::cout << "Collided from TOP." << std::endl;
                        ball.velocity.y = -ball.velocity.y;
                    }
                    else {
                        std::cout << "Collided from BOTTOM." << std::endl;
                        ball.velocity.y = -ball.velocity.y;
                    }
                }
                else {
                    // Resolve the collision along the X axis.
                    ball.sprite.setPosition(ball.sprite.getPosition().x + depth.x, ball.sprite.getPosition().y);

                    // Perform further collisions with the new bounds.
                    sf::FloatRect ballBounds = ball.sprite.getGlobalBounds();

                    // X-distance to the tile center.
                    if(distanceX < 0) {
                        std::cout << "Collided from LEFT." << std::endl;
                        ball.velocity.x = -ball.velocity.x;
                    }
                    else {
                        std::cout << "Collided from RIGHT." << std::endl;
                        ball.velocity.x = -ball.velocity.x;
                    }
                }
            }
        }
    }
}
}  

我尝试第二次运行该算法,如下所示:

void PlayState::update(sf::Time deltaTime) {

    ball.update(deltaTime);

    handleCollisions();
    handleCollisions();
}

我能做些什么来解决这个问题?

【问题讨论】:

    标签: c++ algorithm collision-detection


    【解决方案1】:

    我从头开始在 Flash 游戏中实现了类似的碰撞检测。我发现将球建模为单点并将其路径建模为矢量更容易、更准确。为了获得所需的球大小——在盒子的边缘添加了半径 r 填充。

    问题变成检测球的线段 (ptFrom..ptTo) 和附近块的交点。如果有多个交叉点,请使用最近的一个 ptFrom。使用线段的反射剩余部分重复碰撞检测,直到不再有碰撞。

    【讨论】:

    • 您好,请问您的碰撞检测方法的代码还有吗,让我看看?
    • 当我在家用电脑上时,我可以为您发布重要信息。其中两个重要功能是检测球矢量与垂直或水平砖边缘之间的交点。如果您想创建一个通用函数来处理所有情况,您可以在游戏中使用有角度的积木。
    【解决方案2】:

    进行碰撞检测的唯一正确方法是在计算中加入速度矢量。

    您不仅应该测试离散位置,还应该测试它们之间的整条线。至少您将能够检测到所有碰撞。例如。当您仅测试当前位置(标记为 1 和 2)时,无法检测到下图中的一个

    " " "|2
     " " /<- second 'collision' point
    " " /|
    ___/_|
      /^--collision point
     1
    

    您至少应该知道当前坐标和上一个坐标(即当前减去速度乘以 delta t)。所以你必须计算可能的碰撞点的坐标,找到哪个更接近开始,然后重新计算碰撞点后的路径。

    添加:如何查找和计算碰撞点。

    首先,您必须找到您的球穿过哪些边缘。对于矩形网格,这并不太难:如果前一个坐标和当前坐标在不同的行(列)中,则行(列)边缘被交叉。

      |     |      |     |      |   1 |     |     | 1
    --+-----+--  --+-----+--  --+-----+-- --+-----+--
      |   1 |      |     | 1    |     |     |     |
      | 0   |      |  0  |      |  0  |     | 0   |
    --+-----+--  --+-----+--  --+-----+-- --+-----+--
      |     |      |     |      |     |     |     |
    none crossed    column        row         both
    

    然后你应该根据碰撞边缘方程(x = xc - 交叉垂直边缘坐标)求解球轨迹方程(x0,y0 - 前一点,vx,vy - 速度):

    x = x0 + t⋅vx    y = y0 + t⋅vy   x = xc
    

    和带有方程x = xc的垂直墙,找到yc = y(y坐标)和tc = t(时间)的碰撞,你需要解方程

    t = (xc - x0)/vx   y = y0 +t⋅vy (time and y coord of collision)
    

    对于水平边缘,同样适用于 x 和 y 交换。

    当 x 和 y 边都交叉时,选择较早交叉的一条(t 较小)

               |  1
               | /
               |/                   Here, point a is a row collision
              b*<---                            b is a column collision
              /|                     t[a] should be less than t[b]
            a/ |                       because is closer to trajectory start
       -----*--+-------- xc[row]
           /   |
          0    |
               |
              yc[col]
    

    当然,当您计算一次碰撞并更新坐标和速度时,您应该 重新计算碰撞,因为它们可能更多:

     ----*--+
        / \ |
       0   \|
            *
           /|
          1
    

    【讨论】:

    • 如何计算可能的碰撞点的坐标?
    猜你喜欢
    • 1970-01-01
    • 2015-03-03
    • 1970-01-01
    • 2022-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多