3

一周前我有这个工作,但后来我把它弄坏了。我不能让它再次工作。我有一些 2D 精灵,它们只是矩形。不涉及轮换。我不是在寻找一种方法来检测它们之间的冲突,我有,并且有成千上万的文章在那里。我找不到任何关于当你看到碰撞时该怎么做的资源。我想要的只是不允许精灵重叠。没有反弹或任何东西。他们只是停下来。具体来说,现在我只有一个玩家和一些他遇到的关卡。

这听起来很简单,但事实并非如此。它必须非常精确,否则会发生奇怪的事情。我整天都在尝试以奇怪的混合结果来解决这个问题。有时我的球员卡在地板上,不能左右移动。当我解决这个问题时,他可以左右穿过街区。

所以我目前的设置是这样的:尝试将玩家移动到他想去的地方。然后询问地图他的瓷砖是否与任何东西发生碰撞。如果是这样,地图会显示四个方向中每个方向有多少重叠像素。现在棘手的部分是玩家应该如何对这些数字做出反应。当他在地面上时,与地板有 1 个像素的重叠,以便让他知道自己在地面上(否则他会在跌落和落地之间振动,因为没有重叠他认为他下方没有任何东西)。这一像素重叠意味着左右边缘也嵌入地板中,因此他无法移动。

有没有一种很好的方法来解决所有问题,为玩家使用一个边界框?为他的四个边分别设置一个单独的边界框会更好吗?

4

5 回答 5

2

与地面重叠的一种替代方法是将他的起始位置设置为刚好在地面上方;然后总是调用下降例程。

如果他的下一个下落部分会使他与地砖重叠,则将头像上的字段“在地面上”设置为 true(假设您需要跟踪该字段以用于跳跃目的),并设置头像的 y 位置就在地面上。虽然他会不断地“跌倒”,但在视觉上是难以察觉的。

另一种选择是您提到的,其中有 4 个边界框;问题是您可能不希望 4 个单独的角成为任何框的成员;因此,如果头像图像是 16x16px,则将其视为 4 个 1x16px 边界框,分别位于四个边上。这将带来它自己的数学难题,但它会起作用。

至于处理碰撞和跳跃;我认为可行的一个项目是:将运动分成两个离散的运动部分,一个在 x 轴上的移动和一个在 y 轴上的移动。如果 x 轴上的移动被墙阻挡,请将 x 坐标设置为有效的更远位置(因此,x 应该是导致墙/平台拥抱的值)。对 y 轴进行类似操作。此时,您还可以更改处理速度的方式;如果合适,将 x/y 速度分量设置为 0。根据您正在寻找的游戏感觉,这可能是不可取的。

于 2009-08-31T00:45:41.350 回答
0

听起来您正在尝试推出自己的东西,但我一直在使用 Chipmunk Dynamics 制作一些东西的原型,这是一个不错的开源小型 2D 物理库,因此如果您需要想法,您可以查看并了解他们是如何做到的。

http://code.google.com/p/chipmunk-physics/

于 2009-08-31T01:01:53.320 回答
0

当我解决这个问题时,他可以左右穿过街区。

在这个阶段,您何时确定您在走进一个街区时从碰撞检测中获得了正确的结果?

然后询问地图他的瓷砖是否与任何东西发生碰撞。如果是这样,地图会显示四个方向中每个方向有多少重叠像素。

如果在行进方向上有任何 x 重叠,您可以使用其中的数据来禁止 x 运动。就我个人而言,我会在右侧重叠返回正值,在左侧重叠返回负值,然后仅在 xoverlap 为零或符号与运动方向相反时才允许 x 运动。那么对于 Y,你需要做的就是同样的事情,除了检查 1 而不是 0。

于 2009-08-31T01:34:56.320 回答
0

我想我明白了。主要受到stonemetal的回答的启发。我最近的一个缺陷是玩家能够“钩住”墙壁,因为他的边界框会在墙上卡住一个像素,然后它会落在下面的瓷砖边缘,阻止他。

我通过拆分 x 和 y 运动解决了所有问题。由于重力是可以的,所以我把 x 放在首位。看看您是否可以水平移动,如果不能,请取消该动作。然后尝试垂直,如果你不能取消它。这种分离确保了任何一方都不会从另一方那里得到错误的信息(比如认为你在地上,一个 Y 的事情,因为你靠在墙上,这是一个 X 的事情)。多谢你们!

于 2009-08-31T03:46:39.053 回答
0

这是一个简单的盒子碰撞测试。显然很简单,等你做多边形碰撞测试!

