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

Управление десятками мелких звуковых эффектов — частый вызов в геймдеве. Загрузка каждого эффекта отдельным файлом усложняет загрузку и управление. В этой статье разберем, как использовать аудиоспрайты в Phaser — технику, которая упаковывает множество звуков в один файл и JSON-описание. Это упрощает загрузку, кеширование и воспроизведение звуков через единый интерфейс `sound.playAudioSprite()`.

Версия 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('title', 'assets/pics/catastrophi.png');

        this.load.spritesheet('button', 'assets/ui/flixel-button.png', { frameWidth: 80, frameHeight: 20 });

        this.load.bitmapFont('nokia', 'assets/fonts/bitmap/nokia16black.png', 'assets/fonts/bitmap/nokia16black.xml');

        this.load.audioSprite('sfx', 'assets/audio/SoundEffects/fx_mixdown.json', [ 'assets/audio/SoundEffects/fx_mixdown.ogg', 'assets/audio/SoundEffects/fx_mixdown.mp3' ]);
    }

    create ()
    {
        this.add.image(400, 300, 'title');

        const spritemap = this.cache.json.get('sfx').spritemap;

        let i = 0;
        for (const spriteName in spritemap)
        {
            if (!spritemap.hasOwnProperty(spriteName))
            {
                continue;
            }

            this.makeButton.call(this, spriteName, 680, 115 + i * 40);

            i++;
        }

        this.input.on('gameobjectover', (pointer, button) =>
        {
            this.setButtonFrame(button, 0);
        });
        this.input.on('gameobjectout', (pointer, button) =>
        {
            this.setButtonFrame(button, 1);
        });
        this.input.on('gameobjectdown', function (pointer, button)
        {
            this.sound.playAudioSprite('sfx', button.name);

            this.setButtonFrame(button, 2);

        }, this);
        this.input.on('gameobjectup', (pointer, button) =>
        {
            this.setButtonFrame(button, 0);
        });
    }

    makeButton (name, x, y)
    {
        const button = this.add.image(x, y, 'button', 1).setInteractive();
        button.name = name;
        button.setScale(2, 1.5);

        const text = this.add.bitmapText(x - 40, y - 8, 'nokia', name, 16);
        text.x += (button.width - text.width) / 2;
    }

    setButtonFrame (button, frame)
    {
        button.frame = button.scene.textures.getFrame('button', frame);
    }
}

/**
 * @author    Pavle Goloskokovic <pgoloskokovic@gmail.com> (http://prunegames.com)
 */

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Example,
    pixelArt: true
};

const game = new Phaser.Game(config);

Что такое аудиоспрайт и зачем он нужен

Аудиоспрайт по концепции похож на графический спрайтлист. Это один аудиофайл, содержащий несколько звуковых эффектов, и JSON-файл с метаданными. В метаданных для каждого эффекта указано его имя, временная позиция в файле и длительность.

**Преимущества подхода:** * **Сокращение HTTP-запросов:** Загружается один файл вместо десятков. * **Простота управления:** Все эффекты загружены одним вызовом load.audioSprite. * **Централизованное воспроизведение:** Используется единый метод playAudioSprite.

В примере используется файл fx_mixdown.json, который описывает структуру аудиоспрайта sfx.

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

В методе preload загружаются все необходимые ресурсы. Ключевой момент — использование load.audioSprite.

this.load.audioSprite('sfx', 'assets/audio/SoundEffects/fx_mixdown.json', [ 'assets/audio/SoundEffects/fx_mixdown.ogg', 'assets/audio/SoundEffects/fx_mixdown.mp3' ]);

Здесь: 1. 'sfx' — ключ, по которому аудиоспрайт будет доступен в кеше. 2. Второй аргумент — путь к JSON-файлу с описанием спрайтов. 3. Третий аргумент — массив путей к самим аудиофайлам в разных форматах (для кроссбраузерной совместимости). Phaser автоматически выберет подходящий.

Также загружаются изображение для фона, спрайтшит для кнопок и bitmap-шрифт для текста.

Извлечение списка эффектов и создание интерфейса

В create мы получаем доступ к метаданным аудиоспрайта и создаем кнопку для каждого звукового эффекта.

const spritemap = this.cache.json.get('sfx').spritemap;

Из кеша JSON мы извлекаем объект по ключу 'sfx' и получаем свойство spritemap. Это объект, где ключи — имена эффектов (например, 'bass', 'snare'), а значения — данные о их расположении в аудиофайле.

Далее в цикле for...in для каждого имени эффекта вызывается метод makeButton, который создает интерактивное изображение-кнопку и подписывает его bitmap-текстом.

this.makeButton.call(this, spriteName, 680, 115 + i * 40);

Важно: кнопке через свойство .name присваивается имя звукового спрайта. Это позволит позднее идентифицировать, какой звук нужно проиграть.

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

Ядро логики — обработка событий мыши (или касания) для кнопок. События gameobjectover, out, down, up навешиваются на сцену и срабатывают для любого интерактивного игрового объекта.

**Ключевой обработчик — gameobjectdown:**

this.input.on('gameobjectdown', function (pointer, button)
{
    this.sound.playAudioSprite('sfx', button.name);
    this.setButtonFrame(button, 2);
}, this);
Здесь:
1.  `this.sound.playAudioSprite('sfx', button.name)` — метод, который проигрывает конкретный звуковой спрайт. Первый аргумент — ключ аудиоспрайта, второй — имя конкретного эффекта, которое мы сохранили в `button.name`.
2.  `this.setButtonFrame(button, 2)` — меняет кадр спрайтшита кнопки, чтобы визуально показать нажатие.

Остальные обработчики меняют кадр кнопки для отображения состояния hover и up.

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

Аудиоспрайты в Phaser — это мощный и элегантный способ управления звуковыми эффектами. Они решают проблему дробных загрузок и упрощают код. Для экспериментов попробуйте: создать свой аудиоспрайт из набора WAV-файлов с помощью инструментов вроде audiosprite; управлять громкостью или темпом воспроизведения отдельных спрайтов через параметры config в методе playAudioSprite; или привязать воспроизведение эффектов не к кнопкам, а к игровым событиям, например, столкновениям физических тел.