墙滑碰撞响应仅适用于一个轴(2D)

“精灵”是玩家对象,速度是跟踪运动的Vec2(在任何给定的时间被设置为-1,1或0),“精灵”中的每个精灵都是与玩家相同大小的墙是用于testing的块)。

if (velocity != Vec2.Zero) { Vec2 oldPos = sprite.Position; sprite.Position = sprite.Position + velocity; foreach (Sprite s in sprites.Except(new List<Sprite> { sprite })) { if (sprite.BoundingBox.Intersects(s.BoundingBox)) sprite.Position = new Vec2(oldPos.X, sprite.Position.Y); if (sprite.BoundingBox.Intersects(s.BoundingBox)) sprite.Position = new Vec2(sprite.Position.X, oldPos.Y); } velocity = Vec2.Zero; } 

所以,这实际上工作得很好 – 除了某种原因,它只能在一个方向上工作。 碰撞在两者中都起作用,但是当通过握住两个方向键滑到墙上时,它只能沿着Y轴滑动。

无论何时通过访问器写入“位置”variables,都会更新“精灵”的边界框,所以边界框始终保持最新。 墙壁不移动,所以他们也是最新的。

乍一看,似乎有几个明显的地方要检查 – 我正在修改边界框进行第二次检查,而当时我没有自动执行,所以我试图禁用它。 这样做打破了工作部分,所以不是这样。 当我在第二个if语句中添加“else”时,我没有改变行为,确认第一个块总是运行,第二个块永远不会运行。 如果我把它们翻转过来,那么另一个轴就会起作用。

我试图将它们分成两个foreach循环(只是为了看),行为仍然保持不变。 我有点困惑,为什么这些变化为什么没有提出这个问题的任何提示 – 这可能是我忽略的东西,所以我希望别人会看到。

如果你需要我发表任何其他的代码,请问。

我正在尝试用3D做这件事,而且时间非常糟糕,所以我想我会制作一个我的引擎的二维版本,并在将碰撞移回到3D之前将其分类。

好吧,在仔细阅读和重读我和他人的代码之后,我已经弄明白了。 如果有人看到任何地方我可以改善这个或看到任何问题,请让我知道。 另外,我希望这能够拯救一个人,从我处理的同样的挫折,哈哈。

正如我所发现的那样,它一次只能在一个轴上工作。 关键是确定如何决定使用什么轴,这听起来很简单,但是无论出于何种原因,上星期都在逃避我,尽管我已经注意到可以为我做的代码( 碰撞检测 – 在Sprite周围滑动 ,第二个答案)。 不知何故,当我今天早上几乎逝去的时候,我能够解读这个问题。

无论如何,这个概念是计算位置的差异,如果归一化的话,是从移动物体到可疑物体的方向。 然后检查每个坐标的绝对版本,以确定哪个坐标具有更大的幅度,并首先执行该坐标轴。

我也把我的冲突解决scheme移到了自己的函数中,以便它可以在同一个更新循环中recursion地执行。 这里是parsing器(“sprite”现在是“a”,“s”现在是“b”:

  void Resolve(Sprite a, Sprite b, Vec2 oldPos, int depth = 0) { if (++depth > 9) return; //abort if too deep Vec2 diff = (b.Position - a.Position); float adx = (float)Math.Abs(diff.X); float ady = (float)Math.Abs(diff.Y); if (adx > ady) { if (a.BoundingBox.Intersects(b.BoundingBox)) a.Position = new Vec2(oldPos.X, a.Position.Y); if (a.BoundingBox.Intersects(b.BoundingBox)) a.Position = new Vec2(a.Position.X, oldPos.Y); } else if (adx < ady) { if (a.BoundingBox.Intersects(b.BoundingBox)) a.Position = new Vec2(a.Position.X, oldPos.Y); if (a.BoundingBox.Intersects(b.BoundingBox)) a.Position = new Vec2(oldPos.X, a.Position.Y); } Resolve(a, b, oldPos, depth); } 

请注意,第二个条件非常重要 – 如果您将其保留为“其他”,那么当adx和ady相等时(例如,当您沿着两个方向移动出一个轴的碰撞时),将会出现奇怪的粘着行为最终都是1)。

我没有testing太多的最大深度的值,但5似乎不够,我任意select9,因为它大于方向的数量(8),使它可以补偿更多方向的碰撞可以立即移动(如果它最终导致任何放缓,我可能会减less,也许使它成为一个参数或全局设置)。

以下代码现在位于旧片段的位置:

  if (velocity != Vec2.Zero) { Vec2 oldPos = sprite.Position; sprite.Position = sprite.Position + velocity; foreach (Sprite s in sprites.Except(new List<Sprite> { sprite })) { Resolve(sprite, s, oldPos); velocity = Vec2.Zero; } } 

据我所知,这现在完美地工作。 也许我会把它重新组织成一个运动函数,它在同一个函数中执行循环和初始运动,使其更加整洁。