О чем этот пример
В играх часто требуется множество коротких звуковых эффектов, таких как выстрелы, прыжки или подбор предметов. Загрузка каждого эффекта отдельным файлом может быть неэффективной и замедлять работу игры. Audio Sprite в Phaser решает эту проблему, объединяя несколько звуков в один аудиофайл с метаданными, подобно спрайт-листам для графики. Эта статья покажет, как загрузить и воспроизвести такой "звуковой атлас", создав удобную панель для тестирования всех эффектов в одном месте. Этот подход оптимизирует загрузку и управление аудиоресурсами.
Версия Phaser: код и демо в этой статье рассчитаны на Phaser 3.90.0.
Живой запуск
Ниже встроен рабочий билд примера. Оригинальный источник: GitHub.
Исходный код
/**
* @author Pavle Goloskokovic <pgoloskokovic@gmail.com> (http://prunegames.com)
*/
class Example extends Phaser.Scene
{
constructor ()
{
super();
}
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 (let spriteName in spritemap)
{
if (!spritemap.hasOwnProperty(spriteName))
{
continue;
}
this.makeButton(spriteName, 680, 115 + i*40);
i++;
}
this.input.on('gameobjectover', function (pointer, button)
{
this.setButtonFrame(button, 0);
}, this);
this.input.on('gameobjectout', function (pointer, button)
{
this.setButtonFrame(button, 1);
}, this);
this.input.on('gameobjectdown', function (pointer, button)
{
this.sound.playAudioSprite('sfx', button.name);
this.setButtonFrame(button, 2);
}, this);
this.input.on('gameobjectup', function (pointer, button)
{
this.setButtonFrame(button, 0);
}, this);
}
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);
}
}
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example,
pixelArt: true,
audio: {
disableWebAudio: true
}
};
const game = new Phaser.Game(config);
Загрузка Audio Sprite и ресурсов
В методе preload() загружаются все необходимые ресурсы для сцены. Ключевой момент — использование this.load.audioSprite для загрузки аудиоспрайта. Этот метод принимает уникальный ключ, путь к JSON-файлу с метаданными и массив путей к самим аудиофайлам в разных форматах для совместимости с браузерами. JSON-файл описывает временные отрезки (спрайты) внутри единого аудиофайла, каждый со своим именем и временными метками начала и окончания.
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() создаётся интерфейс для тестирования звуков. Сначала из кэша извлекается объект spritemap, который содержит описание всех звуковых спрайтов, загруженных по ключу 'sfx'. Затем для каждого имени звука в этом объекте создаётся кнопка с помощью метода makeButton. Важно, что имя звука (spriteName) присваивается свойству button.name — это позволит позже идентифицировать, какой звук нужно проиграть.
const spritemap = this.cache.json.get('sfx').spritemap;
let i = 0;
for (let spriteName in spritemap)
{
if (!spritemap.hasOwnProperty(spriteName))
{
continue;
}
this.makeButton(spriteName, 680, 115 + i*40);
i++;
}
Далее настраиваются обработчики событий ввода для интерактивных кнопок. События gameobjectover, gameobjectout, gameobjectdown и gameobjectup меняют кадр кнопки для визуальной обратной связи. Ключевое событие — gameobjectdown: при клике на кнопку вызывается this.sound.playAudioSprite('sfx', button.name), который воспроизводит конкретный звуковой спрайт из общего файла по его имени.
this.input.on('gameobjectdown', function (pointer, button)
{
this.sound.playAudioSprite('sfx', button.name);
this.setButtonFrame(button, 2);
}, this);
Вспомогательные методы: makeButton и setButtonFrame
Метод makeButton создаёт визуальный элемент кнопки. Он создаёт изображение из спрайтшита, делает его интерактивным, задаёт масштаб и добавляет текстовую метку с именем звука с помощью bitmap-шрифта. Имя звука сохраняется в свойстве name объекта кнопки.
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 изменяет кадр (frame) кнопки, что используется для визуальной обратной связи при наведении и клике. Он напрямую обращается к текстуре и устанавливает нужный кадр.
setButtonFrame(button, frame)
{
button.frame = button.scene.textures.getFrame('button', frame);
}
Конфигурация игры и важная настройка audio
Конфигурация игры включает стандартные параметры, такие как тип рендерера, размеры и основную сцену. Особое внимание стоит обратить на свойство audio. В этом примере установлен флаг disableWebAudio: true. Это означает, что Phaser будет использовать HTML5 Audio API вместо Web Audio API. Это может быть необходимо для совместимости в некоторых специфических средах или для работы с аудиоспрайтами определённым образом, но в большинстве современных проектов рекомендуется использовать Web Audio API (значение по умолчанию) из-за его больших возможностей и лучшего контроля.
const config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scene: Example,
pixelArt: true,
audio: {
disableWebAudio: true
}
};
const game = new Phaser.Game(config);
Что попробовать дальше
Audio Sprites — мощный инструмент Phaser для оптимизации аудио в играх, позволяющий управлять множеством эффектов через один файл. Вы научились загружать аудиоспрайт, создавать на его основе интерактивный интерфейс и воспроизводить конкретные звуки. Для экспериментов попробуйте
- заменить HTML5 Audio на Web Audio API, убрав настройку
disableWebAudio, и сравните поведение - создать свой аудиоспрайт с помощью инструментов вроде Audiosprite или подобных онлайн-генераторов
- добавить к кнопкам слайдеры для управления громкостью или скоростью воспроизведения (
rate) конкретного звукового спрайта
