检测到碰撞后手柄移动
Handle movement after collision is detected
我正在用 js 制作一个小型平台游戏,但我遇到了碰撞问题。不幸的是,似乎 90% 的在线信息都在检测碰撞,而不是之后发生的事情。我可以很容易地检测到碰撞,因为我游戏中的所有东西都是轴对齐的二维矩形,但处理这些碰撞是困难的部分。
我试过在检测到碰撞时将玩家移动到最近的楼层,但当您与墙壁碰撞时也会发生这种情况。所以我尝试计算最近的脸并将玩家捕捉到那里,但这会导致各种奇怪的情况。这是我到目前为止的代码(当前代码现在仅适用于地板碰撞,但可以将相同的原则应用于其余方向)
if (collided) {
let ld = {'a': 'l', 'b': Math.abs(player.left.x - col.left.x)}
let rd = {'a': 'r', 'b': Math.abs(player.right.x - col.right.x)}
let td = {'a': 't', 'b': Math.abs(player.top.y - col.top.y)}
let bd = {'a': 'b', 'b': Math.abs(player.bottom.y - col.bottom.y)}
let dirs = [ld, rd, td, bd]
let nearestFace = dirs.hasMin('b').a
if (nearestFace == 'b') {
player.grounded = true
player.yvel = 0
player.pos.y = col.top.y + player.size.y/2
} else {
player.grounded = false
}
}
您的代码似乎不检查最近的面碰撞的位置,只是检查它是碰撞后最近的面。这可能会导致此类问题:
如果你的游戏是二维的,一切都是矩形,没有任何角度,那么你就会遇到一个称为 AABB(Axis-Aligned 边界框)碰撞的碰撞问题,这是一个很好的搜索关键字在你的情况下,这是一个了不起的起点。
首先,我建议您预测碰撞,而不是在碰撞发生后进行处理,因为存在如下潜在问题:
此外,如果您需要这方面的高级教程,上图来自本教程:Swept AABB Collision Detection and Response。它可能拥有您需要的一切。
简而言之,碰撞处理逻辑的输入应该是对象的旧位置、当前大小、速度和所有其他可碰撞对象(它们的大小和位置)。响应应该是您对象的预期行为(即新速度,针对 'avoiding' 碰撞进行了校正)。这样你就可以轻松地测试场景和你的实现,考虑一下:
const player = {x: 0, y: 0, width: 10, height: 10, xvel: 12, yvel: 0};
// player.right = {x: player.x + player.width, y: player.y} // Your engine does this, right?
const collidableList = [{x: 15, y: 0, width: 5, height: 10}];
const newVelocity = handleCollision(player, collidableList);
我想玩家的位置是在左上角对齐的,所以在这种情况下你可以预测你的新速度应该是 xvel = 5, yvel = 0
。这意味着您只需创建一个适用于此测试用例的 handleCollision
,然后您可以 运行 进行多次测试以确保碰撞在边缘情况下表现良好,例如没有碰撞时以及何时两个东西碰撞,一个比另一个更近。
此碰撞背后的主要思想是根据需要找到最接近零的速度,以避免在下一帧中发生碰撞。
例如,想象一个玩家向右移动的场景。我们先忽略 Y 轴,因为它应该只用于检测物体将来是否会发生碰撞,不会影响水平速度本身的计算。如果玩家向右移动,它必须检查每个物体的左侧到玩家右侧的距离,如果这个 space 小于速度,显然会引起碰撞,比如这个:
let targetVelocityX = player.xvel;
for (const collidable of ...) {
// ...
if (targetVelocityX > 0) {
// We are moving to the right so:
// Figure out how much space do we have between the objects
const leftOverSpace = collidable.left.x - player.right.x;
// Is our velocity larger than the space we have available?
if (targetVelocityX > leftOverSpace) {
// We must restrict the velocity because the player will collide otherwise
// We can only move as much as we have space available
targetVelocityX = leftOverSpace;
}
} else if (targetVelocityX < 0) {
// Moving to the left...
}
使用我之前做的测试用例,我们应该得到 leftOverSpace = 5
,它比我们的 targetVelocityX = 12
小,所以新的速度将为 5,这将使它接触到可碰撞的物体,在下一帧中,我们的玩家位置将是 x = 5
和 xvel = 5
,如果我们再次 运行,碰撞逻辑将告诉我们剩下的 space 为零,因此水平速度将设置为零,这意味着我们不能再向右移动,因为我们正在接触物体。
我应该提醒你,这与我上面链接的教程不同,该教程试图找到碰撞发生的时间作为浮点数,如果你想保持速度来偏转乒乓球,这很有用球,或沿着物体滑动,这是他的一些例子。
我正在用 js 制作一个小型平台游戏,但我遇到了碰撞问题。不幸的是,似乎 90% 的在线信息都在检测碰撞,而不是之后发生的事情。我可以很容易地检测到碰撞,因为我游戏中的所有东西都是轴对齐的二维矩形,但处理这些碰撞是困难的部分。
我试过在检测到碰撞时将玩家移动到最近的楼层,但当您与墙壁碰撞时也会发生这种情况。所以我尝试计算最近的脸并将玩家捕捉到那里,但这会导致各种奇怪的情况。这是我到目前为止的代码(当前代码现在仅适用于地板碰撞,但可以将相同的原则应用于其余方向)
if (collided) {
let ld = {'a': 'l', 'b': Math.abs(player.left.x - col.left.x)}
let rd = {'a': 'r', 'b': Math.abs(player.right.x - col.right.x)}
let td = {'a': 't', 'b': Math.abs(player.top.y - col.top.y)}
let bd = {'a': 'b', 'b': Math.abs(player.bottom.y - col.bottom.y)}
let dirs = [ld, rd, td, bd]
let nearestFace = dirs.hasMin('b').a
if (nearestFace == 'b') {
player.grounded = true
player.yvel = 0
player.pos.y = col.top.y + player.size.y/2
} else {
player.grounded = false
}
}
您的代码似乎不检查最近的面碰撞的位置,只是检查它是碰撞后最近的面。这可能会导致此类问题:
如果你的游戏是二维的,一切都是矩形,没有任何角度,那么你就会遇到一个称为 AABB(Axis-Aligned 边界框)碰撞的碰撞问题,这是一个很好的搜索关键字在你的情况下,这是一个了不起的起点。
首先,我建议您预测碰撞,而不是在碰撞发生后进行处理,因为存在如下潜在问题:
此外,如果您需要这方面的高级教程,上图来自本教程:Swept AABB Collision Detection and Response。它可能拥有您需要的一切。
简而言之,碰撞处理逻辑的输入应该是对象的旧位置、当前大小、速度和所有其他可碰撞对象(它们的大小和位置)。响应应该是您对象的预期行为(即新速度,针对 'avoiding' 碰撞进行了校正)。这样你就可以轻松地测试场景和你的实现,考虑一下:
const player = {x: 0, y: 0, width: 10, height: 10, xvel: 12, yvel: 0};
// player.right = {x: player.x + player.width, y: player.y} // Your engine does this, right?
const collidableList = [{x: 15, y: 0, width: 5, height: 10}];
const newVelocity = handleCollision(player, collidableList);
我想玩家的位置是在左上角对齐的,所以在这种情况下你可以预测你的新速度应该是 xvel = 5, yvel = 0
。这意味着您只需创建一个适用于此测试用例的 handleCollision
,然后您可以 运行 进行多次测试以确保碰撞在边缘情况下表现良好,例如没有碰撞时以及何时两个东西碰撞,一个比另一个更近。
此碰撞背后的主要思想是根据需要找到最接近零的速度,以避免在下一帧中发生碰撞。
例如,想象一个玩家向右移动的场景。我们先忽略 Y 轴,因为它应该只用于检测物体将来是否会发生碰撞,不会影响水平速度本身的计算。如果玩家向右移动,它必须检查每个物体的左侧到玩家右侧的距离,如果这个 space 小于速度,显然会引起碰撞,比如这个:
let targetVelocityX = player.xvel;
for (const collidable of ...) {
// ...
if (targetVelocityX > 0) {
// We are moving to the right so:
// Figure out how much space do we have between the objects
const leftOverSpace = collidable.left.x - player.right.x;
// Is our velocity larger than the space we have available?
if (targetVelocityX > leftOverSpace) {
// We must restrict the velocity because the player will collide otherwise
// We can only move as much as we have space available
targetVelocityX = leftOverSpace;
}
} else if (targetVelocityX < 0) {
// Moving to the left...
}
使用我之前做的测试用例,我们应该得到 leftOverSpace = 5
,它比我们的 targetVelocityX = 12
小,所以新的速度将为 5,这将使它接触到可碰撞的物体,在下一帧中,我们的玩家位置将是 x = 5
和 xvel = 5
,如果我们再次 运行,碰撞逻辑将告诉我们剩下的 space 为零,因此水平速度将设置为零,这意味着我们不能再向右移动,因为我们正在接触物体。
我应该提醒你,这与我上面链接的教程不同,该教程试图找到碰撞发生的时间作为浮点数,如果你想保持速度来偏转乒乓球,这很有用球,或沿着物体滑动,这是他的一些例子。