Найти тему
Наука от Rezerford

Phaser 3. Платформер своими руками. Часть 7.

Добавление физики в игру

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

В Phaser 3 существует три типа "физики", а именно:

  • Arcade - самый простой тип физики, используется в большинстве игр, если надо сварганить игру "на быструю руку"
  • Matter.js - более сложный вид физики, с помощью него можно делать сложные физические "трюки" в игре, которые сложно реализуемые, либо вообще не реализуемые с помощью arcade
  • Impact.js - можно использовать движок другого фреймворка ImpactJS - ещё одна чудесная библиотека для создания браузерных HTML5 игр, написанные на JavaScript

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

Чтобы использовать Arcade Physics, необходимо вспомнить наш конфигурационный объект при инициализации сцены игры. Всё что от Вас требуется, так это задать ещё одно свойство physics в объекте config:

Задаётся тип физики (движка) по умолчанию (default) и в свойстве arcade сразу же зададим вертикально направленную гравитацию, со значением 300. Между прочим, тут же можно определить булевое свойство debug. Если задать True, то вокруг спрайтов, tile и объектов будут прямоугольные рамки, и линии направления движения спрайтов (смотрите screenshot).

-2

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

Далее зададим границы игрового мира, которые должны ограничиваться нашей картой, что мы создали в Tiled. Делается это следующим образом:

В конструкторе мы задали два нужных нам свойства viewportWidth и viewportHeight. Наш игровой экран как мы делали с самого начала 800 на 600 пикселей. Далее в create методе определяем границы игрового мира с помощью метода setBounds объекта this.physics.world. Обратите внимание, что ширина игрового мира ограничена нашей картой (свойство this.map.widthInPixels).

Создание главного героя

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

Создайте новый файл player.js и добавьте следующий код (ниже мы разберём каждую строчку):

В конструктор мы передаем объект сцены и начальные координаты нашего главного героя. Сохраняем в свойстве scene нашего объекта player объект игровой сцены.

Далее код Вам должен быть знаком, а именно создаются три анимации для главного героя. Если подзабыли как это делается, вернитесь к Часть 5 нашего туториала и освежите память.

Создаем сам спрайт и появляется на экране с помощью метода scene.physics.add.sprite(). Ещё спрайты можно создать с помощью this.add.sprite(), но учтите, что созданный таким образом спрайт не будет обладать "физикой", что означает, что нельзя ему задать скорость, ускорение и так далее, это просто не сработает, поэтому потребуется специальными методами "наградить" наш спрайт элементарной физикой.

После чего, цепочкой методов, устанавливаем главному герою отскок от платформ (setBounce(0.1)) и, чтобы герой не улетел под действием гравитации за экран игрового мира, вызываем метод setCollideWorldBounds(true).

Обратите внимание, при создании спрайта, третьим аргументом мы передаём ключ так называемой текстуры (texture). Это ни что иное, как загруженный атлас со спрайтом. Спрайт для нашей обучающей игры вы как всегда можете найти на Яндекс.Дисках. Тут же находится json с данными по спрайту. Вот так выглядит наш спрайт для главного героя:

-3

Теперь займёмся самым интересным и кульминационным моментом в игре - это управление главным героем и его оживлением. В Phaser 3 объект this.input.keyboard отвечает за нажимаемые игроком клавиши. Этот объект имеет целый ряд полезных методов и свойств. В нашем обучаемом курсе понадобиться метод addKeys , куда передаётся объект с клавишами, которые будут служить для управления главным героем. Согласно документации передаваемый объект должен содержать значения свойств в виде строк нажимаемых клавиш клавиатуры:

Up, down, left, right - это клавиши стрелок на клавиатуре. Space - Это клавиша пробела. Enter - клавиша ввода и так далее. Полный список клавиш Вы можете найти здесь. Мы воспользуемся в нашем случае элегантным и профессиональным способом, а именно деструктуризация объекта, это ещё одна "фишка" современного JS. Константы клавиш находятся в глобальном объекте:

Phaser.Input.Keyboard.KeyCodes

Ещё один известный и часто используемый метод - это создание курсора

var cursorKeys = scene.input.keyboard.createCursorKeys();

Возвращаемый объект, для каждой нажимаемой клавиши имеет два состояния:

  • isDown - клавиша нажата
  • isUp - клавиша отпущена

По этим двум состояниям и проверяют в условных операторах какую клавишу в данный момент нажал игрок. Существует ещё один метод - создание слушателя события нажатия клавиши и вызов функции обратного вызова:

Надеюсь, уважаемый читатель, комментарии излишни. Создав в конструкторе объект клавиш, нам остаётся оживить героя в методе update, который вызывается, как было уже сказано в первой статье, при обновлении экрана и рендере следующего фрейма.

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

Итак, мы в ветках условного оператора проверяем, если нажата, например клавиша left (проверяем состояние keys.left.isDown), то надо переместить спрайт влево. Мы задаём отрицательную скорость по оси X:

sprite.setVelocityX(-200)

Затем тут же, если спрайт находится "на полу", запускаем анимацию walk.

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

  • onFloor() - если спрайт двигается сверху вниз и натыкается на пол
  • onCeiling() - если спрайт двигается снизу вверх и натыкается на потолок
  • onWall() - если спрайт двигается или слева-направо, или справа-налево и натыкается на стенку.

Далее ветка с проверкой нажатия клавиши вправо:

Если же не нажата никакая клавиша, то мы "останавливаем" спрайт, задав нулевую скорость по оси X, и запустив, если спрайт на полу, анимацию idle (простой - в смысле пустая трата времени).

Но и это ещё не всё, у нас же герой должен уметь прыгать, при нажатии на клавишу пробела или стрелку вверх:

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

  • setFlipX(true) - делает зеркальное отображение спрайта
  • setFlipX(false) - восстанавливает изначальное положение спрайта

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

Теперь осталось рассмотреть, как же нам создать экземпляр нашего главного героя из PlatformerScene. Прежде всего импортируем наш класс Player в platformer-scene.js:

Затем в метод create() пропишите:

У объекта карты есть метод findObject(), первым аргументом передаётся название слоя в Tiled (если забыли вернитесь к Часть 3 как создать слой типа Object). А вторым аргументом идёт функция обратного вызова, которая возвращает по условию true, если объект найден. Помните, мы создавали в слое Objects метку с названием Spawn Point, вот сейчас она нам и нужна. Она задает начальное положение героя, когда начинается новый уровень. Найдя этот объект мы инстанцируем объект класса Player, передав эти начальные координаты.

И, наконец, создаем коллайдер с платформами, которые мы получили вызовом метода createStaticLayer.

И, конечно же, не забудьте в метод update вызвать update объекта player, для того, чтобы можно было управлять нашим главным героем:

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

Перейти к содержанию