Вы играете в игры?…. Ага! Вы когда-нибудь думали о том, чтобы построить такой? ... Хм ...

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

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

В этой статье мы рассмотрим пошаговую реализацию многопользовательской игры космических захватчиков в реальном времени (ах, ностальгия!) С Phaser3 и Ably Realtime.

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

Эта серия руководств разбита на четыре части:

И последнее, прежде чем мы начнем. Эта статья предполагает базовое понимание JavaScript и Express / NodeJS. Я постараюсь объяснить все остальное как можно подробнее 👩🏻‍🏫💡

Давай поиграем!

Часть 1 - Введение в игровые концепции и Phaser

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

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

  • Когда присоединяется новый игрок, ему случайным образом назначается аватар монстра из трех доступных вариантов.
  • Каждый игрок может перемещать свой аватар влево или вправо с помощью клавиш со стрелками на клавиатуре.
  • Каждый игрок будет видеть свой собственный аватар в белом цвете, но все остальные будут в случайном цвете, который им заранее назначен.
  • Когда к игре присоединяется заранее установленное количество игроков, запускается корабль. Этот корабль движется по горизонтальной оси в произвольно выбранном направлении, которое случайным образом меняется каждые 5 секунд. Вместе с этим изменится и скорость корабля. Этот корабль также стреляет пулями через регулярные промежутки времени, которые мы также можем установить.
  • Все аватары автоматически перемещаются вниз с заданным шагом на протяжении всей игры.
  • Цель для победы в игре - добраться до нижней границы игрового экрана, не будучи убитым одной или несколькими пулями, выпущенными кораблем.

Компоненты многопользовательской игры в реальном времени

Хотя может показаться, что за сетевой игрой в реальном времени происходит множество вещей, на самом деле они сводятся к трем основным компонентам:

  • Ресурсы
  • Физика
  • Сети

Давайте рассмотрим каждый из них подробнее.

1. Активы

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

Если вы заинтересованы в создании супер-аркады на тему вашей игры, вы можете узнать, как это сделать, из этого урока по пиксельной графике от Глаубера Котаки.

2. Физика

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

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

3. Сеть

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

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

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

Использование Phaser 3 для добавления ресурсов и включения физики

Phaser - это холст с открытым исходным кодом и фреймворк JavaScript для рендеринга WebGL для HTML5. Мы будем использовать Phaser 3 для создания игры. Причина, по которой я специально упоминаю версию, заключается в том, что между Phaser 2 и 3 есть много критических изменений, включая синтаксис. Кроме того, если вы хотите сами добавить в игру какие-то новые интересные функции, вам следует знать, что и где искать.

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

Есть действительно хорошая серия видеоуроков для Phaser, которую я бы порекомендовал, если вы впервые работаете с Phaser 3.

В версии TL; DR с Phaser3 мы можем указать объект конфигурации, который будет иметь информацию о холсте и самой игре, включая размер холста, его атрибуты стиля, различные сцены, которые включены в наш игровой процесс. (обсуждается вкратце), тип физического движка (как у Phaser) и т. д.

Затем мы передаем объект конфигурации, чтобы начать новую игру.

Это может показаться немного запутанным, поскольку вы еще не видели кода. Давай сделаем это дальше.

Начало работы с игрой

А пока перейдем прямо к экрану игры. О страницах запуска и рейтинге мы позаботимся позже. Наша игра будет HTML-страницей с отрисованным на ней холстом. Этот холст будет содержать и запускать саму игру. Итак, давайте создадим файл и назовем его index.html. Чтобы все было организовано, мы будем хранить все наши HTML-файлы в папке с именем views, а все клиентские файлы JavaScript в папке с именем public. Начнем views/index.html файл с базового скелета:

Как видите, все, что мы здесь делаем, - это ссылка на файлы CSS и JS, которые мы скоро добавим, но также, что более важно, ссылка на Phaser JS CDN. Вы можете скопировать CSS прямо из проекта GitHub

Помимо этого, в теле HTML у нас есть div с идентификатором game-container. Здесь мы добавим холст игры через JavaScript.

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

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

Phaser использует концепцию «сцен» для организации содержания игры. Вы можете думать о сцене как о том, что вы можете визуально увидеть в любой момент времени. Если во время игры она переключается на другой фон с различным взаимодействием между игровыми объектами, то это, вероятно, отдельная сцена от того, что вы просматривали раньше.

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

Как видите, у этого класса есть три пустых метода. Они идут с классом Phaser.Scene. Каждый из этих методов имеет разные функции, как описано ниже.

  1. Метод preload() получает все ресурсы, которые могут нам понадобиться, откуда бы они ни находились, загружает их и держит наготове, когда мы захотим добавить их в нашу игру.
  2. Метод create() выполняется один раз при первом запуске игры. Мы можем добавить в этот метод все инициализации переменных, определения анимации и т. Д.
  3. Метод update() выполняется непрерывно в цикле, пока игра продолжается, и, следовательно, может постоянно обновлять игровые объекты в соответствии с игровой логикой. Мы будем обновлять аватарки, стрелять пулями, перемещать корабль - все этим методом.

Загрузка ассетов и создание анимации

Давайте сейчас загрузим некоторые активы. Определите метод preload() следующим образом

Изначально я разместил эту игру на Glitch, где все ресурсы хранятся в своей корзине, которую можно получить по ссылке CDN. Вы можете использовать то же самое, в противном случае Amazon S3 - еще один популярный вариант.

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

Как видите, мы загрузили актив листа спрайтов в Phaser с помощью this.load.spritesheet() и отправили ему три параметра:

  • идентификатор
  • путь к фактическому файлу
  • размеры файла (в пикселях).

Мы также можем просто назвать это изображением, но тогда мы не сможем делать с ним крутые анимации, как вы вскоре увидите.

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

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

В методе create() мы определим анимацию для этого взрыва:

Мы использовали this.anims.create() метод Phaser для создания анимации. Этот метод принимает:

  • key, который мы будем использовать позже для воспроизведения этой анимации
  • frames, который генерирует кадры с использованием идентификатора ресурса, к которому мы хотим применить эту анимацию.
  • frameRate, который определяет скорость, с которой мы хотим воспроизвести эту анимацию.
  • repeat, который указывает, сколько раз будет запускаться анимация.
  • hideOnComplete указывает, должен ли объект, который был анимирован, исчезнуть после завершения анимации

Пока мы не будем ничего добавлять в метод update(). Если вы заметили, мы на самом деле еще не создали экземпляр игры, мы сделаем это в более поздней части этой серии статей.

На этом пока все. Мы узнаем о сетях для приложений реального времени в части 2 - Оценка сетевых протоколов для приложений реального времени

Другие статьи из этой серии:

Отдельный выпуск, относящийся к этому руководству, доступен на GitHub, если вы хотите его проверить.

Вы также можете следить за последними разработками проекта Github по этому проекту.

Если у вас есть вопросы, напишите мне в Twitter @Srushtika. Мои личные сообщения открыты :)