Как создать игру жанра 2d платформер?

   Автор: Rodrigo Monteiro. Оригинал здесь 

Четыре пути реализации
Существует четыре основных подхода, которыми могут быть реализованы платформеры. В порядке возрастания сложности, они таковы:

Тип # 1: Основанный на плитках (простой)
Движение персонажа ограничено тайлами (плитками), так что вы никогда не сможете стоять между двумя плитками. Могут быть использованы специальные анимации, чтобы создать иллюзию плавного движения, но, игрок всегда будет стоять прямо на каком-то тайле. Это самый простой способ реализации платформера, но это накладывает жесткие ограничения на управление персонажем, что делает подход непригодным для реализации геймплея привычных для нас игр платформеров. Однако, он часто используется для головоломок и “кинематографических” платформеров.
Примеры: Принц Персии, Toki Tori, Lode Runner, Flashback
Как это работает
Карта представляет собой сетку из плиток, каждая из которых хранит информацию о том, является ли она препятствием или нет, какое изображение используется, какие звуки шагов персонажа должны использоваться и так далее. Игрок и другие персонажи представлены в виде набора одного или более плиток, которые перемещаются вместе. В Lode Runner, например, игрок состоит из одной плитки. В Toki Tori, игрок 2 × 2 плитки. В Flashback, игрок в ширину две плитки и пять плиток высотой когда стоит (что является необычным в связи с меньшим размером тайлов карты, см. картинку выше), и  три плитки высотой, когда сидит.
В этом виде игры, игрок редко будет – если вообще будет – двигаться по диагонали. Но, если необходимо, – движение можно разложить в два отдельных этапа. Персонаж скорее всего будет двигаться на одну плитку за шаг. Движение через несколько плиток может быть реализовано в виде нескольких шагов по 1 плитке каждый (в Flashback вы всегда двигаетесь через две плитки сразу). Алгоритм:
1. Создайте копию персонажа, переместите его на место назначения (например, если двигаться на одну клетку вправо, надо сделать копию персонажа, где каждая его плитка сдвигается 1 плитку вправо)
2. Проверьте эту копию на пересечения с фоном и другими персонажами.
3. Если пересечение было найдено, движение персонажа блокируется. Надо включить соответствующие анимации и т.д.
4. В противном случае, сдвигаем персонаж. Во время перемещения проигрывается анимация, поэтому переход выглядит равномерным.
Этот вид движения очень плохо подходит для традиционных дугообразных прыжков – так, игры в этом жанре часто вовсе не используют прыжки (Toki Tori, Lode Runner), или может быть только вертикальные или горизонтальные прыжки (Принц Персии, Flashback), которые есть ни что иное, как разновидность описываемого метода.
Преимущества этой системы включают простоту и точность. Поскольку игры более детерминированы, глюки будут возникать гораздо реже. Игровой процесс более контролируем, нужно меньше вносить изменения в реализацию в зависимости от обстоятельств. Игровая механика (например, подъём на выступах и односторонние платформы) очень проста, по сравнению с более сложными способами создания платформеров – все, что вам нужно сделать, это проверить, в правильном месте ли расположены тайлы игрока на карте для  выполнения какого-то действия.
В принципе, эта система не позволяет делать шаги шириной менее одной плитки, но это можно улучшить  несколькими способами. Например, плитка может быть немного меньше, чем игрок (скажем, игрок 2 × 6 плиток), или вы можете позволить визуально заходить внутрь  плитки, не влияя при этом на логику игры (такой подход, мне думается, использован в  “Lode Runner – Legend Returns”).




Tип #2: Основаный на плитках (со сглаживанием)
Здесь взаимодействие с игровым миром также определяется по сетке ячеек, но персонажи имеют возможность свободно перемещаться по миру (обычно подразумевается разрешение 1px с округлением до целых). Это самый распространённый метод разработки платформеров для 8-битных и 16-битных консолей. Он достаточно популярен и до сих пор, благодаря своей относительной простоте, в связи с чем редактирование уровней игры представляет собой более лёгкую задачу, чем при работе с более продвинутыми типами. Это позволяет создавать наклонные платформы в уровне, а также задавать более плавную траекторию для прыжков.
Если вы хотите сделать игру в жанре «2d экшн», но не уверены, согласно какому типу платформеров хотите вести разработку, я предлагаю прибегнуть именно к этому. Он очень гибок, относительно прост в реализации, и предоставляет большую степень контроля, нежели другие три типа. Неудивительно, что большинство лучших экшн-платформеров всех времён были разработаны именно так.


