如何对二维平台中的碰撞检测做出反应

时间:2014-12-28 15:34:25

标签: collision-detection game-engine

我写了一个html5游戏框架并尝试创建一个2d平台游戏。但我坚持游戏的碰撞检测部分,我的框架包含一个矩形对象与方法“相交”。所以你可以检查矩形是否与另一个矩形相交,它工作正常,但我不知道如何对这个方法作出反应。我试图将玩家位置设置为最后一个“非碰撞”位置,但问题是玩家速度偏移,每次与玩家速度有差距时......

我自己也想不到了。

1 个答案:

答案 0 :(得分:2)

我认为这是一个游戏机制问题,不仅适用于HTML5 / JS游戏。 我不熟悉HTML5语法,因此您可能需要重写我的代码示例的一些细节。另外,我不知道你的代码是如何构建的;我假设播放器和块都有一些功能,如getX()getXSpeed()setX()等等。

当您检测到玩家(或任何其他可以移动的物体)与环境阻挡之间的碰撞时,您会想要更新玩家的位置,以便它只是接触该块而不会拦截它。在您执行此操作之前,您需要找出 与块碰撞的方式:玩家是从上方,从左侧,右侧还是从底部进入区块?

首先,我们需要玩家的位置。

playerX = player.getX();
playerY = player.getY();

根据玩家移动的方向,您需要添加玩家的宽度和高度,以获得指向玩家移动方向的角落坐标。

if(player.getXSpeed() > 0) {
    playerX += player.getWidth();
}
if(player.getYSpeed() > 0) {
    playerY += player.getHeight();
}

接下来,您希望获得最接近玩家之前位置的区块边。您可以检查玩家的速度是正面还是负面,以确定他来自哪一方。它们一起代表角落的坐标,指向玩家来的方向。

blockX = player.getXSpeed() > 0? block.getX() : block.getX() + block.getWidth();
blockY = player.getYSpeed() > 0? block.getY() : block.getY() + block.getHeight();

现在我们有垂直边的Y坐标和玩家通过的块的水平边的X坐标,总的来说我们有两边角的坐标。

我创建了一个小图表,以防你不明白为什么我们这样做:

Collision Diagram

黑框表示环境块。橙色框表示玩家的先前和当前位置。蓝线代表玩家移动的路径。我们现在需要检查代表块角的蓝点是否高于或低于该行。这告诉我们玩家是否撞到了街区的水平面或垂直面。

如果玩家击中垂直方向,则蓝线的倾斜度将小于从玩家角落到方块角落的倾斜度。在数学方面,这意味着:

(currentPlayerY - previousPlayerY) / (currentPlayerX - previousPlayerX)

小于

(currentPlayerY - blockY) / (currentPlayerX - blockX)

如果这不适用,则播放器会击中该块的水平侧。 您的代码看起来有点像这样:

bool verticalSide = abs(player.getYSpeed() / player.getXSpeed()) < abs((playerY / blockY) / (playerX - blockX));

由于我们可以忽视引力,我们可以假装玩家的动作是线性的。我们在这条线上只需要一个点来判断玩家来自哪个方向。这意味着(currentPlayerY - previousPlayerY) / (currentPlayerX - previousPlayerX)总是等于player.getYSpeed() / player.getXSpeed(),无论玩家在上一个游戏周期中有多远。 请注意,玩家不一定来自左上角,因此我们计算的倾向可能是负数。我们使用数学课程#39;编程语言通常提供的abs()函数来获取绝对值,即将所有负值都转为正值。

现在我们知道我们是在处理水平碰撞还是垂直碰撞,我们可以相应地更新玩家的位置:

if(verticalSide) {
    player.setX(player.getXSpeed() > 0? blockX - player.getWidth(): blockX);
    player.setXSpeed(0);
} else {
    player.setY(player.getYSpeed() > 0? blockY - player.getHeight(): blockY);
    player.setYSpeed(0);
}

如果verticalSide为真,则播放器撞到块的一侧。现在我们检查玩家的x速度是否为正,以确定他是从左边还是从右边来,并更新他的位置,这样他就可以接触到该块。我们还将其水平速度设置为0.但是,如果verticalSide不成立,则玩家会撞到块的顶部或底部,因此我们对y轴执行相同的操作。这应该是你需要的一切。

但是我还有一个额外的提示:你很可能需要信息,无论玩家当前是否正在触地,以确定他是否可以在按下空格键或任何键时跳转。要做到这一点,你应该在你的玩家类中创建一个布尔字段grounded,每次移动玩家时都设置为false。在碰撞检查期间,如果玩家正在击中一个块的顶部,则再次将其设置为true,即verticalSidefalseplayer.getYSpeed() > 0true。如果按下跳转键,请在触发跳转之前检查grounded是否为true

有趣的是,我昨天遇到了完全相同的问题,我想出了这个解决方案,但是只将它压缩成几行代码,所以我不得不倒退来记住我在这里做了什么。猜猜我将来会评论我的代码。