如何在基于2D平铺的游戏中检测并解决碰撞?

时间:2014-04-09 18:25:13

标签: c++ collision-detection sfml

我正在尝试使用SFML 2.1在c ++中制作的2D超级马里奥兄弟克隆包装碰撞。我尝试了许多不同的解决方案和想法,但我无法正常工作 我有一个目前正在检查碰撞的玩家自己的Update()(如下所示)

目前:
我的碰撞检测到碰撞略微偏移。如果你能找到任何明显的错误我会很高兴知道 2.我不确定这是否是一个足够好的解决方案,因为我需要检测4个方向的碰撞(跳跃,撞到我的头上的块。坠落到块上,左/右碰撞)
3.当我发现碰撞时,我不知道如何在正确的方向上纠正玩家。

我已经清理了所有不必要的代码并删除了以下不起作用的代码。

void Player::Update(Camera& camera, Level& level, sf::RenderWindow& window)
{
    input.Update(*this); // Read input logic
    physics.update(*this, level, camera, window); // Update physics (movement, gravity)
    drawing.update(*this); // Update animation ( does NOT draw player )

    if(Collision(level, camera, getPosition())) // Check for collision
    {
        CollisionResponse(); // If we had collision, do something about it
    }
}

Collision()函数有三个参数:
- 级别:是地图 - 摄像头:在屏幕上返回从地图显示的图块 - getPosition():只是玩家在窗口中的位置

该功能如下所示:

bool Player::Collision(Level& level, const Camera& camera, const sf::Vector2f& position)
{
    // Rectangle surrounding player
    sf::FloatRect playerRectangle = sprite.getGlobalBounds();

    int tileSize = 32;
    Tile* tile;

    // smallBounds is the area on the screen where we check collision.
    // We dont check the whole screen, only tiles that CAN collide with player.
    sf::IntRect smallBounds = sf::IntRect(
        (position.x / tileSize) + camera.GetTileBounds(tileSize).left,
        position.y / tileSize,
        ((position.x + tileSize) / tileSize) + camera.GetTileBounds(tileSize).left,
        (position.y + tileSize) / tileSize);

    // Loop through all tiles
    for (int y = smallBounds.top; y < smallBounds.height; y++)
    {
        for (int x = smallBounds.left; x < smallBounds.width; x++)
        {
            // Get the tile we are drawing
            tile = level.GetTile(x, y);
                if(tile && !tile->GetWalkable())
            {
                sf::FloatRect tileRectangle(tile->GetSprite().getPosition().x,
                                            tile->GetSprite().getPosition().y,
                                            tileSize,
                                            tileSize);
                tileRect = tileRectangle;
                if(Intersect(playerRectangle, tileRectangle))
                {
                                    // Woah, collision detected!
                    return true;
                }
            }
        }
    }
    return false;
}

和CollisionResponse函数:

void Player::CollisionResponse(void)
{

}

如果我错过任何有用的信息,请在下面发表评论。

地图由坐标保存的不同瓷砖组成,而不是它们实际定位的位置,但它们在阵列中的位置,因此样本地图如下所示:
0,0 0,1 0,2
1,0 1,1 1,2
2,0 2,1 2,2
然后,碰撞功能检查与玩家周围的平铺瓦片相关的碰撞。

我尝试了什么:
许多不同的碰撞方法可以同时解决x和y碰撞问题 碰撞响应将玩家移回最后位置。
碰撞响应将玩家移回到交叉矩形的大小(这在y轴上工作,但在x轴上不起作用)。 当我的头脑感觉不像融化的奶酪时,我会写得更多。

2 个答案:

答案 0 :(得分:2)

将玩家移回最后一个已知的好位置将导致您遇到的行为;玩家将与他碰撞时保持一定距离。举一个例子:如果你的角色以300像素/秒的速度移动并且距离墙壁5个像素,则下一次碰撞发生在1/60秒。如果您的帧速率为60FPS,则在下一帧中将检测到碰撞,并且角色将移回到碰撞发生前他所在墙壁的5个像素。当然不受欢迎。

只要您正确计算数量,您所使用的方法就可以工作(将播放器移回相交矩形的大小)。 Separating Axis theorem对此有好处(在那篇文章中,具体见第7节“寻找MTV”)。

您可以尝试的另一种方法,不涉及计算:在碰撞时,将玩家向后移动一小段时间(甚至可能是逐像素)直到他停止与墙壁碰撞。然后,你会知道一个靠近墙壁的地方,放置他是安全的。效率较低,但更容易实施。

另一种方法是连续碰撞检测。它本质上更复杂一些。你应该能够通过搜索“连续碰撞”来找到它的一些信息,但基本的想法是:

  1. 弄清楚下一次碰撞发生的时间和地点。
  2. 如果在当前时间步长内发生下一次碰撞,请将对象移动到发生碰撞的位置。
  3. 如果在当前时间步长内未发生下一次碰撞,只需将对象向前移动其当前移动速度。

答案 1 :(得分:1)

对于我的游戏(在带有Libgdx的java中)我也使用了AABB碰撞检测。您可以通过两种不同的方式来检测和处理碰撞:

  • 在移动前计算新位置。如果有物体不动。要进行滑行运动检查x方向的碰撞,如果发生碰撞,请不要移动。如果没有碰撞,请检查y是否相同。如果发生碰撞,请不要移动。仍然没有碰撞检查x和y上的碰撞(你真正的新位置)。如果发生碰撞则停止两次动作。
  • 搬到新职位。如果你在那里碰撞,在x方向重置x-运动,如果你在y方向上碰撞重置y-运动,在两个方向上碰撞,重置两者。

当您使用TileBased地图时,您不需要使用&#34;十字路口&#34;试验。
相反,你可以存储你角色的4个角点,四舍五入到他所在的Tile(如果每个Tile是一个单位大而字符英尺在P(0.5; 0.5),你需要检查Tile(0; 0)) Player的位置是他的左下角(通常),所以他的其他3个点是P2(player.position.x.+player.width;player.position.y)P2(player.position.x.+player.width;player.position.y+player.height)P3(player.position.x;player.position.y+player.height),总是向下舍入到{Tile 1}}的位置。

希望它有所帮助。