Обработка столкновений ограничивающей рамки - не обнаружение

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

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

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

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


person Tesserex    schedule 31.08.2009    source источник


Ответы (5)


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

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

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

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

person CoderTao    schedule 31.08.2009

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

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

person Community    schedule 31.08.2009
comment
Если вы собираетесь упомянуть Chipmunk, вы также можете упомянуть Box2D, но я думаю, что оба они излишни для того, что он пытается сделать. - person mpen; 31.08.2009

Когда я это исправлю, он сможет проходить кварталы влево или вправо.

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

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

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

person stonemetal    schedule 31.08.2009

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

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

person Tesserex    schedule 31.08.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);
  }
}
person ArchitectOfEvil    schedule 04.11.2009