Проблеми зіткнення платформи ADB платформи 2D


51

http://dl.dropbox.com/u/3724424/Programming/Gifs/game6.gif

У мене проблема з вирішенням зіткнення AABB.


Я розв'язую перетин AABB, вирішивши спочатку вісь X, потім вісь Y. Це робиться для запобігання цієї помилки: http://i.stack.imgur.com/NLg4j.png


Поточний метод працює чудово, коли об’єкт переміщується в програвач, і програвач повинен бути натиснений горизонтально. Як ви бачите на .gif, горизонтальні шипи натискають програвач правильно.


Коли вертикальні шипи переміщуються в програвач, осі X все ще вирішуються спочатку. Це робить "використання шипів як підйомника" неможливим.

Коли гравець рухається у вертикальні шипи (уражений силою тяжіння, падає на них), його натискають на вісь Y, оскільки для початку не було перекриття на осі X.


Щось я спробував, це метод, описаний у першій відповіді за цим посиланням: 2D виявлення зіткнень прямокутних об'єктів

Однак шипи та рухомі об'єкти рухаються, змінюючи їх положення, а не швидкість, і я не обчислюю їх наступну передбачувану позицію, поки не буде викликаний метод Update (). Потрібно сказати, що і це рішення не спрацювало. :(


Мені потрібно вирішити зіткнення AABB таким чином, щоб обидва описані вище випадки працювали за призначенням.

Це мій поточний вихідний код зіткнення: http://pastebin.com/MiCi3nA1

