О чем этот пример
Управление множеством звуковых эффектов — частая задача в геймдеве. Загружать каждый файл отдельно неудобно и может замедлить загрузку игры. В этой статье мы разберем пример из официальной документации Phaser, который демонстрирует мощный инструмент — аудиоспрайты. Вы научитесь объединять несколько звуков в один файл и управлять ими по имени, что упростит код и оптимизирует производительность вашего проекта.
Версия 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);
Что такое аудиоспрайт и зачем он нужен
Аудиоспрайт (Audio Sprite) — это концепция, похожая на графические спрайтшиты. Несколько коротких звуковых эффектов объединяются в один большой аудиофайл, а отдельный JSON-файл описывает временные метки для каждого звука (его начало и длительность).
Это дает два ключевых преимущества: 1. **Снижение количества HTTP-запросов:** Вместо десятков отдельных файлов загружаются всего два (аудио и JSON). Это ускоряет загрузку игры. 2. **Удобство управления:** В коде вы обращаетесь к звукам по их логическому имени (например, 'laser' или 'explosion'), а не по имени файла. Phaser сам «вырезает» нужный фрагмент из общего файла.
В примере используется файл fx_mixdown.json, который содержит описание спрайтшита, и соответствующие ему аудиофайлы в форматах OGG и MP3 для кросс-браузерной совместимости.
Загрузка ресурсов: ключевой метод `load.audioSprite`
Основная магия происходит на этапе предзагрузки в методе preload. Вместо загрузки кучи файлов используется один вызов.
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 автоматически выберет подходящий формат, который поддерживает браузер пользователя (OGG часто для Firefox и Chrome, MP3 для Safari).
После загрузки данные из JSON можно получить через кэш: this.cache.json.get('sfx').
Динамическое создание интерфейса из данных спрайта
В методе create происходит чтение данных аудиоспрайта и создание на их основе интерфейса — кнопок для воспроизведения каждого звука.
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++;
}
Код получает объект spritemap из загруженного JSON. Этот объект содержит свойства, где ключ — это имя звука (например, 'saw'), а значение — его параметры (start, end).
Цикл for...in проходит по всем именам звуков в spritemap. Для каждого имени вызывается метод makeButton, который создает интерактивную кнопку. Позиция кнопки по вертикали (`y) увеличивается с каждой итерацией (i * 40`), что выстраивает кнопки в столбик.
Воспроизведение звука и обработка событий кнопки
Сердце примера — обработка событий ввода. Кнопки реагируют на наведение и клик, а при нажатии проигрывается соответствующий звук.
this.input.on('gameobjectdown', function (pointer, button)
{
this.sound.playAudioSprite('sfx', button.name);
this.setButtonFrame(button, 2);
}, this);
Обратите внимание на ключевой метод this.sound.playAudioSprite. Он принимает два аргумента:
1. Ключ аудиоспрайта ('sfx'), который был задан при загрузке.
2. Имя конкретного звука (button.name), которое было присвоено кнопке при создании и соответствует ключу в spritemap.
Phaser сам находит в общем аудиофайле нужный отрезок времени и воспроизводит его. Это изящно скрывает всю внутреннюю сложность от разработчика.
Метод setButtonFrame меняет кадр спрайтшита кнопки, чтобы визуально показать ее состояние (обычная, наведенная, нажатая).
Что попробовать дальше
Аудиоспрайты в Phaser — это элегантное и производительное решение для управления звуковыми эффектами. Они позволяют держать код чистым, а ресурсы — организованными. Для экспериментов попробуйте: создать свой аудиоспрайт с помощью инструментов вроде audiosprite или подобных; добавить настройку громкости или панорамирования для отдельных звуков в спрайте; привязать воспроизведение не к клику мыши, а к игровым событиям, например, столкновению или сбору предмета.
