Pull to refresh

Путеводитель по реализации 2Д платформеров (начало)

Reading time10 min
Views48K
Original author: Rodrigo Monteiro
Так как ранее я был разочарован количеством информации по этому вопросу, я решил восполнить этот пробел, собрав разные виды реализации 2Д платформеров, описав их сильные и слабые стороны и порассуждав над деталями реализации.

Моей целью было создать исчерпывающий и понятный путеводитель по реализации 2Д платформеров.



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

Четыре решения


Мне приходит на ум четыре основных варианта решений при создании платформера. В рамках этой статьи будут рассмотрены все четыре, но из-за большого объема статья поделена на 2 части (прим. пер).

От простого к наиболее сложному это:

Тип №1: Тайловый (чистый)


В нём позиционирование персонажа ограничено сеткой тайлов, таким образом, он никогда не сможет встать между двумя тайлами. Для создания иллюзии плавного передвижения могут быть использованы различные анимации, однако, согласно игровой логике, персонаж всегда находится прямо на конкретном тайле. Это самый простой способ сделать платформер, однако он накладывает большие ограничения на контроль персонажа, делая его неприемлемым для традиционных экшн-платформеров. Тем не менее, он очень популярен для паззлов и «кинематографичных» платформеров.

Flashback, сетка тайлов
Примеры: Prince of Persia, Toki Tori, Lode Runner, Flashback

Как оно работает
Карта — это сетка тайлов, каждый из которых содержит информацию о свойствах тайла: препятствие это или нет, какое изображение использовать, какие проигрывать звуки шагов и так далее. А игрок и другие персонажи представлены набором из одного или нескольких тайлов, движущихся вместе. Например, в Lode Runner игрок является одним тайлом. В Toki Tori же игрок 2х2 тайла. А в игре Flashback, которая довольно необычна из-за небольшого размера сетки тайлов, игрок имеет габариты в два тайла в ширину и пять в высоту (см. изображение выше) когда стоит, но всего три тайла в высоту, когда пригибается.
В играх такого типа, персонаж обычно не двигается по диагонали, а если и двигается, то, движение можно разложить на два отдельных шага. Движение на несколько тайлов можно сделать множественными сдвигами одиночных тайлов, если нужно (во Flashback игрок всегда движется по два тайла за раз). Складывается следующий алгоритм:
  1. Создать копию персонажа там, где он должен оказаться (т.е. если нужно сдвинуться на 1 тайл вправо, нужно сделать копию, где каждый тайл персонажа сдвинут на один тайл вправо)
  2. Проверить эту копию на пересечение с фоном и другими персонажами
  3. Если найдено пересечение, то передвижение персонажа заблокировано. Нужно отреагировать соответствующим образом.
  4. В противном случае путь чист. Передвигайте персонажа сюда, воспроизводя анимацию, если необходимо, чтобы перемещение выглядело плавным.


Этот тип движения совершенно непригоден для обычных прыжков «по дуге», поэтому игры такого жанра вовсе лишены прыжков (Toki Tori, Lode Runner) или разрешают только горизонтальные или только вертикальные прыжки фиксированной длины (Prince of Persia, Flashback), которые есть ни что иное как обычное линейное движение. Преимущества этой системы — простота и точность. Такие игры более детерминированы, что ведет к меньшей вероятности появления глюков и более контролируемому геймплею, не требующему слишком часто подстраивать значения в зависимости от обстоятельств. Фиксированные расстояния для взаимодействия дают возможность сделать красивую бесшовную анимацию. Значительно упрощается реализация некоторых игровых механик (таких как ухват за выступ и односторонние платформы) — всё что нужно сделать, это проверить, удовлетворяют ли тайлы фона в нужной позиции необходимым условиям.
Конечно, эта система не даёт делать шаги менее чем на один тайл, но шаги можно уменьшать разными способами. Например, тайлы могут быть чуть меньше, чем игрок (скажем, игрок 2х6 тайлов), или можно разрешить «только визуальное» движение, чтобы перемещаться внутри выбранного тайла без изменения логики (я полагаю, что это решение применено в «Lode Runner – The Legend Returns»)