//! (collision detection)
inline BOOL Sprite::TestCollision(Sprite* pTestSprite)
{
  //! Get the collision box.
  RECT& rcTest = pTestSprite->GetCollision();

  //! box collision check.
  return m_rcCollision.left <= rcTest.right && rcTest.left <= m_rcCollision.right &&
         m_rcCollision.top <= rcTest.bottom && rcTest.top <= m_rcCollision.bottom;
}

现在至于如何停止精灵或反弹或其他什么,它是这样的:(注意:这种情况是检查与边界的碰撞,但基本上与其他对象执行相同的操作)。如果它只是轴对齐的盒子碰撞,您所要做的就是将碰撞对象与另一个碰撞对象的水平或垂直边缘对齐。如果对象从左侧碰撞,则与另一个对象的左边缘对齐。简单的。如果您想要反弹或停止,请相应地更改速度。

在此示例中,我使用更新的位置创建测试变量,然后进行碰撞测试,然后执行下面的例程,或者如果没有碰撞则设置新位置。所以我们实际上是在碰撞发生之前修复它。

// Update the position
POINT ptNewPosition, ptSpriteSize, ptBoundsSize;
ptNewPosition.x = m_rcPosition.left + m_ptVelocity.x;
ptNewPosition.y = m_rcPosition.top + m_ptVelocity.y;
ptSpriteSize.x = m_rcPosition.right - m_rcPosition.left;
ptSpriteSize.y = m_rcPosition.bottom - m_rcPosition.top;
ptBoundsSize.x = m_rcBounds.right - m_rcBounds.left;
ptBoundsSize.y = m_rcBounds.bottom - m_rcBounds.top;

// Check the bounds
// Wrap?
if (m_baBoundsAction == BA_WRAP)
{
  if ((ptNewPosition.x + ptSpriteSize.x) < m_rcBounds.left)
   ptNewPosition.x = m_rcBounds.right;
  else if (ptNewPosition.x > m_rcBounds.right)
   ptNewPosition.x = m_rcBounds.left - ptSpriteSize.x;
  if ((ptNewPosition.y + ptSpriteSize.y) < m_rcBounds.top)
   ptNewPosition.y = m_rcBounds.bottom;
  else if (ptNewPosition.y > m_rcBounds.bottom)
   ptNewPosition.y = m_rcBounds.top - ptSpriteSize.y;
}
// Bounce?
else if (m_baBoundsAction == BA_BOUNCE)
{
  BOOL bBounce = FALSE;
  POINT ptNewVelocity = m_ptVelocity;
  if (ptNewPosition.x < m_rcBounds.left)
  {
   bBounce = TRUE;
   ptNewPosition.x = m_rcBounds.left;
   ptNewVelocity.x = -ptNewVelocity.x;
  }
  else if ((ptNewPosition.x + ptSpriteSize.x) > m_rcBounds.right)
  {
   bBounce = TRUE;
   ptNewPosition.x = m_rcBounds.right - ptSpriteSize.x;
   ptNewVelocity.x = -ptNewVelocity.x;
  }
  if (ptNewPosition.y < m_rcBounds.top)
  {
   bBounce = TRUE;
   ptNewPosition.y = m_rcBounds.top;
   ptNewVelocity.y = -ptNewVelocity.y;
  }
  else if ((ptNewPosition.y + ptSpriteSize.y) > m_rcBounds.bottom)
  {
   bBounce = TRUE;
   ptNewPosition.y = m_rcBounds.bottom - ptSpriteSize.y;
   ptNewVelocity.y = -ptNewVelocity.y;
  }
  if (bBounce)
   SetVelocity(ptNewVelocity);
}
// v0.1.1 (collision detection)
// Die?
else if (m_baBoundsAction == BA_DIE)
{
  if ((ptNewPosition.x + ptSpriteSize.x) < m_rcBounds.left || ptNewPosition.x > m_rcBounds.right
   || (ptNewPosition.y + ptSpriteSize.y) < m_rcBounds.top || ptNewPosition.y > m_rcBounds.bottom)
   return SA_KILL;
}
// v0.1.1 ----------------------
// Stop (default)
else
{
  if (ptNewPosition.x  < m_rcBounds.left || ptNewPosition.x > (m_rcBounds.right - ptSpriteSize.x))
  {
   ptNewPosition.x = max(m_rcBounds.left, min(ptNewPosition.x, m_rcBounds.right - ptSpriteSize.x));
   SetVelocity(0, 0);
  }
  if (ptNewPosition.y  < m_rcBounds.top || ptNewPosition.y > (m_rcBounds.bottom - ptSpriteSize.y))
  {
   ptNewPosition.y = max(m_rcBounds.top, min(ptNewPosition.y, m_rcBounds.bottom - ptSpriteSize.y));
   SetVelocity(0, 0);
  }
}
于 2009-11-04T12:30:41.267 回答