Побудова Платформера - Як визначити, чи дозволено гравцю стрибати?


16

Я будую просту гру Plattformer Jump n 'Run Style. Я не використовую плитки - натомість у мене є геометричні фігури для об'єктів мого рівня (і плеєр теж один). Я закінчив свій код виявлення зіткнень і до цього часу все працює добре.

Далі я хотів здійснити стрибки. Просто перевірте, чи гравець натискає відповідну клавішу, і додайте деяку швидкість вгору. Добре працює. Але це працює, навіть якщо гравець знаходиться в повітрі, а це не те, що я хочу. ;-)

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

Спасибі!

Відповіді:


14

Два варіанти пам’ятають:

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

Звичайна перевірка здається мені чудовою ідеєю. Дякуємо, що опублікували це.
Крістофер Хоренштейн

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

1
+1 Однозначно перевірте нормальність зіткнення. Наявність різних типів світової геометрії - це чудово і дозволяє створити гнучкіший дизайн рівня, але це не вирішить вас першочерговим питанням. "Стіна" також може бути типу "земля", залежно від того, ви натикаєтесь на неї чи стоїте на ній. Тут допоможе нормальне зіткнення.
bummzack

Я вважаю нормальне рішення дуже приємним і вирішило б моє питання. Навіть інша високо оцінена відповідь є приємною і такою - але це краще відповідає на моє запитання. Дякую вашому wkerslake!
Малакс

8

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

Ось ідея: Перелік "Зіткнень" містить прапор для кожної категорії. Наприклад:

namespace Collisions
{
    enum Type
    {
        None   = 0,
        Floor  = 1 << 0,
        Ladder = 1 << 1,
        Enemy  = 1 << 2,
        ... // And whatever else you need.

        // Then, you can construct named groups of flags.
        Player = Floor | Ladder | Enemy
    };
}

За допомогою цього методу ви зможете перевірити, чи гравець juste зіткнувся з чим-небудь, чим вам слід керувати, щоб ваш двигун міг викликати метод "Collided" суб'єкта:

void Player::Collided( Collisions::Type group )
{
   if ( group & Collisions::Ladder )
   {
      // Manage Ladder Collision
   }
   if ( group & Collisions::Floor )
   {
      // Manage Floor Collision
   }
   if ( group & Collisions::Enemy )
   {
      // Manage Enemy Collision
   }
}

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

void PhysicEngine::OnCollision(...)
{
    mPhysics.AddContact( body1, body1.GetFilter(), body2, body2.GetFilter() );
}

Чи є причина використовувати const ints замість enum? У виродженому випадку одного прапора або названої групи перелічений тип є більш читабельним, і ви отримуєте додаткові можливості перевірки типу у більшості компіляторів.

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

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

Я оновив ваші приклади коду, щоб використовувати перелічений тип.

Можливо, це робить думки легше читати. Я переосмислюю внесені вами зміни, метод, який я використав, був правильним, але я не скоригував код для пояснення enouph, дякую за виправлення. Що стосується простору імен, ви цілком праві, але це не стосується всіх мов (мови сценаріїв для зразка). "Безпека" полягає в тому, що мої параметри конфігурації є статичними, тому їх неможливо змінити, але використання enum також запобігає цьому.
Фредерік Імбео,

1

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

Приблизно псевдо-код:

bool isOnGround(Character& chr)
{
   // down vector is opposite from your characters current up vector.
   // if you want to support wall jumps, animate the vecToFoot as appropriate.
   vec vecToFoot = -chr.up * chr.footDistanceFromCentre;

// if feet are moving away from any surface, can't be on the ground if (dot(chr.velocity, down) < 0.0f) return false;

// generate line from character centre to the foot position vec linePos0 = chr.position; vec linePos1 = chr.position + vecToFoot;

// test line against world. If it returns a hit surface, we're on the ground if (testLineAgainstWorld(line0, line1, &surface) return true;

return false; }


Це не спрацює, якщо ви хочете додати такі функції, як подвійні стрибки або настінні стрибки?
Фредерік Імбео

Я не вірю, що в ОП згадувалися подвійні стрибки або стрибки на стіні, чи не так?
jpaver

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

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

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