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

Переключение между игровыми сценами — это не всегда резкая смена экрана. Phaser предлагает мощный API для создания плавных и визуально привлекательных переходов, которые можно интегрировать даже в физический симулятор. Эта статья покажет, как использовать метод `scene.transition()` для анимированного перехода между двумя сценами, в каждой из которых активна физика Matter.js. Этот подход полезен для создания эффектных пауз, меню или смены игровых локаций без потери интерактивности текущей сцены.

Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.

Живой запуск

Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.

Исходный код


class SceneA extends Phaser.Scene
{
    constructor ()
    {
        super({ key: 'sceneA' });
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('ball1', 'assets/sprites/pangball.png');
    }

    create ()
    {
        this.matter.world.setBounds(0, 0, 800, 600, 32, true, true, false, true);

        for (let i = 0; i < 64; i++)
        {
            const ball = this.matter.add.image(Phaser.Math.Between(100, 700), Phaser.Math.Between(-600, 0), 'ball1');
            ball.setCircle();
            ball.setFriction(0.005);
            ball.setBounce(1);
        }

        this.input.once('pointerdown', function (event)
        {

            this.scene.transition({ target: 'sceneB', duration: 2000 });

        }, this);
    }
}

class SceneB extends Phaser.Scene
{
    constructor ()
    {
        super({ key: 'sceneB' });
    }

    preload ()
    {
        // this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('ball2', 'assets/sprites/shinyball.png');
    }

    create ()
    {
        this.matter.world.setBounds(0, 0, 800, 600, 32, true, true, false, true);

        for (let i = 0; i < 64; i++)
        {
            const ball = this.matter.add.image(Phaser.Math.Between(100, 700), Phaser.Math.Between(-600, 0), 'ball2');
            ball.setCircle();
            ball.setFriction(0.005);
            ball.setBounce(1);
        }

        this.input.once('pointerdown', function (event)
        {

            this.scene.transition({ target: 'sceneA', duration: 2000 });

        }, this);
    }
}

const config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    backgroundColor: '#000000',
    parent: 'phaser-example',
    scene: [ SceneA, SceneB ],
    physics: {
        default: 'matter'
    }
};

const game = new Phaser.Game(config);

Настройка сцены и загрузка ассетов

Каждая сцена в примере является самостоятельным классом, расширяющим Phaser.Scene. В конструкторе задается уникальный ключ сцены, который используется для ссылок.

В методе preload() задается базовый URL для загрузки и загружается одно изображение для спрайтов. Обратите внимание, что SceneB закомментировала установку базового URL, так как он уже был установлен в SceneA и сохраняется между сценами.

class SceneA extends Phaser.Scene
{
    constructor ()
    {
        super({ key: 'sceneA' });
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('ball1', 'assets/sprites/pangball.png');
    }

Создание физического мира и объектов

В методе create() происходит основная настройка. Сначала устанавливаются границы физического мира Matter.js с помощью this.matter.world.setBounds(). Последние аргументы true, true, false, true определяют, какие границы (верхняя, правая, нижняя, левая) будут иметь физические тела (стены).

Затем в цикле создаются 64 физических спрайта с помощью this.matter.add.image(). Каждому шару задается форма окружения (setCircle()), очень низкое трение (setFriction(0.005)) и упругий отскок (setBounce(1)). Шары появляются в случайных позициях по X и выше верхней границы экрана по Y, что создает эффект 'падения'.

create ()
{
    this.matter.world.setBounds(0, 0, 800, 600, 32, true, true, false, true);

    for (let i = 0; i < 64; i++)
    {
        const ball = this.matter.add.image(Phaser.Math.Between(100, 700), Phaser.Math.Between(-600, 0), 'ball1');
        ball.setCircle();
        ball.setFriction(0.005);
        ball.setBounce(1);
    }

Инициирование перехода между сценами

Ключевой момент — запуск перехода по событию пользователя. Используя this.input.once('pointerdown', ...), мы назначаем одноразовый обработчик клика или касания.

Внутри обработчика вызывается метод this.scene.transition(). Он принимает объект конфигурации, где target — это ключ целевой сцены (например, 'sceneB'), а duration — длительность перехода в миллисекундах (здесь 2000 мс или 2 секунды). Phaser сам управляет анимацией затемнения/появления.

this.input.once('pointerdown', function (event)
    {
        this.scene.transition({ target: 'sceneB', duration: 2000 });
    }, this);
}

Конфигурация игры и физики

Общая конфигурация игры определяет рендерер, размеры, фон и, что важно, систему физики. Указание physics: { default: 'matter' } активирует для всех сцен в проекте движок Matter.js.

В свойстве scene массивом передаются классы сцен. Phaser создаст их экземпляры в порядке перечисления, но активной будет первая сцена (SceneA).

const config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    backgroundColor: '#000000',
    parent: 'phaser-example',
    scene: [ SceneA, SceneB ],
    physics: {
        default: 'matter'
    }
};

const game = new Phaser.Game(config);

Что попробовать дальше

Метод scene.transition() предоставляет простой и эффективный способ создания плавных переходов между сценами, которые продолжают жить своей физической жизнью во время анимации. Это отлично подходит для игр, где важно сохранять визуальную непрерывность. Для экспериментов попробуйте: изменить длительность перехода; использовать другие события для запуска (например, таймер или столкновение); добавить пользовательскую функцию обратного вызова onUpdate для управления прозрачностью или масштабом сцен во время перехода; или комбинировать это с камерой для эффектов панорамирования.