Создание Plattformer - Как определить, разрешено ли игроку прыгать?

16

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

Далее я хотел реализовать прыжки. Просто проверяйте, нажимает ли игрок соответствующую клавишу и добавляют ли скорость вверх. Работает отлично. Но это работает, даже если игрок находится в воздухе, а это не то, что я хочу. ;-)

Итак, я должен проверить, стоит ли игрок на чем-то. Моя первая идея состояла в том, чтобы проверить, не было ли столкновения в последнем кадре, и пометить игрока как «способного прыгать», но это даже сработало бы, если бы игрок столкнулся со стеной в воздухе. Поскольку мои математические навыки не так хороши, я прошу помощи - даже советы помогут сделать это.

Благодарность!

Malax
источник

Ответы:

14

На ум приходят два варианта:

  • Сначала нужно пометить геометрию идентификатором, а затем проверить, не происходит ли столкновение с геометрией, помеченной как пол. Это обеспечивает максимальный контроль над прыжковыми поверхностями, но затрачивает время на создание уровня.
  • Во-вторых, проверить нормальную коллизию, если она направлена ​​вверх, то разрешить прыжок. Или в пределах некоторого запаса, зависит от того, есть ли у вас наклонные этажи. Это гибкий и не требует каких-либо тегов, но если у вас наклонные полы и стены, вы можете получить прыжок там, где вы этого не хотите.
wkerslake
источник
Нормальная проверка кажется мне отличной идеей. Спасибо за публикацию этого.
Кристофер Хоренштейн
+1 для нормальной проверки. Это гарантирует, что пол может легко переходить в стену, не вызывая каких-либо проблем.
Совют
1
+1 Обязательно проверяй столкновение-нормально. Наличие разных типов геометрии мира прекрасно и позволяет более гибко проектировать уровни, но не решает вашу главную проблему. «Стена» также может иметь тип «земля», в зависимости от того, сталкиваетесь ли вы с ней или стоите на ней. Вот где нормальное столкновение поможет.
Bummzack
Я нахожу нормальное решение очень хорошим и решил бы мою проблему. Даже другой высококлассный ответ хорош и такой - но это отвечает на мой вопрос лучше. Спасибо, ваш wkerslake!
Малакс
8

Вы обязательно должны реализовать какой-то тип поверхности. Подумайте об этом, как вы справитесь, если сможете подняться по лестнице, если не знаете, столкнулся ли ваш персонаж со стеной или лестницей? Вы можете просто использовать ООП для управления иерархией типов, используя наследие, но я бы предложил вам использовать «категории», реализованные с использованием перечислимого типа:

Вот идея: перечисление «Столкновения» имеет флаг для каждой категории. Например:

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
    };
}

С помощью этого метода вы сможете проверить, сталкивался ли игрок с чем-либо, с чем вы должны управлять, поэтому ваш движок может вызвать «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
   }
}

В этом методе используются побитовые флаги и побитовый оператор «ИЛИ», чтобы гарантировать, что каждая группа имеет различное значение в зависимости от двоичного значения категории. Этот метод отлично работает и легко масштабируется, поэтому вы можете создавать таможенные коллизионные группы. Каждый объект (игрок, враг и т. Д.) В вашей игре имеет несколько битов, называемых «фильтром», которые используются для определения того, с чем он может столкнуться. Ваш код столкновения должен проверить, соответствуют ли биты и реагируют ли они соответствующим образом, с некоторым кодом, который может выглядеть так:

void PhysicEngine::OnCollision(...)
{
    mPhysics.AddContact( body1, body1.GetFilter(), body2, body2.GetFilter() );
}
Фредерик Имбо
источник
Есть ли причина использовать константы вместо перечисления? В вырожденном случае одного флага или именованной группы перечисляемый тип более читабелен, и вы получаете дополнительные возможности проверки типов в большинстве компиляторов.
Ну да, перечисление не может (я думаю) использовать бинарные операторы для создания новых пользовательских групп. Причина, по которой я сделал эти утверждения постоянными, заключается в том, что я использую класс Config с открытыми статическими членами для удобства чтения, сохраняя безопасность параметров конфигурации.
Фредерик Имбо
Он может. Перечисление "rvalues" может быть побитовой операцией над другими константными значениями (я думаю, что это может быть любое константное выражение на самом деле, но мне лень проверять стандарт). Нестатические значения достигаются точно так же, как в вашем примере, но более точно набираются. Я понятия не имею, что вы подразумеваете под «безопасностью», но тот же самый синтаксис без издержек, связанных с классом, может быть достигнут с пространством имен.
Я обновил ваши примеры кода, чтобы использовать перечислимый тип.
Может быть, это делает мысли легче читать. Я одобряю внесенные вами изменения, метод, который я использовал, был правильным, но я не корректировал код для объяснения, спасибо за исправления. Что касается пространства имен, вы совершенно правы, но это относится не ко всем языкам (например, языки сценариев). «Безопасность» заключается в том, что мои параметры конфигурации являются статическими, поэтому их нельзя изменить, но использование перечисления предотвращает это.
Фредерик Имбо
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
Я пересмотрел код, чтобы показать, как абстрагировать вектор от ноги до центра персонажа. Если вы оживите это, и ноги окажутся вбок и столкнутся со стеной, вы сможете поддержать прыжок со стены.
jpaver
Это в основном менее гибкий способ проверки нормального столкновения. (Менее гибкий, потому что вы можете тривиально отличить прыжок от стены от прыжка с пола и прыжка с потолка, просто проверив направление нормали.)
2
Никогда не вызывайте переменную char, даже в псевдокоде.
вправо