Тип №2: Тайловый (Плавный)


Столкновения по прежнему определяются сеткой тайлов, но персонажи могут двигаться в мире свободно (обычно с разрешением в 1 пиксель. см. замечание в конце статьи относительно плавности движения). Это наиболее частая форма реализации платформеров на 8-ми и 16-ти битных консолях, остающаяся популярной и сегодня, так как крайне легка в реализации и позволяет редактировать уровень намного проще чем с применением более сложных техник. Также она позволяет делать уклоны и плавные прыжки по дуге.
Если вы не уверены какой именно платформер хотите сделать, но точно экшн, то я предлагаю остановиться на этом типе. Неудивительно, что подавляющее большинство лучших экшн-платформеров всех времён основываются именно на этом методе.

Mega Man X, с сеткой и прямоугольником персонажа.

Примеры: Super Mario World, Sonic the Hedgehog, Mega Man, Super Metroid, Contra, Metal Slug, и практически любой платформер 16-битной эры

Как это работает
Информация о карте хранится также, как и в чисто тайловом методе. Разница только в том, как персонаж взаимодействует с фоном. Теперь у персонажа есть описывающий его прямоугольник для просчета столкновений (AABB, который не может вращаться), и, обычно, по размеру является кратным размеру тайла. Стандартные размеры вроде одного тайла в ширину и один (маленький Mario, пригнувшаяся Samus), два (большой Mario, Mega Man, пригнувшаяся Samus) или три (стоящая Samus) тайла в высоту. Во многих случаях визуально спрайт персонажа больше, чем логический прямоугольник, так как это делает внешний вид игры более приятным и геймплей — более честным (согласитесь, что для игрока лучше избежать попадания, когда он должен его получить, чем получить когда не должен).
На изображении выше, можно заметить, что спрайт с персонажем «X» квадратный (два тайла шириной), однако описывающий его прямоугольник шириной только в один тайл.
При условии, что нет уклонов и односторонних платформ, алгоритм прост:
  1. Разложить движение на оси X и Y, делать одно перемещение за раз. Если планируется позже добавить уклоны, тогда сначала по X, затем по Y. В противном случае порядок абсолютно не важен. Затем для каждой оси:
    Получить координату грани в направлении движения. Например: если двигаться влево, X координата левой грани описывающего прямоугольника. Если вправо, X координата правой стороны. Если верх, Y координата верха и так далее.
  2. Определить какие линии тайлов пересекаются с описывающим прямоугольником — это даст минимальное и максимальное значение тайла на ДРУГОЙ оси. Например, если мы движемся влево, предположим игрок пересекается с горизонтальными линиями 32, 33 и 34 (вот оно, тайлы с Y = 32 * TS, Y = 33 * TS и Y = 34 * TS, где TS = размер тайла).
  3. Изучите эти линии с тайлами в направлении движения, пока не найдете ближайшее препятствие. Затем в цикле смотрите на каждое движущееся препятствие и определите, какое из всех наиболее близкое на вашем пути.
  4. Результирующее движение игрока вдоль этого направления это минимум между расстоянием до ближайшего препятствия и дальностью хода игрока.
  5. Передвинуть игрока на новую позицию. С этой позиции обрабатывайте другую координату, если еще не обработали.


Уклоны



Mega Man X, с комментариями к уклонам

Уклоны (тайлы, на которые указывают зеленые стрелки) слегка мудрёная штука, так как они и препятствия и в то же время позволяют персонажу заходить на их тайл. Также они вызывают изменение Y координаты при простом перемещении вдоль оси Х. Простой способ сделать их — это позволить тайлу хранить информацию о «высоте пола» с каждой стороны. Допустим система координат с нулём в левом верхнем углу, тогда тайл слева от X (героя), первый тайл уклона, будет содержать высоты {0, 3}. Тот, на котором он стоит будет содержать {4, 7}, затем {8, 11}, потом {12, 15}. После чего всё повториться снова с {0, 3} и так далее. После мы видим уклон с большим углом, собранный из двух тайлов {0, 7} и {8, 15}.

Детальный вид тайла {4, 7}

