Advertisement
  1. Game Development
  2. Platformer

Основы физики 2D платформера, часть 3

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Basic 2D Platformer Physics .
Basic 2D Platformer Physics, Part 2
Basic 2D Platformer Physics, Part 4

Russian (Pусский) translation by Alexey Malyutin (you can also view the original English article)

Односторонние платформы

Поскольку мы только что закончили работать над проверкой столкновения с землей, мы можем также добавить односторонние платформы, пока мы все еще здесь. В конце концов они будут касаться только проверки столкновений с поверхностями. Односторонние платформы отличаются от сплошных блоков тем, что они останавливают объект, только когда он падает вниз. В добавок, мы также позволим персонажу сваливаться вниз с таких платформ.

Прежде всего, когда мы хотим спрыгнуть с односторонней платформы, мы по сути хотим игнорировать столкновение с землей. Легким путем к этому является установка смещения, после прохождения которого персонаж или объект больше не сталкивается с платформой.

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

Теперь давайте добавим переменную, которая позволит нам знать, находится ли в данный момент объект на односторонней платформе.

Давайте изменим определение функции HasGround, чтобы она брала ссылку на булеву переменную, которая будет установлена, если объект приземлился на одностороннюю платформу.

Теперь, после того, как мы проверили, является ли тайл, под которым мы находимся, препятствием, и определили, что нет, мы должны проверить, не является ли он односторонней платформой.

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

Конечно, мы не можем просто сравнить разницу между верхом тайла и сенсором, потому что легко представить, что даже если мы падаем, мы можем переместиться гораздо ниже двух пикселей под верхом платформы. Для того, чтобы односторонние платформы останавливали объект, нам нужно, чтобы расстояние между верхним краем тайла и сенсором была меньше или равна cOneWayPlatformThreshold плюс смещение от положения в этом кадре до положения в предыдущем кадре.

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

На самом деле мы не должны рассматривать такую позицию, как "одностороннюю платформу", потому что мы не можем отсюда впрыгнуть вниз - нас останавливает сплошной блок. Вот почему мы сначала должны продолжить искать сплошной блок, и если таковой найдется до того, как мы вернем результат, на также нужно будет установить onOneWayPlatform в ложь.

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

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

Давайте посмотрим, как он работает.

Все работает правильно.

Управление столкновениями для потолка

Нам нужно создать функцию, аналогичную HasGround для каждой стороны AABB, поэтому давайте начнем с потолка. Различия будут следующие:

  • Линия сенсора находится над AABB вместо того, чтобы быть снизу
  • Мы проверяем тайл потолка снизу вверх, по мере того, как двигаемся вниз
  • Не нужно поддерживать односторонние платформы

Вот измененная функция.

Поддержка столкновений для левой стены

Аналогично тому, как мы сделали поддержку проверки столкновений для потолка и земли, нам также необходимо проверять, не столкнулся ли объект со стеной слева или справа. Давайте начнем с левой стены. Идея здесь практически такая же, но есть несколько отличий:

  • Сенсорная линия находится на левом краю AABB.
  • Внутренний цикл for должен обойти тайлы по вертикали, потому что теперь сенсор является вертикальной линией.
  • Внешний цикл должен обойти тайлы горизонтально, чтобы посмотреть, не пропустили ли мы стену при движении с большой горизонтальной скоростью.

Поддержка столкновений с правой стеной

Наконец, давайте создадим функцию CollidesWithRightWall, которая, как вы догадываетесь будет делать практически то же самое, что и CollidesWithLeftWall, но вместо использования левого сенсора, мы используем сенсор с правого края персонажа.

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

Перемещение объекта из состояния столкновения

Все наши функции обнаружения столкновений созданы, поэтому давайте воспользуемся ими для завершения реакции на столкновения с тайлами карты. Однако перед тем, как мы сделаем это, нам нужно выяснить порядок, в котором мы будем проверять столкновения. Давайте рассмотрим следующие ситуации.

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

Ситуация слева достаточно простая - мы видим, что мы падаем прямо вниз и поэтому мы определенно должны приземлиться на верхнюю грань блока.

Ситуация справа немного более затруднительная, так как по правде говоря мы можем приземлиться на самый уголок тайла и оттолкнуть персонажа вверх будет также логично, как и оттолкнуть его вправо. Давайте выберем приоритет горизонтального движения. На самом деле не так важно, какое выравнивание мы захотим делать в первую очередь; оба варианта в действии выглядят корректно.

Давайте отправимся в нашу функцию UpdatePhysics и добавим переменные,которые будут содержать результаты наших запросов на столкновение.

Теперь давайте начнем с проверки, должны ли мы двигать объект направо. Условия здесь вот такие:

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

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

Если условия выполнены, нам нужно выровнять левый край нашего AABB по правому краю тайла, убедиться, что мы перестали двигаться налево, и отметить, что мы рядом со стеной слева.

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

Теперь давайте проверим столкновение с правой стеной.

Как видите, это та же формула, которую мы использовали для проверки столкновения с левой стеной, но зеркально отраженная.

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

Скругляем углы

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

Сначала, для нашего удобства, давайте создадим функцию, которая трансформирует вектор из чисел с плавающей запятой в вектор из округленных чисел с плавающей запятой.

Теперь давайте используем эту функцию в каждой проверке столкновения. Сначала давайте исправим функцию HasCeiling.

Следующая будет OnGround.

PushesRightWall.

И наконец, PushesLeftWall.

Это должно решить наши проблемы!

Проверим результаты

Это будет самое оно. Давайте проверим, как наши столкновения работают теперь.

Заключение

Вот и все с этой частью! У нас теперь есть полностью рабочий набор столкновений с тайлами карты, который должен быть очень надежным. Мы знаем, в каком состоянии позиции находится объект в данный момент времени: стоит ли он на твердой поверхности, касается тайла слева или справа, или наткнулся на потолок. Мы также реализовали поддержку односторонних платформ, которые являются очень важным инструментом в каждом платформере.

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

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.