Кадр из игры Mega Man X. Видны границы ячеек и hitbox (зона поражения) игрока.
Примеры игр: Super Mario World, Sonic the Hedgehog, Mega Man, Super Metroid, Contra, Metal Slug, и практически все остальные платформеры 16-битной эпохи.




Как это работает.

Информация о карте записывается и хранится так же, как и в первом типе, разница заключается во взаимодействии персонажей с игровым фоном. Hitbox персонажа –  ограничивающий прямоугольник, выровненный по координатным осям (AABB, или, проще говоря, прямоугольник, который не вращается). Как правило, ограничивающий прямоугольник является целым кратным от размера ячейки. Стандартные мерки: одна ячейка в ширину, а в высоту же одна (маленький Марио, свёрнутая в шар Сэмус),  две (большой Марио, Мега Мэн, Сэмус в положении «сидя») или три ячейки(Сэмус в положении «стоя»). Во многих случаях, спрайт самого персонажа больше, чем логический хитбокс, так как это более приемлемо визуально. Геймплей таким образом становится «честнее», ведь для игрока лучше иметь возможность избежать удара противника, когда он по идее должен быть, чем получать повреждения, когда  на экране снаряд проходит мимо. На картинке выше можно увидеть, что спрайт по форме напоминает квадрат (фактически, он имеет ширину в 2 ячейки), но при этом имеет прямоугольный хитбокс (1 ячейка).

Если предположить, что на карте нет наклонных и односторонних платформ, алгоритм несложен :

1.  Последовательно разбить на шаги движение по осям X и Y. Если затем вы планируете ввести наклоны, начните разбивку с оси Х, в противном случае порядок не имеет большого значения. Затем, для каждой оси:

2.  Найдите координату края, направленного вперёд (ведущего края). Например, при движении влево это координата Х левой стороны ограничивающего прямоугольника, при движении вправо – коорднината Х правой стороны, при движении вверх – координата Y верхней стороны и т. д.

3. Определите, с какими линиями сетки пересекается ограничивающий прямоугольник – так вы получите минимальное и максимальное значение на противоположной оси (т.е. при движении по горизонтали мы найдем номера вертикальных ячеек с которыми пересекаемся). К примеру, если вы двигаетесь налево, игрок пересекает ряды номер 32, 33 и 34 (то есть ячейки с координатами Y = 32 * TS, Y = 33 * TS, и Y = 34 * TS, где TS = размер ячейки).

4. Проследуйте дальше по линии ячеек в направлении движения пока не найдёте ближайшее твердое препятствие. Затем пройдитесь по всем движущимся объектам, и определите ближайшее  препятствие среди них на вашем пути.

5. Минимальное из двух значений (расстояние до ближайшего препятствия и расстояние на которое вы собирались продвинуться изначально) и будет значением расстояния на которое мы передвигаем персонажа.

6. Передвиньте игрока на новую позицию. С новой позиции, повторите шаги, и так далее.

Наклоны

Mega Man X с отмеченными ячейками наклона.

Наклоны (ячейки, на которые указывают зелёные стрелки) могут представлять определённую сложность, так как они по сути являются препятствиями, но при этом игрок всё же может частично входить в эти ячейки. Они также предполагают изменение Y координаты персонажа в зависимости от движения по оси X. Во избежание проблем, нужно сделать так, чтобы ячейка содержала параметр “floor y” каждой стороны. Если в системе координат обозначить левую верхнюю ячейку как {0, 0}, как показано на рисунке, тогда точка {0, 3} – это ячейка, расположенная чуть левее от персонажа (первая ячейка наклона); ячейка, на которой он стоит – точка {4, 7}, затем {8, 11}, и {12, 15}. Далее ячейки начинают повторяться, с новой точки {0, 3} и далее, а дальше спуск становится круче и состоит из двух точек {0, 7} и {8, 15}.

Ячейка {4, 7} в деталях

