Разработка браузерных игр с использованием Phaser3, React, Typescript
Думаю, ни для кого не секрет, что каждый уважающий себя разработчик программного обеспечения должен иметь в своем портфолио хотя бы один пет-проект, а лучше полноценный продукт, дающий дополнительный постоянный заработок. Предметных областей и тематик приложений великое множество, но среди них есть одна, которая заслуживает отдельного внимания — разработка своей собственной игры.
Преимущества разработки игр
Прежде всего, следует понимать, что разработка игр достаточно трудоемкий процесс. Как правило, он включает в себя как знание особенностей фронтенда, так и бекенда и всех сопутствующих серверных технологий, особенно, если речь идет о мультиплеерах. Кроме того, часто необходимо задумываться над производительностью, используемыми ресурсами, архитектурой и алгоритмами. Потребуется подкачать знание математики, геометрии, физики. Навыки художника, пусть и в минимальном объеме, тоже будут очень кстати. А если игра имеет коммерческую цель - то и маркетинг, анализ рынка пригодятся. В итоге, разработка игр позволяет прокачать свои навыки настолько, что любое собеседование или работа в коммерческой компании над различными веб - сервисами покажется легкой прогулкой. Тем более, что с игрой в портфолио вы будете выделяться среди других кандидатов на вакансии. Быть разработчиком игр - весело и круто.
С чего начать?
Браузерная игра - достойная идея, но нужно идти в ногу со временем и использовать последние технологии. В этой статье использую и постараюсь раскрыть связку:
- Typescript
- React
- Webpack
- HTML/CSS
- Phaser3
Разумеется, помимо технических навыков следует вспомнить базовые понятия:
- Математики
- Физики
- Компьютерной графики
Школьного уровня понимания этих предметов вполне достаточно. В качестве художественных материалов можно взять готовые ресурсы, спрайты, модели из других игр в свободном доступе, например warcraft 2.
Почему Phaser3?
Потому что на данный момент это самый часто используемый и активно развивающийся open-source фреймворк для разработки браузерных игр и интерактивных приложений на JavaScript/TypeScript
Какие будут ваши доказательства?
На официальных ресурсах Phaser можно найти бесчисленное количество примеров кода, игр и best-практик. Также среди достоинств: регулярные обновления и новые фичи, огромное комьюнити разработчиков, открытая и полная документация, доступны книги от создателя фреймворка Richard Davey @photonstorm.
Практика
Выше представлена ссылка на демо проекта. Теперь по порядку.
Требования: NodeJS >= v20, NPM >= v10
Для начала, выгружаем проект. Устанавливаем зависимости и запускаем:
Демо содержит 2 связанные, но изначально не особо ладящие друг с другом, технологии - React и Phaser. Для того, чтобы они работали вместе без проблем, в Index.html объявлено 2 разных контейнера, каждый из них привязывает свой фреймворк соответственно:
Заметьте, что контейнер React с id ="root" находится первым, на нем будет строиться все UI проекта, блок с z-index отличным от нуля(для отрисовки UI поверх игровых сцен), нестатический и позиционированный, что добавляет удобства в верстке. В блоке id="game-root" используется только canvas, поэтому можно пожертвовать его позиционированием, прилепляем его к вернему левому краю абсолютным позиционированием.
Любая Phaser игра начинается с конфигурации фреймворка.
phaser-game.ts :
Все параметры, впринципе, должны быть интуитивно понятны, но самый главный из них это набор сцен:
scene: [BootstrapScene, GameScene]
Сцены - основной объект для отрисовки игрового содержимого, через нее проходят все ресурсы, события и процессы в игре. Первая из них используется в качестве предзагрузчика. Все загруженные в первой сцене ресурсы будут доступны в других. Ресурсы могут быть разные, это и спрайт-листы, и атласы анимаций, и звуковые файлы, Tilemap-файлы, шейдеры и пр.
Любая сцена имеет 4 важных функции, изменяя которые, можно управлять игровой логикой:
- preload - загружает ресурсы, и это все.
- init - запускается следом. Позволяет получить данные при переходе из предыдущей сцены, инициализирует игровую логику.
- create - позволяет создать объекты и привязать их к сцене. Большинство игровых объектов достаточно просто объявить в этом методе. Под капотом они сами обновляются в игровом цикле.
- update - игровой цикл. Здесь можно добавить дополнительную логику, когда базового функционала метода create уже не хватает.
В конструкторе передается строковый ключ этой сцены.
Сцена данного предзагрузчика также имеет функционал, позволяющий показать прогресс загрузки всех прописанных ресурсов, выводя данные с помощью глобального объекта контроля React компонентов CONTROLS, но об этом позднее. Также прописываем инструкцию проигрывания музыки на старте:
Главная сцена, на которой будет строиться весь геймплей - GameScene.
Рассмотрим метод create
Для отрисовки анимаций, персонажей и многих других игровых объектов, в большинстве случаев используются спрайты.
Спрайт - это миниатюрный игровой "контейнер" текстур и анимаций с различными параметрами: координаты позиции на игровом поле, скорости, ускорения движения и др. Например:
Для создания анимации, необходимо после загрузки ресурсов также указать последовательность кадров и связать ее с уникальным ключом.
Создадим анимацию взрыва из 20 нарезанных сверху-вниз, слева-направо кадров текстуры fireballBlast:
Ширина и высота кадра, а также ключ текстуры берется из загрузки на предыдущей сцене:
Далее создадим спрайт в точке (2500, 1100) и запустим анимацию "explosion" при помощи функции play
Для создания персонажа используем функцию this.createPlayer()
Где персонаж является объектом класса Mage
В свою очередь он наследуется от класса Player с логикой анимирования движущегося и атакующего персонажа в 8 направлениях(взависимости от нажатой клавиши)
Для создания анимаций движения персонажа во всех направлениях и умений по спрайтам:
В том числе, используя атлас анимаций мага mage.json, указывающий координаты и размеры конкретного кадра:
Phaser имеет богатый функционал, в том числе удобное манипулирование устройствами ввода. Данная строчка позволяет повесить обработчик нажатия любой кнопки клавиатуры:
Аналогично с мышью, добавляем обработку клика:
Умение, или выстрел - важная составляющая игры. Это абстрактный класс, который сам по себе также является спрайтом и содержит функции отрисовки анимации. Количество текстур не имеет значения. Метод play так или иначе запустит нужную анимацию.
Огненный шар
Баф
Также стоит упомянуть механику обработки столкновений в игре. Для этого используется функционал аркадного физического движка.
Создадим предмет - лицо, как группу предметов, для его последующего респауна по истечению 3х секунд после столкновения персонажа с ним.
Отрисовка игрового поля
Игровая карта представляет собой набор файлов: map01merged.json, tiles.png, tiles.tsx ( не путать с typescript tsx файлом).
В качестве редактора уровней использовался - Tiled, предназначеный для построения любых, в том числе изометрических уровней, карт, на основе тайлов и тайлсетов.
Богатая поддержка Tiled в Phaser позволяет гибко оперировать с самими тайлами карты - клетками. Их можно заменять, удалять, применять эффекты и обработку коллизий игровых объектов с ними.
Рендеринг карты очень простой
Пользовательский интерфейс
Как я уже сказал, взаимодействие игрока происходит через устройства ввода и пользовательский интерфейс, который представляет из себя обычные React компоненты.
Чтобы отобразить отладочную информацию в левом верхнем углу экрана необходимо:
Объявить компонент с отладочной информацией
Связать хуки компонента с глобальным объектом CONTROLS, зарегистрировав их
Объявить необходимый регистратор в файле controls.ts
И спокойно вызывать из игровой сцены
Точно таким же компонентом является и форма входа в игру:
Для отключения событий клика по блоку React компонентов достаточно поправить свойство "pointer-events":
Значение этого css-свойства можно изменить в конкретных местах там, где обработка клика необходима (кнопки, формы и т.д.)
Вебсокеты
В данном демо также имеется поддержка работы с вебсокетами. Для работы с ними есть файл network.ts
Для отправки сообщения на сервер достаточно вызвать метод send из любого места приложения:
Для обработки входящего сообщения достаточно объявить где-нибудь обработчик вида:
Итог
Демо игры получилось достаточно бодрым. Полученные навыки в ходе разработки пусть даже такого небольшого демо - бесценны. Теперь вы без проблем можете создать свой собственный вариант игры, постепенно расширяя ее функционал.
В скором времени выйдет статья, раскрывающая backend мултьтиплееров
Делитесь материалом с коллегами, пишите комментарии на какую тему хотели бы увидеть материал