Метод, который я собираюсь описать позволяет делать произвольные уклоны, несмотря на очевидные причины, эти два уклона наиболее распространены и получаются из 12 тайлов (6 описано ранее и их зеркальные копии). Алгоритм коллизий изменяется для горизонтального движения:
  • Убедитесь, что передвижение по оси X происходит раньше чем по Y.
  • Во время проверки столкновений (4 пункт выше по тексту) уклон считается столкновением только если его ближайшая грань наивысшая (меньше Y координаты). Это предотвратит ситуацию с подёргиванием персонажа при движении с другой стороны.
  • Возможно вы захотите запретить персонажу останавливаться на полпути уклона (например на {4, 7} тайле). Эти ограничения приняты в Mega Man X и многих других играх. Если не захотите, то вам придется разбираться с еще более сложным случаем, когда игрок пытается забраться с нижней стороны тайла с уклоном. Один вариант побороть это — обработать уровень и пометить все подобные тайлы. Тогда при обнаружении столкновений нужно также считать это столкновением от нижней части игрока, если наименьшая Y координата игрока ниже выпирающей части тайла (координата тайла * размер тайла + уровень пола y).
  • Целый тайл с препятствием, смежный с уклоном, если на нем стоит игрок, не должен считаться будто он прикреплен к уклону. Тоесть если персонаж (его нижний центральный пиксель) на уклоне {0, *}, нужно игнорировать левый тайл, а если на улоне {*, 0} — игнорировать правый. Можно делать так для большего числа тайлов, если персонаж шире, чем два тайла — просто скидывать проверку всего ряда, если игрок двигается навстречу верхней части уклона. Причина, для того чтобы это делать, в том, чтобы предотвратить застревание персонажа в этих тайлах (подсвечены желтым на скриншоте выше), пока он забирается на уклон и его ступни будут ниже «уровня поверхности» до тех пор. пока он не поднимется до уровня прямых тайлов.


И для вертикального передвижения:
  • Если позволить гравитации делать свою работу для спуска по склону, убедитесь, что минимальное смещение гравитацией совместимо с уклоном и горизонтальной скоростью. Например, на 4:1 уклоне (на скриншоте {4, 7} выше) гравитационный сдвиг должен быть как минимум 1\4 горизонтальной скорости округленной вверх. На склоне 2:1 (таком как {0, 7}) минимально 1\2. Если это не учесть, игрок будет двигаться горизонтально до конца рампы в течении времени, пока гравитация не поймает и не бросит его вниз, заставляя его скакать на наклонной плоскости вместо плавного спуска по ней.
  • Альтернативой использования гравитации можно взять расчет разницы количества пикселей между высотой игрока до начала и после окончания движения (использую формулу ниже) и менять его позицию, чтобы всё совпадало.
  • Когда движетесь вниз возьмите для расчета не наивысшую грань уклона для просчета столкновений, а текущую точку на вертикали с позицией игрока. Чтобы это сделать найдите [0, 1] значение, которое определит расположение игрока на тайле (0 = лево, 1 = право) и используйте линейную интерполяцию floorY значений. Код будет выглядеть как-то так:
    float t = float(centerX - tileX) / tileSize;
    float floorY = (1-t) * leftFloorY + t * rightFloorY;
  • При движении вниз, если несколько тайлов с препятствиями находится на одной Y координате и одна на X координате центра игрока — тайл уклона, то используйте именно его, игнорируйте остальные, даже если они, технически, ближе. Это обеспечит более верное поведение на краях уклонов и не позволит персонажу «съезжать» на совершенно ровном тайле только потому, что рядом есть уклон.


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



Super Mario World, где Mario сваливается сквозь (слева) и стоит на (справа) одной и той же односторонней платформе

Односторонние платформы — это обычные платформы на которые можно встать, но при этом можно запрыгнуть сквозь них снизу. Другими словами, они считаются препятствием если вы стоите на них, и не считаются, если вы прыгаете снизу. Это полностью описывает их поведение. Алгоритм немного меняется:
  • по X координате этот тайл никогда не бывает препятствием
  • По Y координате этот тайл препятствие только при движении сверху вниз и только при координате игрока больше (хотя бы на 1 пиксель, когда он стоит), чем верхняя грань тайла.

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

