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

Включение видеороликов может значительно обогатить игровой опыт, будь то кат-сцены, фоновые заставки или динамические текстуры. Однако современные браузеры строго регулируют автовоспроизведение видео со звуком, что может привести к неожиданным ошибкам. В этой статье мы разберём пример из официальной документации Phaser, который демонстрирует не только базовую загрузку и воспроизведение видео, но и правильную обработку блокировок браузера, а также реализацию интерактивного управления (пауза/продолжение) с помощью клика мыши. Вы научитесь создавать надёжный видеоплеер, готовый к реалиям веб-разработки.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.video('spaceace', 'assets/video/spaceace.mp4');
    }

    create ()
    {
        const intro = this.add.video(640, 360, 'spaceace');

        //  Depending on browser settings video playback is locked
        //  from autoplaying because this video has audio.

        //  Catch the event here. Playback will start
        //  as soon as you click on the game (regardless if you have a
        //  click handler or not). Here we use it to display a message.

        intro.on('locked', () => {

            let message = this.add.text(640, 100, 'Click to play video', { font: '32px Courier', fill: '#00ff00' }).setShadow(1, 1).setOrigin(0.5);

            intro.on('unlocked', () => {

                message.destroy();

            });

        });

        intro.play();

        //  Listen for the 'play' event to create our pause/resume handler
        intro.once('play', () => {

            this.input.on('pointerdown', () => {

                if (intro.isPlaying())
                {
                    intro.pause();
                }
                else
                {
                    intro.resume();
                }

            });

        });
    }
}

const config = {
    type: Phaser.AUTO,
    width: 1280,
    height: 720,
    backgroundColor: '#000000',
    parent: 'phaser-example',
    scene: Example
};

let game = new Phaser.Game(config);

Загрузка видеоресурса

Как и любой другой ресурс, видео необходимо предзагрузить в методе preload(). Для этого используется метод this.load.video(). Первый аргумент — это уникальный ключ ассета, который будет использоваться для его создания в сцене. Второй аргумент — путь к файлу.

preload ()
{
    this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
    this.load.video('spaceace', 'assets/video/spaceace.mp4');
}

В примере сначала задаётся базовый URL для загрузчиков сцены с помощью this.load.setBaseURL(). Это позволяет указывать относительные пути для всех последующих загрузок, что удобно для организации ресурсов.

Создание видеообъекта и проблема блокировки

Создать видеообъект на сцене так же просто, как и спрайт, с помощью метода this.add.video(). Ему передаются координаты (X, Y) и ключ загруженного видео.

const intro = this.add.video(640, 360, 'spaceace');
intro.play();

Однако вызов intro.play() может не сработать мгновенно. Браузеры блокируют автовоспроизведение видео, если у них есть звуковая дорожка, пока пользователь не взаимодействовал со страницей. В Phaser это состояние называется 'locked' (заблокировано). Чтобы корректно обработать эту ситуацию, нужно подписаться на событие 'locked' объекта видео.

intro.on('locked', () => {
    let message = this.add.text(640, 100, 'Click to play video', { font: '32px Courier', fill: '#00ff00' }).setShadow(1, 1).setOrigin(0.5);
    intro.on('unlocked', () => {
        message.destroy();
    });
});

В обработчике 'locked' мы создаём текстовое сообщение, информирующее пользователя о необходимости клика. Как только пользователь взаимодействует со страницей и блокировка снимается, срабатывает событие 'unlocked', и сообщение уничтожается методом .destroy().

Интерактивное управление воспроизведением

После того как видео начало воспроизводиться (событие 'play'), мы можем добавить более сложную логику управления. В примере по клику мыши видео ставится на паузу или возобновляется.

intro.once('play', () => {
    this.input.on('pointerdown', () => {
        if (intro.isPlaying())
        {
            intro.pause();
        }
        else
        {
            intro.resume();
        }
    });
});

Обратите внимание на несколько ключевых моментов: 1. Используется intro.once('play', ...), чтобы подписаться на событие первого запуска видео только один раз и не дублировать обработчики. 2. Внутри этого события на глобальный клик (this.input.on('pointerdown', ...)) вешается обработчик. 3. Метод intro.isPlaying() возвращает true, если видео в данный момент проигрывается, и false, если оно на паузе или остановлено. 4. В зависимости от состояния, вызываются методы .pause() или .resume().

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

Код завершается стандартной для Phaser конфигурацией игры и её инстанцированием. Важно убедиться, что размеры сцены (width, height) соответствуют ожидаемому разрешению видео и координатам размещения элементов интерфейса (например, текстового сообщения).

const config = {
    type: Phaser.AUTO,
    width: 1280,
    height: 720,
    backgroundColor: '#000000',
    parent: 'phaser-example',
    scene: Example
};

let game = new Phaser.Game(config);

Здесь создаётся игровой экран размером 1280x720 пикселей с чёрным фоном. Класс нашей сцены Example передаётся в свойство scene конфигурации.

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

Работа с видео в Phaser требует учёта не только игрового API, но и ограничений веб-платформы. Основной паттерн: загрузить, создать, попытаться воспроизвести и обязательно обработать возможную блокировку. После успешного старта вы получаете полный контроль над плеером. Для экспериментов попробуйте: зациклить видео с помощью intro.setLoop(true); использовать видео в качестве текстуры для Sprite или Plane; добавить управление громкостью через intro.setVolume(); или реализовать перемотку, изменяя свойство intro.currentTime.