Я був би дуже вдячний, якби хтось міг розібратися в цьому, оскільки ця помилка була присутня в двигуні ще з самого початку, і я намагаюся знайти хороше рішення, не маючи успіху. Це серйозно змушує мене проводити ночі, переглядаючи код зіткнення і заважаючи мені потрапити на "веселу частину" та кодувати логіку гри :(


Я спробував реалізувати ту саму систему зіткнення, що і в демонстраційній платформі XNA AppHub (шляхом скріплення копіювання більшості матеріалів). Однак помилка "стрибки" трапляється в моїй грі, в той час як вона не відбувається в демо-версії AppHub. [стрибок помилка: http://i.stack.imgur.com/NLg4j.png ]

Щоб перейти, я перевіряю, чи гравець "onGround", а потім додайте -5 до Velocity.Y.

Оскільки швидкість X у гравця вище, ніж Velocity.Y (див. Четверту панель на діаграмі), onGround встановлено на істинне, коли цього не повинно бути, і, таким чином, дозволяє гравцеві стрибати в повітрі.

Я вважаю, що цього не відбувається в демонстраційній програмі AppHub, оскільки швидкість гравця.X ніколи не буде вище Velocity.Y, але я можу помилятися.

Я вирішив це раніше, вирішивши спочатку по осі X, потім по осі Y. Але це накручує зіткнення зі шипами, як я вже говорив вище.


5
Насправді нічого не відбувається. Метод, який я використовую, натискає спочатку на вісь X, потім на вісь Y. Тому гравця натискають горизонтально навіть на вертикальні шипи. Мені потрібно знайти рішення, яке дозволяє уникнути "проблеми стрибків" і штовхає гравця на неглибоку вісь (вісь з найменшим проникненням), але я не можу знайти її.
Вітторіо Ромео

1
Ви могли виявити, до якого обличчя перешкода торкається гравця, і вирішити цю
проблему

4
@Johnathan Hobbs, прочитайте запитання. Ві точно знає, що робить його код, але не знає, як вирішити певну проблему. Перегляд коду не допоможе йому в цій ситуації.
AttackingHobo

4
@Maik @Jonathan: Ніщо не «не піде не так» з програмою - він точно розуміє, де і чому його алгоритм не робить того, що він хоче. Він просто не знає, як змінити алгоритм, щоб робити те, що він хоче. Тож налагоджувач не корисний у цьому випадку.
BlueRaja - Danny Pflughoeft

1
@AttackingHobo @BlueRaja Я згоден і видалив свій коментар. Це я просто стрибав на висновки про те, що відбувається. Я дійсно вибачаюсь за те, що ви змусили вас пояснити це і за те, що ввели в оману хоча б одну людину. Чесно кажучи, ви можете розраховувати на те, що я справді правильно сприйму це питання, перш ніж я наступного разу залишу відповідь.
doppelgreener

Відповіді:


9

Гаразд, я зрозумів, чому демонстратор платформи XNA AppHub не має помилки "стрибки": демонстрація тестує плитки зіткнення зверху вниз . Якщо стоять проти "стіни", гравець може перекривати кілька плиток. Порядок вирішення важливий, оскільки вирішення одного зіткнення може також вирішити інші зіткнення (але в іншому напрямку). onGroundВластивість встановлюється тільки тоді , коли зіткнення вирішується натисканням гравця вгору по осі у. Ця резолюція не відбудеться, якщо попередні резолюції штовхали плеєра вниз та / або горизонтально.

Я зміг відтворити "стрибаючу" помилку в демонстрації XNA, змінивши цей рядок:

for (int y = topTile; y <= bottomTile; ++y)

до цього:

for (int y = bottomTile; y >= topTile; --y)

(Я також підробив деякі константи, пов'язані з фізикою, але це не має значення.)

Можливо, сортування bodiesToCheckпо осі у перед вирішенням зіткнень у вашій грі виправить помилку "стрибки". Я пропоную вирішити зіткнення на "дрібній" осі проникнення, як це робить демонстратор XNA та Тревор . Також зауважте, що демонстраційний програвач XNA удвічі більший, ніж плитка, здатна зіткнутись, що робить випадок множинних зіткнень більш імовірним.


Це звучить цікаво ... Ви вважаєте, що впорядкування всього за координатою Y перед виявленням зіткнень могло б вирішити всі мої проблеми? Чи є улов?
Вітторіо Ромео

Ааа і я знайшов "улов". За допомогою цього методу виправляється помилка, що стрибає, але якщо я встановив Velocity.Y на 0 після удару в стелю, він повторюється.
Вітторіо Ромео

@Vee: встановити Position = nextPositionнегайно всередині forциклу, інакше небажані рішення зіткнення (налаштування onGround) все ще виникають. При ударі об стелю гравця слід штовхати вертикально вниз (і ніколи вгору), таким чином, onGroundніколи не слід встановлювати. Ось як це робить демонстратор XNA, і я не можу спростувати помилку "стелі" там.
Лефтій

pastebin.com/TLrQPeBU - це мій код: я фактично працюю над самою позицією, не використовуючи змінну "nextPosition". Це повинно працювати так, як це робиться в демонстрації XNA, але якщо я тримаю лінію, яка встановлює Velocity.Y до 0 (рядок 57), не коментується, то виникає стрибок-помилка. Якщо я його зніму, гравець продовжує плавати, коли стрибає в стелю. Це можна виправити?
Вітторіо Ромео

@Vee: ваш код не показує, як onGroundвстановлено, тому я не можу дослідити, чому стрибки дозволено неправильно. Демонстрація XNA оновлює свою аналогічну isOnGroundвластивість всередині HandleCollisions(). Крім того, після встановлення Velocity.Y0, чому гравітація знову не починає рухати плеєр вниз? Я здогадуюсь, що onGroundвластивість неправильно встановлена. Погляньте, як демонструється оновлення XNA previousBottomта isOnGround( IsOnGround).
Лефтій

9

Найпростішим рішенням для вас буде перевірити обидва напрямки зіткнення проти кожного об'єкта в світі перед тим, як вирішити будь-які зіткнення, і просто вирішити менший з двох результативних "складних зіткнень". Це означає, що ви вирішуєте найменшою можливою сумою, замість того, щоб завжди вирішувати x спочатку або завжди вирішувати y першу.

Ваш код виглядатиме приблизно так:

// This loop repeats, until our object has been fully pushed outside of all
// collision objects
while ( StillCollidingWithSomething(object) )
{
  float xDistanceToResolve = XDistanceToMoveToResolveCollisions( object );
  float yDistanceToResolve = YDistanceToMoveToResolveCollisions( object );
  bool xIsColliding = (xDistanceToResolve != 0.f);

  // if we aren't colliding on x (not possible for normal solid collision 
  // shapes, but can happen for unidirectional collision objects, such as 
  // platforms which can be jumped up through, but support the player from 
  // above), or if a correction along y would simply require a smaller move 
  // than one along x, then resolve our collision by moving along y.

  if ( !xIsColliding || fabs( yDistanceToResolve ) < fabs( xDistanceToResolve ) )
  {
    object->Move( 0.f, yDistanceToResolve );
  }
  else // otherwise, resolve the collision by moving along x
  {
    object->Move( xDistanceToResolve, 0.f );
  }
}

велика редакція : Читаючи коментарі до інших відповідей, я думаю, що нарешті помітив нестандартне припущення, яке призведе до того, що такий підхід не працює (і який пояснює, чому я не міг зрозуміти проблеми, які деякі - але не всі - люди бачили з таким підходом). Щоб уточнити, ось ще декілька псевдокодів, які більш чітко показують, які функції, на які я посилався, повинні насправді виконувати:

bool StillCollidingWithSomething( MovingObject object )
{
  // loop over every collision object in the world.  (Implementation detail:
  // don't test 'object' against itself!)
  for( int i = 0; i < collisionObjectCount; i++ )
  {
    // if the moving object overlaps any collision object in the world, then
    // it's colliding
    if ( Overlaps( collisionObject[i], object ) )
      return true;
  }
  return false;
}

float XDistanceToMoveToResolveCollisions( MovingObject object )
{
  // check how far we'd have to move left or right to stop colliding with anything
  // return whichever move is smaller
  float moveOutLeft = FindDistanceToEmptySpaceAlongNegativeX(object->GetPosition());
  float moveOutRight = FindDistanceToEmptySpaceAlongX(object->GetPosition());
  float bestMoveOut = min( fabs(moveOutLeft), fabs(moveOutRight) );

  return minimumMove;
}

float FindDistanceToEmptySpaceAlongX( Vector2D position )
{
  Vector2D cursor = position;
  bool colliding = true;
  // until we stop colliding...
  while ( colliding )
  {
    colliding = false;
    // loop over all collision objects...
    for( int i = 0; i < collisionObjectCount; i++ )
    {
      // and if we hit an object...
      if ( Overlaps( collisionObject[i], cursor ) )
      {
        // move outside of the object, and repeat.
        cursor.x = collisionObject[i].rightSide;
        colliding = true;

        // break back to the 'while' loop, to re-test collisions with
        // our new cursor position
        break;
      }
    }
  }
  // return how far we had to move, to reach empty space
  return cursor.x - position.x;  
}

Це не тест "на пару об'єктів"; він не працює, випробовуючи та розв’язуючи рухомий об’єкт проти кожної плитки карти світу окремо (такий підхід ніколи не буде надійно працювати, і не спрацьовує все частіше катастрофічно, оскільки розміри плитки зменшуються). Натомість це тестування рухомого об’єкта проти кожного об'єкта на карті світу одночасно, а потім вирішення на основі зіткнень із цілою картою світу.

Таким чином ви гарантуєте, що (наприклад) окремі настінні плитки в стіні ніколи не відскакують програвача вгору-вниз між двома сусідніми плитками, внаслідок чого гравець потрапляє в пастку в деякому неіснуючому просторі "між" ними; відстані дозволу зіткнення завжди обчислюються до пустого простору у світі, а не лише до межі однієї плитки, яка може мати ще одну суцільну плитку поверх неї.


1
i.stack.imgur.com/NLg4j.png - це станеться, якщо я використовую вищевказаний метод.
Вітторіо Ромео

1
Можливо, я неправильно розумію ваш код, але дозвольте мені пояснити проблему на схемі: оскільки гравець швидше на осі X, проникнення X є> ніж Y проникнення. Зіткнення вибирає вісь Y (оскільки проникнення менше) і штовхає гравця вертикально. Це не відбувається, якщо гравець досить повільний, так що проникнення Y завжди більше>, ніж проникнення X.
Вітторіо Ромео

1
@Vee На яку область діаграми ви посилаєтесь тут як на неправильну поведінку, використовуючи такий підхід? Я припускаю панель 5, в якій гравець чітко проникає в X, ніж у Y, що призведе до витіснення вздовж осі X - це правильна поведінка. Погану поведінку ви отримуєте лише в тому випадку, якщо вибираєте вісь для роздільної здатності на основі швидкості (як на вашому зображенні), а не на основі фактичного проникнення (як я запропонував).
Тревор Пауелл

1
@Trevor: Ах, я бачу, що ти кажеш. У такому випадку ви можете просто зробити цеif(fabs( yDistanceToResolve ) < fabs( xDistanceToResolve ) || xDistanceToResolve == 0) { object->Move( 0.f, yDistanceToResolve ); } else { object->Move( xDistanceToResolve, 0.f ); }
BlueRaja - Danny Pflughoeft

1
@BlueRaja Чудова точка. Я відредагував свою відповідь, щоб використовувати менше умовних умов, як ви пропонуєте. Дякую!
Тревор Пауелл

6

Редагувати

Я це покращив

Здається, ключовим, що я змінив, є класифікація перехресть.

Перехрестя є або:

  • Стельові шишки
  • Земляні удари (штовхатися з підлоги)
  • Зіткнення стіни на повітрі
  • Зіткнення заземлених стін

і я вирішую їх у тому порядку

Визначте зіткнення з землею, як гравець, що знаходиться як мінімум на 1/4 шляху на плитці

Отже, це зіткнення з землею, і гравець ( синій ) буде сидіти на плитці ( чорний ) зіткнення землі

Але це НЕ земне зіткнення, і гравець буде «ковзати» з правого боку плитки, коли він приземлиться на неї не гоніг бути наземним зіткненням гравець не проскочить

За допомогою цього методу гравець більше не буде потрапляти на сторони стін


Я спробував ваш метод (див. Мою реалізацію: pastebin.com/HarnNnnp ), але я не знаю, де встановити швидкість гравця на 0. Я намагався встановити його на 0, якщо encrY <= 0, але це дозволяє гравцеві зупинятися на повітрі при цьому ковзаючи по стіні, а також дозволяє йому неодноразово стрибати по стіні.
Вітторіо Ромео

Однак це працює правильно, коли гравець взаємодіє зі шипами (як показано на .gif). Я дуже вдячний, якщо ви можете допомогти мені вирішити проблему стрибків у стіну (також застрягаючи в стінах). Майте на увазі, що мої стіни зроблені з декількох плиток AABB.
Вітторіо Ромео

Вибачте за публікацію ще одного коментаря, але після копіювання вставлення коду в абсолютно новий проект, зміна sp на 5 та надання плитки нереститься у формі стіни, трапиться помилка (див. Цю схему: i.stack.imgur.com /NLg4j.png)
Вітторіо Ромео

Гаразд, спробуйте зараз.
bobobobo

+1 для зразка робочого коду (хоча відсутні горизонтально рухомі блоки "шипа"),
Лефтій,

3

Примітка:
Мій двигун використовує клас виявлення зіткнень, який перевіряє зіткнення з усіма об'єктами в режимі реального часу, лише коли об’єкт рухається, і лише в квадрати сітки, які він зараз займає.

Моє рішення:

Коли я зіткнувся з подібними проблемами у своєму 2d платформері, я зробив щось подібне:

- (двигун швидко виявляє можливі зіткнення)
- (гра інформує двигун, щоб перевірити точні зіткнення для конкретного об'єкта) [повертає вектор об'єкта *]
- (об'єкт дивиться на глибину проникнення, а також попереднє положення відносно попереднього положення іншого об'єкта, визначити, з якої сторони сповзати)
- (об'єкт рухається і усереднює його швидкість зі швидкістю об'єкта (якщо об'єкт рухається))


"(об'єкт розглядає глибину проникнення, а також попереднє положення відносно попереднього положення іншого об'єкта, щоб визначити, з якої сторони вислизнути)" це ця частина, що мене цікавить. Чи можете ви детальніше розглянути? Чи вдається це в ситуації, показаній .gif і діаграмою?
Вітторіо Ромео

Я ще не знайшов ситуації (окрім високих швидкостей), що цей метод не працює.
ultifinitus

Звучить добре, але чи можете ви насправді пояснити, як ви вирішуєте проникнення? Ви вирішуєте завжди на найменшій осі? Ви перевіряєте сторону зіткнення?
Вітторіо Ромео

Насправді, менша вісь - це не гарний (первинний) шлях. ІМХО. Я зазвичай вирішую, виходячи з того, яка сторона раніше була поза стороною іншого об'єкта, це найпоширеніша. Коли це не вдається, я перевіряю, виходячи зі швидкості та глибини.
ultifinitus

2

У відеоіграх я запрограмував підхід, щоб він мав функцію, яка визначає, чи правильна ваша позиція, тобто bool ValidPos (int x, int y, int wi, int he);

Функція перевіряє обмежувальне поле (x, y, wi, he) на всю геометрію в грі і повертає помилкове, якщо є перетин.

Якщо ви хочете рухатись, скажіть праворуч, займіть позицію гравця, додайте 4 до х і перевірте, чи позиція є дійсною, якщо ні, перевірте за допомогою клавіш + 3, + 2 тощо, поки вона не стане.

Якщо вам також потрібна гравітація, вам потрібна змінна, яка зростає до тих пір, поки ви не вдаритесь об землю (ударивши об землю: ValidPos (x, y + 1, wi, he) == true, y тут позитивний вниз). якщо ви можете перемістити цю відстань (наприклад, ValidPos (x, y + сила тяжіння, wi, he) повертає істину) ви падаєте, корисні іноді, коли вам не вдасться керувати вашим персонажем при падінні.

Тепер ваша проблема полягає в тому, що у вас є предмети у вашому світі, які рухаються так, перш за все, вам потрібно перевірити, чи ваша стара позиція все-таки дійсна!

Якщо це не так, потрібно знайти таку позицію. Якщо об'єкти ingame не можуть рухатись швидше, ніж скажімо, 2 пікселі за ігровий оборот, вам слід перевірити, чи позиція (x, y-1) є дійсною, тоді (x, y-2), тоді (x + 1, y) і т.д. і т.д. весь простір між (x-2, y-2) до (x + 2, y + 2) повинен бути перевірений. Якщо немає дійсної позиції, то це означає, що вас "розчавили".

HTH

Валмонд


Раніше я використовував такий підхід ще в свої дні в Game Maker. Я можу придумати шляхи його прискорити / покращити за допомогою кращих пошуків (скажімо, бінарний пошук на пройденому вами просторі, хоча залежно від відстані, яку ви пройшли, та геометрії, можливо, повільніше), але головним недоліком такого підходу є що замість того, щоб сказати одну перевірку проти всіх можливих зіткнень, ви вирішуєте будь-яку зміну позиції - це перевірки.
Джефф

"Ви займаєтесь тим, що змінити позицію - це чеки" Вибачте, але що саме означає, ніж означає? BTW звичайно, ви будете використовувати методи розділення простору (BSP, Octree, quadtree, Tilemap тощо), щоб пришвидшити гру, але вам це все одно потрібно робити (якщо карта велика), питання не в тому, але про алгоритм, що використовується для переміщення (правильно) гравця.
Валмонд

@Valmond Я думаю, що @Jeff означає, що якщо у гравця рухається 10 пікселів, у вас може бути до 10 різних виявлень зіткнень.
Джонатан Коннелл

Якщо це те, що він має на увазі, то він має рацію, і це має бути саме так (хто може інакше сказати, якщо ми зупинимось на +6, а не +7?). Комп'ютери швидкі, я використовував це на S40 (~ 200 МГц і не такий швидкий кілометр), і він працював як шарм. Ви завжди можете спробувати оптимізувати початковий альго, але це завжди надасть вам кутові випадки, такі, як у ОП.
Валмонд

Саме це я і мав на увазі
Jeff

2

У мене є кілька питань, перш ніж почати відповідати на це. По-перше, в оригінальній помилку, в яку ви застрягли в стінах, були ті плитки зліва окремі плитки на відміну від однієї великої плитки? І якщо вони були, чи гравець застряг між ними? Якщо так, обидва ці питання, просто переконайтеся, що ваша нова посада є дійсною . Це означає, що вам доведеться перевірити, чи є зіткнення, куди ви говорите гравцеві рухатися. Тож вирішіть мінімальну величину переміщення, як описано нижче, а потім перемістіть програвача на основі цього, лише якщо він зможепереїхати туди. Майже занадто під носом: P Це фактично введе ще одну помилку, яку я називаю «кутовими справами». По суті, з точки зору кутів (наприклад, внизу зліва, де горизонтальні шипи виходять у вашому .gif, але якби не було шипів), це не вирішило зіткнення, оскільки було б думати, що жодне з створених вами резолюцій не призводить до правильної позиції . Щоб вирішити це, просто продовжуйте вирішувати питання про те, чи зіткнення було вирішено, а також список усіх мінімальних дозволів проникнення. Згодом, якщо зіткнення не було вирішено, переведіть цикл на кожну створену вами роздільну здатність і слідкуйте за максимальною роздільною здатністю X і максимальною Y (максимуми не повинні виходити з тієї ж роздільної здатності). Потім вирішіть зіткнення на цих максимумах. Це, здається, вирішує всі ваші проблеми, а також ті, з якими я стикався.

    List<Vector2> collisions = new List<Vector2>();
        bool resolved = false;
        foreach (Platform p in testPlat)
        {
            Vector2 dif = p.resolveCollision(player.getCollisionMask());

            RectangleF newPos = player.getCollisionMask();

            newPos.X -= dif.X;
            newPos.Y -= dif.Y;


            if (!PlatformCollision(newPos)) //This checks if there's a collision (ie if we're entering an invalid space)
            {
                if (dif.X != 0)
                    player.velocity.X = 0; //Do whatever you want here, I like to stop my player on collisions
                if (dif.Y != 0)
                    player.velocity.Y = 0;

                player.MoveY(-dif.Y);
                player.MoveX(-dif.X);
                resolved = true;
            }
            collisions.Add(dif);
        }

        if (!resolved)
        {
            Vector2 max = Vector2.Zero;

            foreach (Vector2 v in collisions)
            {
                if (Math.Abs(v.X) > Math.Abs(max.X))
                {
                    max.X = v.X;
                }
                if (Math.Abs(v.Y) > Math.Abs(max.Y))
                {
                    max.Y = v.Y;
                }
            }

            player.MoveY(-max.Y);

            if (max.Y != 0)
                player.velocity.Y = 0;
            player.MoveX(-max.X);

            if (max.X != 0)
                player.velocity.X = 0;
        }

Інше питання - це шипи, на яких ви показуєте одну плитку, або окремі плитки? Якщо це окремі тонкі плитки, можливо, доведеться використовувати інший підхід для горизонтальних і вертикальних, ніж те, що я описую нижче. Але якщо вони цілі плитки, це має працювати.


Гаразд, так в основному саме це і описав @Trevor Powell. Оскільки ви використовуєте лише AABB, все, що вам потрібно зробити, - це знайти, скільки один прямокутник проникає в інший. Це дасть вам величину в осі X і Y. Виберіть мінімум з двох і перемістіть ваш об'єкт, що стикається, по цій осі на цю суму. Це все, що потрібно для вирішення зіткнення AABB. В такому зіткненні НІКОЛИ не потрібно рухатись по одній осі, тому ніколи не слід плутати те, що рухатись спочатку, оскільки ви будете рухатись лише мінімум.

Програмне забезпечення Metanet має класичний підручник з підходу тут . Це також переходить в інші форми.

Ось функція XNA, яку я зробив для пошуку вектора перекриття двох прямокутників:

    public Point resolveCollision(Rectangle otherRect)
    {
        if (!isCollision(otherRect))
        {
            return Point.Zero;
        }

        int minOtherX = otherRect.X;
        int maxOtherX = otherRect.X + otherRect.Width;

        int minMyX = collisionMask.X;
        int maxMyX = collisionMask.X + collisionMask.Width;

        int minOtherY = otherRect.Y;
        int maxOtherY = otherRect.Y + otherRect.Height;

        int minMyY = collisionMask.Y;
        int maxMyY = collisionMask.Y + collisionMask.Height;

        int dx, dy;

        if (maxOtherX - minMyX < maxMyX - minOtherX)
        {
            dx = (maxOtherX - minMyX);
        }
        else
        {
            dx = -(maxMyX - minOtherX);
        }

        if (maxOtherY - minMyY < maxMyY - minOtherY)
        {
            dy = (maxOtherY - minMyY);
        }
        else
        {
            dy = -(maxMyY - minOtherY);
        }

        if (Math.Abs(dx) < Math.Abs(dy))
            return new Point(dx, 0);
        else
            return new Point(0, dy);
    }

(Я сподіваюся, що це просто прослідкувати, тому що я впевнений, що існують кращі способи його реалізації ...)

isCollision (Прямокутник) - це буквально лише дзвінок до прямокутника XNA. Інтерсекти (Прямокутник).

Я тестував це на платформах, що рухаються, і, здається, працює добре. Я зроблю ще кілька тестів, схожих на ваш .gif, щоб переконатися і повідомити, якщо це не працює.



Я якось скептично ставлюсь до цього коментаря, який я висловив щодо нього, не працюючи з тонкими платформами. Я не можу придумати причину, чому б цього не сталося. Також якщо ви хочете джерела, я можу завантажити для вас проект XNA
Jeff

Так, я тільки тестував це ще трохи, і це повертається до вашої тієї ж проблеми, що застрягла в стіні. Це тому, що там, де дві плитки вишикуються одна з одною, вона говорить про те, щоб рухатись вгору через нижню, а потім виходити (прямо у вашому випадку) для верхньої, фактично заперечуючи рух сили тяжіння, якщо швидкість у вас досить повільна . Мені доведеться шукати рішення для цього, я, мабуть, пам’ятаю, що мав цю проблему раніше.
Джефф

Гаразд, я придумав надзвичайно прискіпливий спосіб подолати вашу першу проблему. Рішення передбачає пошук мінімального проникнення для кожного AABB, вирішення лише на одному з найбільшим X, потім другий прохід, який вирішується лише на найбільшому Y. Це погано, коли вас розчавлюють, але підйомники працюють і вас можуть штовхнути горизонтально, і Вашої оригінальної проблеми з наклеюванням вже немає. Це хакей, і я поняття не маю, як це перетвориться на інші форми. Іншою можливістю може бути зварювання зворушливих геометрій, але я навіть цього не робив
Джефф

Стіни виконані з плитки 16х16. Шипи - це плитка 16х16. Якщо я вирішую на мінімальній осі, виникає стрибковий помилка (дивіться на схему). Чи допоможе ваше "надзвичайно хакітне" рішення ситуацією, зображеною у .gif?
Вітторіо Ромео

Так, працює в шахті. Який розмір вашого персонажа?
Джефф

1

Це просто.

Перепишіть колоски. Це вини шипів.

Ваші зіткнення повинні відбуватися в дискретних, відчутних одиницях. Наскільки я бачу, проблема:

1. Player is outside spikes
2. Spikes move into intersection position with the player
3. Player resolves incorrectly

Проблема полягає в кроці 2, а не 3 !!

Якщо ви намагаєтеся зробити так, щоб речі відчували себе твердими, ви не повинні дозволяти предметам ковзати один одному. Опинившись на перехресті, якщо ви втратите своє місце, проблему стає важче вирішити.

Шипи в ідеалі повинні перевіряти наявність гравця, і, коли вони рухаються, вони повинні натискати на нього за необхідності.

Найпростіший спосіб досягти цього, щоб гравець мав moveXта moveYфункціонував, які розуміють ландшафт і заштовхуватимуть гравця певною дельтою або наскільки це можливо, не потрапляючи на перешкоду .

Зазвичай вони будуть викликатися циклом подій. Однак вони також можуть викликати об'єкти, щоб підштовхнути програвача.

function spikes going up:

    move spikes up

    if intersecting player:
        d = player bottom edge + spikes top edge

        player.moveY(-d)
    end if

end function

Очевидно, що гравцеві все ж потрібно реагувати на стрибки, якщо він заходить у них .

Загальне правило - після переміщення будь-якого об'єкта воно повинно закінчуватися в положенні без перетину. Таким чином неможливо отримати проблеми, які ви бачите.


в крайньому випадку, коли moveYне вдасться зняти перехрестя (оскільки перешкоджає інша стіна), ви можете знову перевірити перехрестя і спробувати moveX або просто вбити його.
Кріс Берт-Браун
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.