Лестницы



Mega Man 7, с сеткой тайлов, подсвеченными тайлами лестницы, и прямоугольник игрока.

Лестницы могут показаться сложной штукой, но реализуются они довольно просто — когда персонаж на лестнице, он просто игнорирует большинство коллизий и переопределяет их новым набором правил. Лестницы обычно в один тайл шириной.
На лестницу можно попасть двумя путями:
  • Описывающий персонажа прямоугольник пересекается с лестницей, неважно в воздухе или на земле и нажатием кнопки «вверх» (некоторые игры позволяют нажимать «вниз»)
  • Персонаж стоит на вершине тайла лестницы (который часто является тайлом односторонней платформы, так что по нему можно ходить) и жмет «вниз».

Это немедленно вызывает эффект привязывания координаты Х игрока к тайлам лестницы, и если двигаться сверху лестницы вниз, нужно просто менять У координату, поскольку игрок уже находится внутри реальной лестницы. Начиная с этого момента некоторые игры начинают использовать другой описывающий прямоугольник, чтобы определять находится ли игрок до сих пор на лестнице. Mega Man, например, видимо использует одиночный тайл (эквивалент верхнего тайла обычного персонажа, обведенный красным на картинке выше).
Чтобы покинуть лестницу есть несколько вариантов:
  • Достигнуть верха лестницы. Обычно это оканчивает анимацию и перемещает игрока на несколько пикселей вверх по У, чтобы он теперь просто стоял на верху лестницы.
  • Достигнуть низа висящей лестницы. Это приведёт к тому, что игрок просто упадёт, хотя некоторые игры просто не дадут покинуть лестницу таким путём.
  • Сдвинуться влево или вправо. Если сбоку нет препятствий, игроку может быть позволено сбежать таким путём.
  • Спрыгнуть. Некоторые игры позволяют освободить лестницу даже так.

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

Ступеньки



Castlevania: Dracula X, с сеткой тайлов

Ступеньки это разновидность лестниц, как замечено в некоторых играх, но особенно в серии Castlevania. Реализация очень похожа на лестницы с некоторыми оговорками:
  • Игрок двигается тайл за тайлом или по половинке тайла (как в Dracula X)
  • Каждый «шаг» сдвигает игрока одновременно и по Х и по У координатам на фиксированную величину.
  • Определение стартового соприкосновения с лестницей при начале подъема должно смотреть на тайл вперёд вместо того, с которым идёт пересечение сейчас.

Другие игры могут реализовывать лестницы как уклоны. В этом случае лестницы несут больше декоративных характер.

Двигающиеся платформы



Super Mario World

Двигающиеся платформы могут показаться хитрыми, но на самом деле крайне просты. В отличие от нормальных платформ их нельзя представить фиксированными тайлами (по очевидным причинам), а необходимо описать их AABB прямоугольником. Это обычное препятствие для всех видов столкновений. Если останоиться на этом, то это будут очень скользкие платформы (они будут работать, но как и задумано, за исключением того, что персонаж не будет двигаться вместе с ними).
Есть несколько вариантов решения. Разберём один алгоритм:
  • До того как в сцене что-то сдвинетcя, надо определить стоит ли персонаж на подвижной платформе. Этого можно добиться проверкой, например, находится ли центральный пиксель игрока на один пиксель выше поверхности платформы. Если да, сохраним указатель на платформу где-нибудь внутри персонажа.
  • Сдвигаем все подвижные платформы. Убедимся что сделали это до того как начали двигать персонажей.
  • Для каждого персонажа, который стоит на платформе, посчитаем дельту смещения платформы (насколько сдвинулась по каждой оси). Теперь сдвинем персонажа на тоже самое значение.
  • Теперь двигаем персонажей, как и обычно.


Другие фичи



Sonic the Hedgehog 2

Есть игры, которые располагают сложными и эксклюзивными фичами. Например серия Sonic the Hedgehog. Эти фичи выходят за рамки этой статьи (и моих знаний, что очень важно!), но могут стать темой будущей статьи.

Продолжение (окончание)
Tags:
Hubs:
+50
Comments30

Articles