【问题标题】:2D physics / collision formula not working as intended2D 物理/碰撞公式未按预期工作
【发布时间】:2016-05-06 13:32:59
【问题描述】:

我有一个 2D 瓷砖矩阵(现在是墙),对于每面墙,我检查我的玩家是否是下一个 Rectangle下一个,如 下一帧中的位置) 正在与那堵墙发生碰撞,如果是,我希望我的玩家站在它旁边,而不是进入它。

这是我想出的公式(这只是左右碰撞):

//*move entity
if (KState.Down(Keys.W)) en.AddForce(new Vector2(0, -10));
if (KState.Down(Keys.S)) en.AddForce(new Vector2(0, 10));
if (KState.Down(Keys.A)) en.AddForce(new Vector2(-10, 0));
if (KState.Down(Keys.D)) en.AddForce(new Vector2(10, 0));

foreach(Item i in map.Items)
{
    //resolve left-right collision
    if (en.NextBody(elapsedTime).Intersects(i.Body))
    {
        //check distance between top left corners
        int distance = (int)Math.Abs(i.Position.X - en.Position.X);

        //stop player in his horisontal movement
        en.AddForce(new Vector2(-en.Force.X, 0));

        //place player next to the wall his about to collide with
        en.Position = new Vector2(en.Position.X - distance + i.Body.Width, en.Position.Y);
    }
}

//stop player in place
if (KState.Clicked(Keys.Space)) en.AddForce(en.Force*-1);

en.Update(elapsedTime);
**

这给了我这个结果(请注意,fps 不稳定,目前的矩形是 32x32 的正方形):

第一行文字是玩家Rectangle,下一行是玩家最后撞墙的Rectangle
现在,岩石(玩家)向左移动时,我按住左键一段时间,向右移动时,我按住右键一段时间。

问题:正如你在 gif 中看到的,当玩家向左走时,他在左墙之间移动了 1 个像素。当他向右移动时,他会弹起一次,然后就停留在右边的墙壁旁边。我不想要任何弹跳,但我不知道如何在我正在使用的逻辑中解决这个问题。

另外,如果您知道如何解决 Rectangle 冲突的良好逻辑,以便它们不会从任何方面相交,我会很乐意与我分享。

编辑 1: 我设法解决了正确问题的冲突。而不是上面我放的代码

int sign = Math.Sign(i.Position.X - en.Position.X);
en.AddForce(new Vector2(-en.Force.X, 0));
//wall is right and player is left
if (sign > 0) en.Position = new Vector2(i.Body.Left - en.Body.Width, en.Position.Y);
//wall is left and player is right
if (sign < 0) en.Position = new Vector2(i.Body.Right, en.Position.Y);

但是左侧碰撞的问题是一样的。我试过
if (sign &lt; 0) en.Position = new Vector2(i.Body.Right + 1, en.Position.Y);
if (sign &lt; 0) en.Position = new Vector2(i.Body.Right - 1, en.Position.Y); 以防万一,但问题是一样的(在“...-1...”情况下玩家卡在墙上)。

编辑 2: 按照@paste 的要求,这里是播放器类中NextBody(float elapsedTime) 的代码。

public Rectangle NextBody(float elapsedTime)
{ return new Rectangle((int)(force.X * elapsedTime) + body.X, (int)(force.Y * elapsedTime) + body.Y, body.Width, body.Height); }

【问题讨论】:

  • 你有en.SetForce() 方法吗?当从当前力中减去“相等”的量时,可能会出现一些精度问题,从而使残余力仍在en 对象中。这可以通过调用 en.SetForce(Vector2.Zero) 来解决
  • 猜对了,但我查了一下,确实是 0。
  • 您可以发布您的NextBody() 代码吗?
  • 整个项目中是否还有其他地方调用了AddForce() 或更改了Position 属性?
  • 是的。在级别构造函数中。位置已设置,但该函数仅触发一次。这是一个很好的观察,但我知道我为什么发布这个问题。我正在系统地逐部分创建,所以我知道在尝试某些东西时使用了什么代码。 +1 良好观察

标签: xna 2d physics monogame rectangles


【解决方案1】:

这是正在发生的事情:

当岩石向左移动时,当物体第一次发生碰撞时,算法会正常工作。它看到对象重叠,将力设置为Vector2.Zero,并移动en,使其接触item 的右侧。好的。您的对象已停止并位于正确的位置。

下一帧发生的事情是出错的地方。你设置你的力向量,然后调用NextBody()。让我们看看X坐标。它被设置为:

(int)(force.X * elapsedTime) + body.X

这意味着什么? force.X 非零,elapsedTime 也非零,但它们小于 1,因此当它们转换为 int 时,结果为零。因此,您的body 与之前的位置相同,这意味着Rectangles 不会 重叠。这意味着您的碰撞响应代码被跳过并且您的 force 向量仍然不为零!

然后,再往下,调用播放器的Update() 方法。我猜在那个代码中你更新了玩家的PositionBody。但由于force 不为零,并且您使用Vector2 表示Position,x 坐标最终将类似于31.841304... 而不是32。如果你使用这个数字来计算新的Body 并在你的Draw 方法中使用它,你最终会在 x = 31 处绘制它。下次检查时,它会检测到碰撞,并且玩家会再次反弹。

有多种方法可以解决此问题。我在碰撞代码中所做的是首先实际移动对象并记录它们在前一帧中的位置。这样,他们的位置的最后一次调整是当他们被移出碰撞区域时。

【讨论】:

  • 你找到了!我只是将 body.X 包含在像这样的 (int)(force.X * elapsedTime + body.X) (对于 Y 相同)中,现在它可以完美地工作了。我从来没想过。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-05-14
  • 1970-01-01
  • 2018-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多