Система, которую я сейчас опишу, позволяет вводить произвольные наклоны, хотя, по чисто визуальным причинам, эти два вида наклонов встречаются чаще всего и включают в себя всего 12 ячеек (6, обозначенных выше и их зеркальные отражения). Алгоритм столкновений для горизонтального движения изменяется следующим образом:

■  Здесь обязательно сначала смещаться по X, потом по Y.
■  Мы считаем что столкновение с ячейкой склона произошло только если его ближайший край – высокий (на картинке – меньшая координата y). Это исключит «проваливание» персонажа через склон с противоположной стороны.
■  Может быть полезным запрещать склонам заканчиваться “на полпути” (т.е. на ячейке {4, 7}). Это ограничение использовано в Mega Man X и многих других играх. Если этого не сделать, вы можете столкнуться с более сложными ситуациями, при которых игрок будет пытаться забраться с нижней стороны ячейки спуска. Эти ситуации решаются, например, путём предварительной обработки уровня с отметкой всех подобных конфликтных ячеек. При определении столкновения, задайте дополнительное условие:  нижняя координата Y игрока должна быть больше (ниже на оси) чем выступающий край ячейки (tileСoord * tileSize + floorY).
■  Обычная ячейка-препятствие, смежная с ячейкой наклона, на которой игрок располагается в данный момент времени не должна считаться препятствием, если она непосредственно соединяется с наклоном, т. е. если персонаж (а именно его нижний средний пиксель) располагается на ячейке склона типа {0, *}, пропускайте ячейку слева, а если на {*, 0} – пропустите ячейку справа. Возможно, вам потребуется применить это для большего числа ячеек, если ширина персонажа больше, чем 2 ячейки – можно просто пропустить целый ряд, если игрок движется к верхней части склона. Это делается для того, чтобы игрок не застревал в ячейках (на рисунке подсвечены жёлтым) в процессе движения вверх по склону, так как его нога продолжит быть на “поверхности” на момент контакта с твёрдой ячейкой.

Для вертикального движения:

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

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

Super Mario World: Слева Марио проходит сквозь одностороннюю платфому, справа стоит на ней же.

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

■       По оси Х ячейка не является «твёрдой» никогда;
■       По оси Y ячейка является препятствием только в том случае, если до начала движения игрок целиком находился над ней, т. е. нижняя координата игрока располагалась как минимум на один пиксель выше верхней координаты односторонней платформы. Чтобы обеспечить это условие, желательно сохранить начальную позицию игрока перед началом движения .

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

В некоторых играх имеется возможность “спрыгнуть вниз” с таких платформ. Способов задать такую возможность немного, но они все относительно просты. Например, можно на один кадр отключить односторонние платформы и убедиться, что вертикальная скорость равняется как минимум единице – так, на следующем кадре игрок окажется вне зоны соприкосновения. Или же можно задать исключительное условие, при котором игрок стоит на односторонних платформах и, при соблюдении этого условия, вручную перемещать игрока на пиксель вниз.

Лестницы (вертикальные)

Mega Man 7: видны границы ячеек, подсвечены ячейки лестниц и «лестничный» хитбокс игрока (в красной рамке).

Лестницы могут показаться сложными для реализации, но они попросту представляют собой  другое состояние персонажа: будучи на лестнице, игроком игнорируется практически вся система столкновений, и вместо неё вводится новый набор правил. Обычно, ширина лестницы – одна ячейка.

Переход в «лестничное» состояние возможен следующими путями:

■  Хитбокс игрока пересекает лестницу на уровне пола или в полёте, игрок нажимает клавишу «вверх» (в некоторых играх переход возможен также по нажатию клавиши «вниз»);
■ Персонаж стоит на верхней «лестничной» ячейке (которая чаще всего по сути является односторонней платформой, чтобы по ней можно было пройтись сверху), игрок нажимает «вниз».

Таким образом, возникает эффект моментального совмещения x-координаты игрока с лестницей.  Для движения вниз с верхушки лестницы нужно переместить y-координату персонажа так, чтобы игрок оказался внутри лестницы. В некоторых играх для таких ситуаций вводится альтернативный хитбокс, отслеживающий  позицию игрока в пределах лестницы. К примеру, в Mega Man, он представляет собой единичную ячейку, совпадающую с верхней ячейкой спрайта персонажа.

