О чем этот пример

В этом примере мы рассмотрим, как оживить игровую сцену, совместив статический текстовый контент с динамической графикой. Вы научитесь загружать и отображать большие объемы текста с помощью 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-шрифт или добавить больше спрайтов с разными векторами скорости для создания сложной фоновой анимации. Также можно привязать движение текста к позиции кролика, создав более тесную связь между элементами сцены.