Як я не дозволяю персонажу мого платформера чіплятись на стінові плитки?


9

В даний час у мене є платформер з плитками для місцевості (графіка, запозичена у Cave Story). Гра написана з нуля за допомогою XNA, тому я не використовую існуючий двигун чи фізику.

Зіткнення плиток описані майже точно так, як описано в цій відповіді (з простим SAT для прямокутників і кіл), і все працює добре.

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

Зображення лову персонажа

На цьому скріншоті гравець рухається праворуч і падає вниз. Тож після руху перевіряються зіткнення - і по-перше, виявляється, персонаж гравця зіштовхується з плиткою 3-ї з підлоги і висувається вгору. По-друге, він виявив, що він зіштовхується з плиткою біля себе, і штовхнувся вбік - кінцевим результатом є те, що персонаж гравця думає, що він на землі і не падає, і «ловить» на плитці до тих пір, поки він натикається на неї .

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

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


Подібне до цього питання? gamedev.stackexchange.com/questions/14486 / ...
MichaelHouse

@ Byte56 Питання не те саме, але його відповідь може допомогти.
doppelgreener

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

Відповіді:


8

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

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

Є більш вдосконалені версії цієї самої техніки, які полегшують і швидше підтримувати неквадратичну плитку.

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

http://www.metanetsoftware.com/technique/tutorialA.html http://www.metanetsoftware.com/technique/tutorialB.html


Чи можете ви уточнити, як ігнорувати деякі краї під час зіткнення? Зазвичай я бачу, як зіткнення виявляються як перетин двох прямокутників (межі гравця та межі плитки). Якщо ви скажете не перевіряти зіткнення проти окремих ребер, чи означає це, що алгоритм зіткнення - це не перетин прямокутника-прямокутника, а щось інше? Ваш опис має сенс, але я не впевнений, як виглядатиме реалізація.
Девід Гувейя

Так, просто зробіть зіткнення прямокутника. Спрощено лише тому, що лінія завжди вирівняна по осі. Ви можете просто розкласти свій чекбокс.
Шон Міддлічч

5

Ось наказ я б робив ...

1) Збережіть поточний X, Y символів

2) Перемістіться в напрямку X

3) Перевірте весь куточок символу, отримавши дані плитки та перевірте, чи він твердий, виконавши наступне: (X і Y - позиція символів)

  • верхній лівий: підлога (X / плиткаШирина), підлога (Y / плиткаВисота);
  • праворуч вгорі: підлога ((X + символ ширина) / плитка ширина), підлога (Y / tileHeight);
  • ліворуч внизу: підлога (X + / плитка ширина), підлога ((Y + символ висота) / плитка висота);
  • праворуч внизу: підлога ((X + символ ширина) / плитка ширина), підлога ((Y + символ висота) / плитка висота);

... для отримання правильних даних плиток із даних карт

4) Якщо будь-яка з плиток є твердою, то вам потрібно примусити X у найкращому положенні, відновивши збережені X і ...

  • якщо ви рухаєтесь ліворуч: X = підлога (X / tileWidth) * tileWidth;
  • якщо ви куди рухаєтесь праворуч: X = ((пол ((X + символ ширина) / плитка ширина) + 1) * плитка ширина) - символ ширина;

5) Повторіть 2-4, використовуючи Y

Якщо ваш персонаж вище, ніж плитка, вам потрібно перевірити більше плиток. Для цього вам потрібно зафіксувати цикл та додати плитку висоту до позиції символу Y, щоб отримати плитку

int characterBlockHeight = 3;

// check all tiles and intermediate tiles down the character body
for (int y = 0; y < characterBlockHeight - 1; y++) {
    tile = getTile(floor(characterX / tileWidth), floor((characterY + (y * tileHeight)) / tileHeight));
    ... check tile OK....
}

// finally check bottom
tile = getTile(floor(characterX / tileWidth), floor((characterY + characterHeight) / tileHeight);
... check tile OK....

Якщо символ ширший за 1 блок, то зробіть те саме, але замініть знак Y, символ висоту, плитку висоти тощо тощо символомX, шириною символу тощо


2

Що робити, якщо ви перевірили можливість зіткнення в рамках методу руху гравця та відхилили рух, через який гравець ковзав до іншого об’єкта? Це не дозволить гравцеві потрапити «всередину» блоку, і тому він не може бути «на вершині» блоку під ним.


1

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

У вашому прикладі площа зіткнення осі x була б більшою, ніж вісь y, тому вона буде оброблена спочатку. Тоді, перш ніж намагатися керувати зіткненням по осі y, він перевірить і побачить, що він більше не стикається після переміщення по осі x. :)


1

Просте, але не ідеальне виправлення - змінити форму коробки зіткнення гравця, щоб вона не була площею. Відріжте кути, щоб зробити його схожим на восьмикутник чи щось подібне. Таким чином, коли він зіткнеться з плиткою, як ті, що є у стіні, він сповзатиме з неї і не потрапить. Це не ідеально, тому що ви все ще можете помітити, як він трохи попадає на кути, але він не застрягне, і це дуже просто здійснити.


1

Дуже схожа проблема була висвітлена в цьому підручнику: Вступ до розробки ігор . Відповідна частина відео о 1:44 , пояснюючи діаграмами та фрагментами коду.

Повідомлення 14-tilemap.py демонструє проблему відсікання, але вона фіксується у 15-blocker-sides.py

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

  • Умовна реакція на зіткнення залежно від типу плитки (ребра яких фактично активні.)
  • Гравець завжди "падає". Хоча гравець, здається, лежить на плитці, гравець насправді неодноразово провалюється через плитку, потім відштовхується доверху. (Учасник self.restingцього коду використовується лише для перевірки, чи може гравець стрибати. Незважаючи на це, я не можу змусити гравця зробити «подвійний» стрибок зі стіни. Однак теоретично це можливо)

0

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

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


0

Я тільки зробив 3 SAT в 3D, так що, можливо, я не отримую цього правильно. Якщо я розумію вашу проблему, це може бути пов’язане з налагодженням контактів з контактною віссю, яка не може бути можливою, внаслідок чого гравець діє так, як там є підлога. Можливо, одним із варіантів вирішення може бути використання форми кола для вашого персонажа, тоді ви будете отримувати контактні осі лише в напрямку осі x, коли вдаряєтеся про стіну.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.