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

Управление звуком — ключевой элемент игровой атмосферы. В этом примере мы разберем, как создать простой интерактивный аудиоплеер в Phaser, который воспроизводит различные звуковые дорожки по нажатию клавиш A-G и останавливает всю музыку пробелом. Вы научитесь правильно загружать аудиофайлы, создавать и управлять звуковыми объектами, а также обрабатывать пользовательский ввод с клавиатуры. Этот паттерн полезен не только для музыкальных демо, но и для создания систем звуковых эффектов в вашей игре.

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

Живой запуск

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

Исходный код


class Example extends Phaser.Scene
{
    preload ()
    {
        this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
        this.load.image('touhou', 'assets/pics/touhou1.png');

        this.load.setPath('assets/audio/tech');

        this.load.audio('bass', [ 'bass.ogg', 'bass.mp3' ]);
        this.load.audio('drums', [ 'drums.ogg', 'drums.mp3' ]);
        this.load.audio('percussion', [ 'percussion.ogg', 'percussion.mp3' ]);
        this.load.audio('synth1', [ 'synth1.ogg', 'synth1.mp3' ]);
        this.load.audio('synth2', [ 'synth2.ogg', 'synth2.mp3' ]);
        this.load.audio('top1', [ 'top1.ogg', 'top1.mp3' ]);
        this.load.audio('top2', [ 'top2.ogg', 'top2.mp3' ]);
    }

    create ()
    {
        this.add.image(790, 600, 'touhou').setOrigin(1);

        const bass = this.sound.add('bass');
        const drums = this.sound.add('drums');
        const percussion = this.sound.add('percussion');
        const synth1 = this.sound.add('synth1');
        const synth2 = this.sound.add('synth2');
        const top1 = this.sound.add('top1');
        const top2 = this.sound.add('top2');

        const keys = [
            'Press A for Bass',
            'Press B for Drums',
            'Press C for Percussion',
            'Press D for Synth1',
            'Press E for Synth2',
            'Press F for Top1',
            'Press G for Top2',
            '',
            'SPACE to stop all sounds'
        ];

        const text = this.add.text(10, 10, keys, { font: '32px Courier', fill: '#00ff00' });

        if (this.sound.locked)
        {
            text.setText('Click to start');

            this.sound.once('unlocked', () =>
            {
                text.setText(keys);
            });
        }

        this.input.keyboard.on('keydown-SPACE', function ()
        {
            this.sound.stopAll();
        }, this);

        this.input.keyboard.on('keydown-A', () =>
        {
            bass.play();
        });

        this.input.keyboard.on('keydown-B', () =>
        {
            drums.play();
        });

        this.input.keyboard.on('keydown-C', () =>
        {
            percussion.play();
        });

        this.input.keyboard.on('keydown-D', () =>
        {
            synth1.play();
        });

        this.input.keyboard.on('keydown-E', () =>
        {
            synth2.play();
        });

        this.input.keyboard.on('keydown-F', () =>
        {
            top1.play();
        });

        this.input.keyboard.on('keydown-G', () =>
        {
            top2.play();
        });
    }
}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    scene: Example
};

const game = new Phaser.Game(config);

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

Перед использованием любых звуков их необходимо загрузить. В фазе preload мы устанавливаем базовый URL и путь для аудиофайлов, а затем загружаем каждый звук в двух форматах (OGG и MP3) для кросс-браузерной совместимости.

this.load.setBaseURL('https://raw.githubusercontent.com/phaserjs/examples/master/public/');
this.load.setPath('assets/audio/tech');

this.load.audio('bass', [ 'bass.ogg', 'bass.mp3' ]);
this.load.audio('drums', [ 'drums.ogg', 'drums.mp3' ]);

Создание и хранение звуковых объектов

В фазе create мы создаем экземпляры звуков для каждого загруженного ключа с помощью метода this.sound.add(). Каждый экземпляр сохраняется в константу для последующего воспроизведения. Важно отметить, что метод .add() лишь создает объект звука, но не начинает его проигрывать.

const bass = this.sound.add('bass');
const drums = this.sound.add('drums');
const percussion = this.sound.add('percussion');

Обработка политики автовоспроизведения браузеров

Современные браузеры блокируют автовоспроизведение аудио без взаимодействия пользователя. Phaser предоставляет свойство this.sound.locked для проверки этого состояния. Если звук заблокирован, мы показываем подсказку 'Click to start' и ждем события unlocked, после которого заменяем текст на список управляющих клавиш.

if (this.sound.locked)
{
    text.setText('Click to start');
    this.sound.once('unlocked', () =>
    {
        text.setText(keys);
    });
}

Привязка воспроизведения звуков к клавиатуре

Ядро примера — обработка событий клавиатуры. Для каждой клавиши (A-G) мы создаем слушатель события keydown-*, который вызывает метод .play() у соответствующего звукового объекта. Это позволяет мгновенно запускать звук при нажатии.

this.input.keyboard.on('keydown-A', () =>
{
    bass.play();
});

Глобальное управление звуком

Помимо воспроизведения отдельных дорожек, важна возможность мгновенной остановки всей музыки. Для этого мы навешиваем обработчик на клавишу SPACE, который вызывает метод this.sound.stopAll(). Этот метод останавливает все звуки, управляемые менеджером SoundManager текущей сцены.

this.input.keyboard.on('keydown-SPACE', function ()
{
    this.sound.stopAll();
}, this);

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

Вы создали основу для интерактивной звуковой системы в Phaser. Этот паттерн можно расширить: добавьте переключение звуков на другие клавиши, реализуйте цикличное воспроизведение (loop: true), регулировку громкости или эффекты панорамирования. Попробуйте применить этот подход для озвучки действий персонажа (прыжок, выстрел) или создания динамического саундтрека, который меняется в зависимости от игровых событий.