【问题标题】:Simple platformer collision algorithm is glitching简单的平台游戏碰撞算法出现故障
【发布时间】:2015-10-09 22:11:21
【问题描述】:

我在使用这个简单的碰撞算法时遇到了问题。我在做rpg game tutorial from codingmadeeasy,碰撞很像我在以前的平台风格游戏中做过的任何算法碰撞。以前的游戏我也遇到过这个问题,只是不记得修复了。我有一张地图,它有很多瓷砖,有些被标记为实心。他们的 Update 方法检查与玩家的碰撞。 如果瓷砖是实心的,它应该停止玩家并将他移动到一个有效的位置。

问题是当我向上或向下时,粘在瓷砖上,然后按向左或向右移动键。
例如:

我正在按下向下键,我在实心图块的顶部,然后按下向左。在那一刻,玩家被重新定位到瓷砖的右侧。

由于碰撞方法中的if顺序,如果我向左或向右并向上或向下按就不会发生这种情况。

public void Update(GameTime gameTime, ref Player player)
{
    if (State != TileState.Solid) return;
    Rectangle tileRect = 
        new Rectangle((int)Position.X, (int)Position.Y,
            SourceRect.Width, SourceRect.Height);
    Rectangle playerRect = 
        new Rectangle((int)player.Sprite.position.X, (int)player.Sprite.position.Y,
            player.Sprite.SourceRect.Width, player.Sprite.SourceRect.Height);

    HandleCollisionWithTile(player, playerRect, tileRect);
}

/// <summary>
/// Repositions the player at the current position after collision with tile
/// </summary>
/// <param name="player">the player intersecting with the tile</param>
/// <param name="playerRect">the rectangle of the player</param>
/// <param name="tileRect">the rectangle of the tile</param>
private static void HandleCollisionWithTile(Player player, Rectangle playerRect, Rectangle tileRect)
{
    // Make sure there's even collosion
    if (!playerRect.Intersects(tileRect)) return;

    // Left
    if (player.Velocity.X < 0)
        player.Sprite.position.X = tileRect.Right;

    // Right
    else if (player.Velocity.X > 0)
        player.Sprite.position.X = tileRect.Left - player.Sprite.SourceRect.Width;

    // Up
    else if (player.Velocity.Y < 0)
        player.Sprite.position.Y = tileRect.Bottom;

    // Down
    else if (player.Velocity.Y > 0)
        player.Sprite.position.Y = tileRect.Top - player.Sprite.SourceRect.Height;

    // Reset velocity
    player.Velocity = Vector2.Zero;
}

显然,如果我将前两个 if 和最后一个 if 互换,如果我从侧面过来按向上或向下,就会发生这种情况。

我到底错过了什么?

【问题讨论】:

  • 看我对答案的评论。这个算法有底线问题

标签: c# collision-detection monogame


【解决方案1】:

正如您所指出的,发生此问题的原因是您检查碰撞条件的顺序。特别是,虽然您的玩家精灵在视觉上似乎位于图块上方,但您的代码检测精灵为相交,并且由于第一次检查是针对水平碰撞,玩家精灵的位置移动到图块精灵的右侧(上方和实际上,在右侧,因此您只需调整检测到的碰撞轴上的坐标)。

在我看来,这暴露了代码中的一个根本缺陷:虽然有人可能会争辩说已经解决了发生在垂直方向上的碰撞,但通过将玩家精灵放置在瓷砖精灵的顶部,现在玩家精灵不应与瓷砖精灵处于碰撞状态,而是您的代码将此情况检测为碰撞。

如果没有更好的代码示例,很难知道最好的解决方法是什么。但是一个明显的快速而肮脏的解决方案是确保当您解决碰撞时,您不会将玩家精灵置于与瓷砖精灵相交的位置。例如:

private static void HandleCollisionWithTile(Player player, Rectangle playerRect, Rectangle tileRect)
{
    // Make sure there's even collosion
    if (!playerRect.Intersects(tileRect)) return;

    // Left
    if (player.Velocity.X < 0)
        player.Sprite.position.X = tileRect.Right + 1;

    // Right
    else if (player.Velocity.X > 0)
        player.Sprite.position.X = tileRect.Left - player.Sprite.SourceRect.Width - 1;

    // Up
    else if (player.Velocity.Y < 0)
        player.Sprite.position.Y = tileRect.Bottom + 1;

    // Down
    else if (player.Velocity.Y > 0)
        player.Sprite.position.Y = tileRect.Top - player.Sprite.SourceRect.Height - 1;

    // Reset velocity
    player.Velocity = Vector2.Zero;
}

换句话说,在图块周围留出 1 个像素的边距,玩家精灵可能无法进入。

另一种方法是在检查定义碰撞的交点之前将平铺矩形(即使用负值调用 Inflate())“缩小”1 个像素。然后,当确实发生碰撞时,展示位置将与以前一样(即没有上面显示的+1-1 调整),但该展示位置本身不会被检测为碰撞。 p>

还有许多其他方法可以处理碰撞检测,但是 a) 如果没有关于您的场景的更多细节,很难知道其中是否以及哪些可能是更好的选择,并且 b) 鉴于您当前实现的明显简单性质,a我在上面提出的相对简单的修复似乎更有保证和有用。

【讨论】:

  • 不是这样的。错误是当您发生碰撞时,并且因为您是垂直碰撞,并且首先进行了水平检查,所以它将首先修复水平碰撞。修复应该围绕知道哪个碰撞更重要。这就是我现在正在测试的内容,但似乎没有任何效果。
  • “知道哪个碰撞更重要”——我什至不知道那是什么意思。无论如何,在您描述的场景中,根本不应该发生碰撞。 IE。这不是优先考虑冲突的问题;您的代码应该(在垂直方向发生碰撞后)将玩家精灵放置在瓷砖上方的非碰撞位置。如果玩家随后向左移动,那为什么会导致另一次碰撞呢?瓷砖不在玩家的左侧,因此朝该方向移动应该不会导致任何碰撞。
  • 我明白了 +1 和 -1 的意义。除了精灵位于瓷砖角落的情况外,它修复了碰撞。我现在正在修复它
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多