如何在2D平铺游戏中检测和解决碰撞? 我试图用C++的SFML 2.1来为我的2D超级马里奥兄弟克隆做碰撞。我尝试了很多不同的解决方案和想法,但我无法让任何东西正常工作。 我有一个播放器,它当前在自己的更新()中检查冲突(如下所示)

如何在2D平铺游戏中检测和解决碰撞? 我试图用C++的SFML 2.1来为我的2D超级马里奥兄弟克隆做碰撞。我尝试了很多不同的解决方案和想法,但我无法让任何东西正常工作。 我有一个播放器,它当前在自己的更新()中检查冲突(如下所示),c++,collision-detection,sfml,C++,Collision Detection,Sfml,当前: 1.我的碰撞检测到碰撞稍微偏移。如果您能发现任何明显的错误,我很乐意知道。 2.我不确定这是否是一个足够好的解决方案,因为我需要检测4个方向的碰撞(跳跃、头部撞到障碍物上、摔倒在障碍物上以及左/右碰撞) 3.当我检测到碰撞时,我不知道如何纠正玩家的正确方向 我已经清理了所有不必要的代码,并删除了下面不起作用的代码 void Player::Update(Camera& camera, Level& level, sf::RenderWindow& window)

当前:
1.我的碰撞检测到碰撞稍微偏移。如果您能发现任何明显的错误,我很乐意知道。
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;
}
如果我遗漏了任何有用的信息,请在下面留下评论

地图由不同的分片组成,分片保存的坐标不是它们实际所在的位置,而是它们在阵列中的位置,因此示例地图如下所示:
0,0,10,2
1,01,11,2
2,02,12,2
碰撞功能然后检查与播放器周围的那些瓷砖相关的碰撞

我的尝试:
有很多不同的碰撞方法可以同时解决x和y碰撞。
碰撞响应将玩家移回最后位置。
碰撞响应,按相交矩形的大小将播放器向后移动(这在y轴有效,但在x轴无效)。
当我的头感觉不到融化的奶酪时,我会写更多的东西。

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

只要正确计算数量,您使用的方法(将播放器向后移动相交矩形的大小)应该是有效的。这一点很好(在这篇文章中,具体参见第7节“寻找MTV”)

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

另一种方法是连续碰撞检测。这在本质上有点复杂。您只需四处搜索“连续碰撞”,就可以找到有关它的一些信息,但基本思想是:

  • 找出下一次碰撞发生的时间和地点
  • 如果下一次碰撞将在当前时间步内发生,请将对象移动到发生碰撞的位置
  • 如果下一次碰撞没有在当前时间步长内发生,只需将对象向前移动其当前移动速度即可
  • 对于我的游戏(在java和Libgdx中),我也使用了AABB碰撞检测。有两种不同的方法可以检测和处理碰撞:

    • 移动前计算新位置。如果有物体,不要移动。要在x方向进行滑动运动检查碰撞,如果发生碰撞,请不要移动。如果没有碰撞,请检查y的碰撞。如果发生碰撞,不要在y方向移动。仍然没有对x和y(您真正的新位置)上的碰撞进行碰撞检查。如果发生碰撞,停止两个移动
    • 移动到新位置。如果在那里碰撞,在x方向重置x移动,如果在y方向碰撞重置y移动,在两个方向碰撞,重置两者
    由于您使用的是平铺贴图,因此不需要使用“交叉点”测试。
    相反,您可以存储角色的4个角点,四舍五入到角色所在的
    磁贴上(如果每个
    磁贴都有一个单位大,并且角色的脚在P(0.5;0.5)上,您需要检查
    磁贴
    (0;0))。
    玩家的位置
    是他的左下角(正常情况下),因此他的其他3点是
    P2(玩家位置x+玩家宽度;玩家位置y)
    P2(玩家位置x+玩家宽度;玩家位置y+玩家高度)
    P3(玩家位置x;玩家位置y+玩家高度)
    ,始终向下舍入到
    平铺
    s位置


    希望能有所帮助。

    我没有阅读所有内容,尤其是代码(c++不是我最喜欢的语言^^^),但我有几个问题:要检测碰撞的字符是由什么形状组成的?他们能高速移动吗(一次更新超过一个瓷砖)?我使用的是矩形(我相信是AABB),他们的移动速度不够快,无法解决“子弹穿过纸张”的问题。我将尝试实现其中的每一个。我可能会学到一些东西。但在第二种“低效”的方法中,我怎么知道他是从哪个方向被击中的?x、 -x,y,-y?另外,第三个备选方案听起来很好学,但对这个游戏来说可能太高级了。当我取得进展时,我会再次联系。谢谢:Dre:第二种方法,只需沿角色移动方向的相反方向移动即可。如果他移动+x,将他向后移动-x。如果他移动+y,移动他-y。如果他在移动-x和+y,那么移动他+x和-y。
    void Player::CollisionResponse(void)
    {
    
    }