边界框碰撞处理 - 未检测

时间:2009-08-31 00:27:09

标签: 2d collision-detection sprite bounding-box

一周前我有这个工作,但后来我打破了它。我再也无法恢复工作了。我有一些2D精灵,它们只是矩形。不涉及轮换。我不是在寻找一种方法来检测它们之间的碰撞,我有这个,而且有成千上万的文章在那里。我找不到的是关于在看到碰撞时该怎么做的任何资源。我想要的只是不允许精灵重叠。没有弹跳或任何东西。他们只是停下来。特别是现在我只有一个玩家和他遇到的一些关卡。

这听起来很简单,但事实并非如此。它必须非常精确或奇怪的事情发生。我一直试图用奇怪的混合结果整天解决这个问题。有时候我的球员被卡在地板上而无法向左或向右移动。当我解决这个问题时,他可以向左或向右走过。

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

使用播放器的一个边界框,是否有一个很好的方法可以解决所有问题?为他的四个方面分别设置一个单独的边界盒会不会更好?

5 个答案:

答案 0 :(得分:2)

与地面重叠的一种选择是将他的起始位置设置在地面上;然后总是调用下降程序。

如果他的下一个下降部分会让他与地面砖重叠,请将化身“地面上”的字段设置为true(假设您需要跟踪,例如,跳跃目的),并设置y位置只是在地面上的化身。虽然他会经常“摔倒”,但在视觉上却难以察觉。

另一种选择是你提到的,你有4个边界框;你可能不希望4个角落成为任何一个盒子的成员;因此,如果头像图像是16x16px,则将其视为四个边中每个边上的4个1x16px边界框。这将带来它自己的数学头痛,但它会起作用。

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

答案 1 :(得分:0)

听起来你正在试图推出自己的东西,但我一直在使用Chipmunk Dynamics进行原型设计,这是一个很好的2D物理库,它是开源的,所以你可以看看,如果你想要它们是怎么做的想法。

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

答案 2 :(得分:0)

  

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

在这个阶段,你确定你在步入一个街区的碰撞检测中得到了正确的结果吗?

  

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

如果行进方向上有任何x重叠,则使用此数据禁止x运动。就个人而言,如果xoverlap为零或符号与运动方向相反,我会在右边返回一个正重叠并在左边返回负数上重叠,然后只允许x运动。然后对于Y你需要做的就是同样的事情,除了检查1而不是零。

答案 3 :(得分:0)

我想我已经明白了。主要受到stonemetal答案的启发。我最近的缺陷是玩家可以“挂钩”到墙上,因为他的边界框会在墙上留下一个像素,然后它将落在下面的瓷砖边缘,阻止他。

我通过分割x和y动作解决了所有问题。由于引力是一个特殊的交易,我把x作为优先事项。看看你是否被允许水平移动,如果不能,则取消该动作。然后尝试垂直,如果你不能,取消它。分离确保两个人都不会从另一半获得虚假信息(比如认为你在地上,Y事情,因为你是在墙上,这是一个X事物)。谢谢你们!

答案 4 :(得分: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);
  }
}