Покинуть лестницу можно следующими способами:

■ Игрок достигает верха лестницы. Здесь обычно включается особая анимация, при которой игрок перемещается на несколько пикселей вверх по оси Y и оказывается над лестницей в стоячем положении;

■ Игрок достигает низа висячей лестницы. В этом случае игрок попросту падает, хотя в некоторых играх это не допускается;

■ Игрок движется влево или вправо. Если нет препятствий, игрок сходит с лестницы в соответствующем направлении;

■ Игрок прыгает. В некоторых играх позволяется отпустить лестницу таким образом.

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

Лестницы (наклонные)

Castlevania: Dracula X. Видны границы ячеек.

Данный тип лестниц встречается редко. В основном, наклонные лестницы присущи играм серии Castlevania. Их реализация во многом совпадает с реализацией обычных лестниц, за некоторыми исключениями:

■       Шаг игрока составляет либо целую ячейку, либо половину ячейки (как в Dracula X);
■       Во время каждого «шага» по лестнице, игрок смещается одновременно по обеим осям на заранее заданное значение;

В иных играх встречаются лестницы, которые ведут себя как склоны – здесь они служат для чисто визуальных целей.

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

Super Mario World

Движущиеся платформы довольно просты в реализации, хотя сначала может показаться иначе. В отличие от обычных платформ, они, очевидно, не могут быть представлены фиксированными ячейками. Вместо этого они должны являть собой прямоугольник типа ABBA (тот, который не вращается). С точки зрения определения соприкосновений, эти платформы по сути – обычные препятствия, но если не ввести определённые изменения, они будут очень «скользкими» (т. е. двигаться, постоянно выползая из-под персонажа).

Для решения этой проблемы существует несколько путей. Рассмотрим такой алгоритм:

■ Перед тем как что либо сдвинуть на карте, задайте условие, при котором игрок считается находящимся на движущейся платформе. Пускай программа следит за тем, чтобы, например, нижний средний пиксель персонажа располагался ровно на один пиксель выше поверхности платформы. Если это условие соблюдается, сохраните указатель на платформу и её позицию в персонаж
■ Сдвиньте все движущиеся платформы на один шаг. Обязательно сделайте это перед тем, как будете двигать персонажей и другие объекты;
■ Для всех персонажей, стоящих на движущейся платформе, определите дельта-положение платформы, т. е. полное перемещение, совершённое ей по каждой оси. Затем, переместите персонажа в соответствии с этим значением;
■Передвигайте персонажей дальше как обычно.

Другие особенности

Sonic the Hedgehog 2

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

Тип #3: Битовая маска

Похож на предыдущий, только для определения столкновения используются не крупные тайлы, а пиксели. Такая техника позволяет улучшить детализацию, но при этом увеличивается сложность исполнения и использование памяти. Редактор уровней больше напоминает какой-нибудь paint. Часто для создания графики тайлы не используются, поэтому для каждого уровня могут требоваться большие, замысловатые, индивидуальные изображения. В связи со всем этим, данная техника не является распространённой, но с её использованием можно добиться более качественных результатов, по сравнению с типами, основанными на ячеистом принципе. Она также подходит для создания динамичного окружения, такого как разрушаемая местность в Worms – в битовой маске можно «рисовать», и тем самым изменять модель уровня.

Игра Worms World Party с разрушаемой местностью

Примеры: Worms, Talbot’s Odyssey
Пример реализации разрушаемого ландшафта (destructible terrain) как в Worms можно скачать тут  (C++/SFML)

Как это работает

Базовые принципы схожи с описанными для типа «основанный на плитках со сглаживанием» – можно просто каждый пиксель считать за тайл (шириной 1px), применить точно такой же алгоритм и всё будет работать как надо, за одним лишь важным исключением – склоны. Так как склоны теперь косвенно определяются расстановкой близлежащих ячеек, методы, описанные выше не работают, и возникает необходимость в применении более сложного алгоритма. С другими моментами, вроде лестниц, здесь тоже сложней.

 

Склоны

Talbots Odyssey: поверх изображния наложена битовая маска столкновений.

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

Приведу в общих чертах алгоритм, изпользуемый в Talbot’s Odyssey:

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

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

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

