C# 简单的平台碰撞算法存在故障

C# 简单的平台碰撞算法存在故障,c#,collision-detection,monogame,C#,Collision Detection,Monogame,这个简单的碰撞算法有个小问题。我在做这个,这个碰撞和我在以前的platformer风格的游戏中所做的算法碰撞非常相似。我以前的游戏也有这个问题,只是不记得修复了。我有一张地图,它有很多瓷砖,有些被标记为实心。他们的更新方法检查是否与玩家发生冲突。 如果磁贴是实心的,它应该停止玩家并将其移动到一个有效的位置 问题是当我向上或向下移动时,粘在瓷砖上,然后按向左或向右移动键。 例如: private static void HandleCollisionWithTile(Player player,

这个简单的碰撞算法有个小问题。我在做这个,这个碰撞和我在以前的platformer风格的游戏中所做的算法碰撞非常相似。我以前的游戏也有这个问题,只是不记得修复了。我有一张地图,它有很多瓷砖,有些被标记为实心。他们的更新方法检查是否与玩家发生冲突。 如果磁贴是实心的,它应该停止玩家并将其移动到一个有效的位置

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

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;
}
我按下向下键,我在实心瓷砖的顶部,按下向左。此时,玩家被重新定位到磁贴的右侧

由于碰撞方法中的
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;
}
publicsvoid更新(游戏时间,参考玩家)
{
如果(State!=tilstate.Solid)返回;
矩形平铺=
新矩形((int)Position.X,(int)Position.Y,
SourceRect.Width、SourceRect.Height);
矩形playerRect=
新矩形((int)player.Sprite.position.X,(int)player.Sprite.position.Y,
player.Sprite.SourceRect.Width,player.Sprite.SourceRect.Height);
带瓷砖的手牌集合(玩家、玩家等、瓷砖竖立);
}
/// 
///与平铺碰撞后,将玩家重新定位到当前位置
/// 
///与瓷砖相交的玩家
///播放器的矩形
///瓷砖的长方形
私有静态void HandleCollisionWithTile(Player-Player、矩形Player-rect、矩形tillest)
{
//确保有讨论会
如果(!playerRect.与(tilRect))相交,则返回;
//左
if(播放器速度X<0)
player.Sprite.position.X=til.Right;
//对
else if(player.Velocity.X>0)
player.Sprite.position.X=tilpright.Left-player.Sprite.SourceRect.Width;
//向上
else if(player.Velocity.Y<0)
player.Sprite.position.Y=til.Bottom;
//向下
else if(player.Velocity.Y>0)
player.Sprite.position.Y=til直立.Top-player.Sprite.SourceRect.Height;
//重置速度
player.Velocity=Vector2.0;
}
很明显,如果我把前两个如果换成最后一个,如果我从侧面向上或向下按,就会发生这种情况


我到底遗漏了什么?

正如您所指出的,发生此问题的原因是您检查碰撞条件的顺序。特别是,当玩家精灵在视觉上显示在磁贴上方时,代码会检测到精灵相交,并且由于第一个检查是水平碰撞,玩家精灵的位置会移动到磁贴精灵的右侧(实际上,位于上方和右侧,因此仅调整检测到的碰撞轴中的坐标)

在我看来,这暴露了代码中的一个基本缺陷:有人可能会说,在解决了垂直方向上发生的冲突后,通过将播放器精灵放置在平铺精灵的顶部,现在播放器精灵不应该与平铺精灵处于冲突状态,相反,您的代码会检测到这种情况碰撞

如果没有更好的代码示例,很难知道最佳修复方法是什么。但一个明显的快速而肮脏的解决方案是确保在解决冲突时,您不会将播放器精灵放置在与平铺精灵相交的位置。例如:

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;
}
private static void HandleCollisionWithTile(Player-Player,矩形Player-rect,矩形tillest)
{
//确保有讨论会
如果(!playerRect.与(tilRect))相交,则返回;
//左
if(播放器速度X<0)
player.Sprite.position.X=til竖立。右+1;
//对
else if(player.Velocity.X>0)
player.Sprite.position.X=til直立.Left-player.Sprite.SourceRect.Width-1;
//向上
else if(player.Velocity.Y<0)
player.Sprite.position.Y=til.Bottom+1;
//向下
else if(player.Velocity.Y>0)
player.Sprite.position.Y=til直立.Top-player.Sprite.SourceRect.Height-1;
//重置速度
player.Velocity=Vector2.0;
}
换句话说,在玩家精灵可能无法进入的磁贴周围给自己留一个1像素的边距

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


处理冲突检测还有很多其他方法,但是a)如果没有关于场景的更多细节,就很难知道这些方法中的哪些是更好的选择,b)鉴于当前实现的简单性,我上面提出的一个相对简单的解决方案似乎更有可能得到保证和使用。

不是这样的。错误是当你发生碰撞时,因为你是垂直碰撞,而水平检查是首先完成的,它将首先修复水平碰撞。解决方法应该是知道哪个碰撞更重要。这就是我现在正在测试的,但似乎没有什么能做到。“知道哪个碰撞更重要”——我甚至不知道这意味着什么。在任何情况下,在您描述的场景中,都不应该有任何冲突。也就是说,这不是优先考虑碰撞的问题;您的代码应该(在垂直方向发生碰撞后)将玩家精灵放置在平铺上方的非碰撞位置。如果玩家向左移动,为什么会导致另一次碰撞?瓷砖不在房间的左边