简单的平台碰撞算法是毛刺

时间:2015-10-09 22:11:22

标签: c# collision-detection monogame

我对这个简单的碰撞算法有一个小故障。我正在做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;
}

显然,如果我用最后一个切换两个第一个ifs,那么如果我从侧面按下并向上或向下按压,就会发生这种情况。

我到底错过了什么?

1 个答案:

答案 0 :(得分:0)

正如您所指出的,出现此问题的原因是您检查碰撞条件的顺序。特别是,虽然视觉上你的玩家精灵看起来在一个平铺上方,你的代码正在检测精灵是否相交,并且由于第一次检查是水平碰撞,玩家精灵的位置被移动到平铺精灵的右边(上面和实际上,在右边,所以你只调整检测到的碰撞轴的坐标。)

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

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

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调整),但该放置本身不会被检测为碰撞。

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