Обработка столкновений в ограничительной коробке - не обнаружение - PullRequest
3 голосов
/ 31 августа 2009

У меня это работало неделю назад, но потом я его сломал. Я не могу заставить его работать снова. У меня есть несколько 2D спрайтов, они просто прямоугольники. Нет ротации. Я не ищу способ обнаружить столкновения между ними, у меня это есть, и есть тысячи статей об этом. Я не могу найти какой-либо ресурс о том, что делать, когда вы видите столкновение. Все, что я хочу, это чтобы спрайты не перекрывались. Не подпрыгивая или что-нибудь. Они просто останавливаются. В частности, сейчас у меня просто есть игрок и несколько тайлов уровня, с которыми он сталкивается.

Звучит просто, но это не так. Это должно быть очень точно, или происходят странные вещи. Я пытался исправить это весь день со странными смешанными результатами. Иногда мой игрок застревает в полу и не может двигаться ни влево, ни вправо. Когда я это исправлю, он сможет пройти через блоки влево или вправо.

Итак, моя текущая установка выглядит так: Попробуйте переместить игрока туда, куда он хочет. Затем спросите карту, сталкивается ли его плитка с чем-либо. Если это так, на карте указано, сколько пикселей совпадают в каждом из четырех направлений. Самое сложное в том, как игрок должен реагировать на эти цифры. Когда он на земле, он перекрывается на 1 пиксель с полом, чтобы он знал, что он на земле (иначе он колеблется между падением и приземлением на землю, потому что без перекрытия он думает, что под ним ничего нет). Это однопиксельное перекрытие означает, что левый и правый края также встроены в пол, и поэтому он не может двигаться.

Есть ли хороший метод для сортировки всего, используя одну ограничивающую рамку для игрока? Было бы лучше иметь отдельную ограничивающую рамку для каждой из четырех сторон?

Ответы [ 5 ]

2 голосов
/ 31 августа 2009

Одной из альтернатив перекрытию с землей было бы установить его начальное местоположение чуть выше земли; затем всегда вызывайте рутину падения.

Если из-за его следующей падающей части он перекрывается с плиткой земли, установите для поля аватара «на земле» значение true (при условии, что вам необходимо отследить это, скажем, для целей прыжка), и установите местоположение y аватара просто по земле. Хотя он будет постоянно «падать», это будет визуально незаметно.

Другой альтернативой было бы то, что вы упомянули, где у вас есть 4 ограничивающие рамки; выгода в том, что вы, вероятно, не хотите, чтобы 4 отдельных угла были членами какой-либо из коробок; Итак, если изображение аватара имеет размер 16x16px, рассматривайте его как 4 ограничивающие рамки 1x16px на каждой из четырех сторон. Это принесет с собой собственные математические головные боли, но это сработает.

Что касается обработки столкновений и прыжков; один пункт, который, я думаю, сработал бы, состоял бы в следующем: разбить движение на две отдельные части движения, движение по оси x и движение по оси y. Если перемещение по оси x заблокировано стеной, установите координату x в следующем допустимом положении (таким образом, x должно быть значением, которое приводит к объятию стены / платформы). Сделайте то же самое с осью Y. В этот момент вы также можете изменить скорость обработки; если нужно, установите компонент скорости x / y равным 0. Это может быть нежелательно в зависимости от игры, которую вы ищете.

0 голосов
/ 04 ноября 2009

Вот простой тест столкновения коробки. Это, очевидно, очень просто, подождите, пока вы не проведете тестирование столкновения полигонов!

//! (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);
  }
}
0 голосов
/ 31 августа 2009

Я думаю, что понял. Главным образом вдохновленный ответом Stonemetal. Мой последний недостаток заключался в том, что игрок мог «зацепиться» за стены, потому что его ограничивающий прямоугольник поместился бы в стене на один пиксель, а затем он упал бы на край плитки ниже, останавливая его.

Я решил все, разделив движение x и y. Поскольку гравитация - это сделка, я сделал х приоритетом. Посмотрите, разрешено ли вам двигаться горизонтально, затем отмените это движение, если не можете. Затем попробуйте по вертикали и отмените его, если не можете. Разделение гарантирует, что ни одна половина не получит ложную информацию от другой (например, думая, что вы находитесь на земле, вещь Y, потому что вы против стены, которая является вещью X). Спасибо, ребята!

0 голосов
/ 31 августа 2009

Когда я исправлю это, он может пройти через блоки влево или вправо.

Когда на этом этапе вы уверены, что получаете правильные результаты обнаружения столкновений при входе в блок?

Тогда спросите карту, сталкивается ли его плитка с чем-либо. Если это так, на карте указано, сколько пикселей совпадают в каждом из четырех направлений.

Вы используете данные из этого, чтобы запретить движение x, если есть какое-либо перекрытие x в направлении движения. Лично у меня было бы перекрытие справа, возвращающее положительное значение и перекрытие слева, возвращающее отрицательное значение, тогда разрешалось бы только движение x, если xoverlap равно нулю или знак противоположен направлению движения. Тогда для Y все, что вам нужно сделать, это то же самое, кроме проверки против 1 вместо нуля.

0 голосов
/ 31 августа 2009

Звучит так, будто вы пытаетесь накатить свои собственные вещи, но я прототипировал некоторые вещи с помощью Chipmunk Dynamics, милой маленькой 2D-библиотеки физики с открытым исходным кодом, так что вы можете посмотреть и посмотреть, как они это делают, если хотите идеи.

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

...