Тип #4: Векторы

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

Braid (редактор уровней) с видимыми слоями (наверху) и  …полигонами (внизу)

Как это работает
Есть два основных способа этого достигнуть:
1. Движение и столкновения реализуйте способом похожим на битовую маску, но используя при этом многоугольники, чтобы вычислять отклонения и получать правильный наклон.
2. Используйте физический движок (например, Box2D)
Очевидно, второй способ более распространенный (хотя я подозреваю, что Braid первый), потому что он проще и позволяет делать различные вещи с динамикой в игре.
К сожалению, приходится быть очень внимательным, когда идешь этим путем, чтобы не превратить игру в обычный физический платформер.

Составные объекты

Этот подход имеет свои проблемы.  Иногда бывает трудно определить, стоит игрок на полу (из-за ошибок округления), ударяется о стену или скользит вниз по крутому склону. При использовании физического движка трение может стать проблемой, если захотите усилить его у ног и ослабить по бокам.

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

Общие положения
Независимо от выбранного Вами типа (за исключением, пожалуй, типа 1#), к ним применимы несколько общих положений.

Ускорение

Super Mario World (низкое ускорение), Super Metroid (среднее ускорение), Mega Men  7 (высокое ускорение)

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

Ускорение – это скорость  изменения скорости. Если оно низкое, персонаж долго разгоняется или долго тормозит, когда игрок отпускает кнопку. Это делает движение персонажа «скользящим», что создает сложности для играющего. Такое движение обычно ассоциируется с серией игр Super Mario. Когда ускорение высокое, персонаж разгоняется до максимума за считанные секунды (либо моментально) и так же быстро останавливается, что приводит к очень быстрому, «дерганому» управлению джойстиком, как, например, в Мега Мэн. Полагаю, что в Мега Мэн на самом деле используется неограниченное ускорение, т.е. вы либо бежите на полной скорости, либо стоите на месте.

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

Как это работает

Реализация ускорения на деле очень проста, но существует несколько подводных камней , о которых следует помнить.

1. Определите скорость  xTargetSpeed. Она должна быть 0, если игрок не нажимает кнопок джойстика, -maxSpeed если нажата кнопка «Влево» или +maxSpeed если нажата кнопка «Вправо».
2. Определить  значение yTargetSpeed. Оно должно быть  0, если игрок стоит на платформе. В противном случае +terminalSpeed.
3. Для каждой оси увеличить текущую скорость до заданной, используя либо среднее взвешенное, либо добавочное ускорение.

Два вида ускорения:
Среднее взвешенное : число (“a”) от 0 (без движения) до 1 (мгновенное ускорение).
Используйте это значение для вставки между заданной и текущей скоростью, а результат установите как заданную скорость.

vector2f curSpeed = a * targetSpeed + (1-a) * curSpeed;
if (fabs(curSpeed.x) < threshold) curSpeed.x = 0;
if (fabs(curSpeed.y) < threshold) curSpeed.y = 0;

Добавочное ускорение: Мы определим, в каком направлении добавить ускорение (используя функцию sign, которая возвращает  1 если аргумент больше нуля и  -1 если меньше), затем проверим, не вышли ли мы за рамки.

vector2f direction = vector2f(sign(targetSpeed.x – curSpeed.x),  sign(targetSpeed.y – curSpeed.y));
curSpeed += acceleration * direction;
if (sign(targetSpeed.x – curSpeed.x) != direction.x)
curSpeed.x = targetSpeed.x;
if (sign(targetSpeed.y – curSpeed.y) != direction.y)
curSpeed.y = targetSpeed.y;

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

Когда персонаж натыкается на препятствие, лучше всего снизить его скорость до нуля.

Контроль прыжков


Super Metroid, Самус выполняет «Космический прыжок» (с использованием Screw Attack)

Возмность прыжка в игре-платформере  сводиться к определению стоит ли игрок на земле (или, то что он был на земле последние n кадров). В таком случае персонажу задается отрицательная начальная скорость  y speed (соответствует физическому термину «импульс»), остальное – дело гравитации.

Существуют четыре основных способа управления прыжками:

1. Импульс: используется в таких играх, как Super Mario World и Sonic the Hedgehog. Прыжок сохраняет импульс (на игровом жаргоне скорость), который был у персонажа до него. В некоторых играх это единственный способ повлиять на дугу прыжка – совсем как в реальной жизни.  Здесь специально реализовавыть ничего не надо.
2. Воздушное ускорение: сохранение контроля над горизонтальным движением в воздухе. В реальности это невозможно, но в играх эта функция очень популярна, так как делает персонажа более управляемым. Она есть почти в каждом платформере, за исключением игр вроде Принца Персии.
Как правило, в воздухе ускорение значительно снижается, так что важен импульс, но некоторые игры предоставляют вам полный воздушный контроль. Обычно это осуществляется как настройка параметра ускорения, в то время когда вы находитесь в воздухе.
3. Контроль подъема: еще одно физически невозможное, но чрезвычайно популярное действие, т.к. предоставляет больший контроль над персонажем. Чем дольше вы удерживаете кнопку прыжка, тем выше персонаж прыгает. Обычно это осуществляется постепенным добавлением импульса персонажу (хотя импульс может также постепенно уменьшаться) или сопротивлением гравитации, пока удерживается кнопка. Накладывается ограничение по времени, если вы не хотите, чтобы персонаж прыгал бесконечно.
4. Многократные прыжки: в некоторых играх подпрыгнувшему  игроку позволяется прыгнуть еще раз, возможно, неограниченное (как в Прыжке в Космос в Super Metroid или в полете в Одиссее Талбота) или ограниченное количество раз до того, как он коснется земли («двойной прыжок» – самый распространенный вариант). Это может быть достигнуто при помощи счетчика, который увеличивает значение на единицу после каждого прыжка и  уменьшает, когда вы стоите на земле (будьте осторожны, когда обновляете его, иначе вы можете сбросить его сразу после первого прыжка). Дальнейшие прыжки возможны, если показания счетчика низкие. Иногда второй прыжок короче начального. Могут применятся другие ограничения – Прыжок в Космос возможен только после того, как вы выполнили вращательный прыжок и начали падать.

Анимация и управление

Black Thorne, анимация персонажа замедляется перед выстрелом назад (кнопка Y)

Во многих играх анимация вашего персонажа будет проигрываться до реального выполнения требуемого действия, во всяком случае, в играх, основанных на резких движениях. Это разочарует игроков, поэтому НЕ ДЕЛАЙТЕ ЭТОГО! Вам следует ставить на первое место анимацию, когда речь идет о прыжках и беге, но если вам не все равно, ограничьтесь такой  анимацией, чтобы само действие происходило  независимо от анимации.

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

·    Используйте числа с плавающей запятой (тип float) для всех расчетов и хранения данных положения,  и подводите итог в целых числах всякий раз, когда вы визуализируете изображение или рассчитываете коллизии. Быстро и просто, но при удалении от (0,0) начинает теряться точность. Это, вероятно, не имеет значения, если у вас очень большое игровое поле, но об этом следует помнить. В противном случае используется тип double.

·   Используйте числа с фиксированной запятой для всех расчетов и положения и снова подведите итог в целых числах , когда визуализируете изображение или рассчитываете коллизии. Менее точный, чем float и с более узким диапазоном формат, но в этом случае точность всегда одна и та же, а на некоторых устройствах работа с ним протекает быстрее (особенно медленно работа протекает с числами с плавающей запятой на мобильных устройствах).

·   Храните данные о положении в виде целых чисел, но держите «остаток» в типе float. Рассчитайте дельта-движение  как число с плавающей запятой, приплюсуйте к нему остаток, затем целую часть  добавьте к положению, а дробную – к полю «остаток». В следующем кадре к остатку будет добавлено значение. Преимущество этого метода в том, что вы используете целые числа везде, кроме расчета движения, что гарантирует отсутствие сложностей с плавающей запятой и повышает производительность. Этот способ также подойдет, если в вашем  фреймворке положение объекта должно быть выражено целым числом, или  если float, но это же положение используется для отрисовки на экран. В  этом случае вы можете хранить только  целочисленные значения, чтобы отрисовка всегда выравнивалась по пикселям.

Буду благодарен, если поделитесь:
SFML вопросы, прошу, задавайте на форуме.

Добавить комментарий