О чем этот пример
В этом примере мы рассмотрим, как оживить игровую сцену, совместив статический текстовый контент с динамической графикой. Вы научитесь загружать и отображать большие объемы текста с помощью Bitmap Fonts, а также управлять физикой простого спрайта, заставляя его отскакивать от границ игрового мира. Этот подход полезен для создания титров, прокручивающихся лор-текстов в играх или просто для добавления фоновой анимации, привлекающей внимание игрока.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
var largeText = null;
var bunny = null;
var vx = 4;
var vy = 4;
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.bitmapFont('desyrel', 'assets/fonts/bitmap/desyrel.png', 'assets/fonts/bitmap/desyrel.xml');
this.load.text('loremipsum', 'assets/text/loremipsum.txt');
this.load.image('bunny', 'assets/sprites/bunny.png');
}
create ()
{
largeText = this.add.bitmapText(0, 0, 'desyrel', game.cache.text.get('loremipsum'));
bunny = this.add.sprite(0, 0, 'bunny');
bunny.originX = 0;
bunny.originY = 0;
}
update ()
{
largeText.y -= 0.3;
bunny.x += vx;
bunny.y += vy;
if (bunny.x + bunny.width > 1024)
{
vx *= -1;
bunny.x = 1024 - bunny.width;
}
else if (bunny.x < 0)
{
bunny.x = 0;
vx *= -1;
}
if (bunny.y + bunny.height > 768)
{
vy *= -1;
bunny.y = 768 - bunny.height;
}
else if (bunny.y < 0)
{
bunny.y = 0;
vy *= -1;
}
}
}
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 1024,
height: 768,
scene: Example
};
const game = new Phaser.Game(config);
Загрузка ресурсов: шрифты, текст и изображения
В методе preload происходит подготовка всех необходимых для сцены ресурсов. Ключевой особенностью является загрузка bitmap-шрифта и текстового файла.
preload ()
{
this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.bitmapFont('desyrel', 'assets/fonts/bitmap/desyrel.png', 'assets/fonts/bitmap/desyrel.xml');
this.load.text('loremipsum', 'assets/text/loremipsum.txt');
this.load.image('bunny', 'assets/sprites/bunny.png');
}
Метод setBaseURL задает базовый путь для всех последующих загрузок, что упрощает указание относительных путей. load.bitmapFont загружает bitmap-шрифт — это изображение со всеми глифами и XML-файл с их разметкой. Такой шрифт отрисовывается быстрее веб-шрифтов и гарантирует идентичное отображение на всех устройствах. load.text загружает содержимое текстового файла и помещает его в текстовый кеш игры под ключом 'loremipsum'. load.image загружает спрайт — классического кролика Phaser.
Создание игровых объектов: текст и спрайт
В методе create мы создаем и настраиваем основные объекты сцены, используя загруженные ресурсы.
create ()
{
largeText = this.add.bitmapText(0, 0, 'desyrel', game.cache.text.get('loremipsum'));
bunny = this.add.sprite(0, 0, 'bunny');
bunny.originX = 0;
bunny.originY = 0;
}
this.add.bitmapText создает объект Bitmap Text. В качестве контента используется текст, полученный из кеша по ключу 'loremipsum'. game.cache.text.get — это способ доступа к данным, загруженным через load.text. Текст позиционируется в точке (0, 0) — верхнем левом углу сцены.
this.add.sprite создает спрайт кролика. Установка свойств originX и originY в 0 меняет точку привязки (origin) спрайта на его левый верхний угол. Это критически важно для последующих расчетов столкновений с границами, так как теперь позиция спрайта (bunny.x, bunny.y) соответствует координатам его верхнего левого угла, а не центра.
Анимация и физика: движение и отскоки
Логика движения и взаимодействия с миром реализована в методе update, который вызывается на каждом кадре.
update ()
{
largeText.y -= 0.3;
bunny.x += vx;
bunny.y += vy;
if (bunny.x + bunny.width > 1024)
{
vx *= -1;
bunny.x = 1024 - bunny.width;
}
else if (bunny.x < 0)
{
bunny.x = 0;
vx *= -1;
}
if (bunny.y + bunny.height > 768)
{
vy *= -1;
bunny.y = 768 - bunny.height;
}
else if (bunny.y < 0)
{
bunny.y = 0;
vy *= -1;
}
}
Строка largeText.y -= 0.3 плавно поднимает текст вверх, создавая эффект прокрутки.
Движение кролика управляется векторами скорости vx и vy. Каждый кадр его координаты увеличиваются на эти значения. Условия if проверяют столкновения с границами игрового поля (1024x768, как указано в конфиге). При столкновении с правой границей (bunny.x + bunny.width > 1024) скорость по оси X инвертируется (vx *= -1), а позиция спрайта фиксируется у самой границы, чтобы он не уходил за экран. Аналогичная логика работает для левой, нижней и верхней границ. Это создает классический эффект отскока мяча от стен.
Конфигурация игры: настройка сцены и рендерера
Конфигурационный объект определяет основные параметры игрового экземпляра Phaser.
const config = {
type: Phaser.WEBGL,
parent: 'phaser-example',
width: 1024,
height: 768,
scene: Example
};
const game = new Phaser.Game(config);
Поле type задает рендерер. Phaser.WEBGL предпочтительнее для производительности, если он поддерживается браузером. parent — это ID HTML-элемента, в который будет встроен canvas игры. width и height задают внутреннее разрешение игрового мира. Важно, что именно эти значения (1024 и 768) используются в расчетах столкновений в методе update. scene указывает на класс, который будет использован в качестве основной сцены. Создание экземпляра игры new Phaser.Game(config) запускает весь жизненный цикл.
Что попробовать дальше
Этот пример демонстрирует базовый, но мощный паттерн: разделение загрузки, создания и обновления логики. Вы можете экспериментировать, изменяя скорость прокрутки текста или траекторию движения спрайта. Попробуйте заменить текст на собственную историю, изменить bitmap-шрифт или добавить больше спрайтов с разными векторами скорости для создания сложной фоновой анимации. Также можно привязать движение текста к позиции кролика, создав более тесную связь между элементами сцены.
