~2 мин чтения

Аудио

Интеграция Web Audio в Phaser 4 — музыка против звуковых эффектов, разблокировка по жесту пользователя, микширование и пространственный звук.

Phaser 4 оборачивает браузерный Web Audio API за this.sound. Аудио загружается через стандартный загрузчик и воспроизводится через типизированный менеджер звука.

Загрузка

preload() {
	// Несколько форматов — браузер выбирает первый, который может декодировать.
	this.load.audio('music', ['assets/audio/theme.ogg', 'assets/audio/theme.mp3']);
	this.load.audio('hit', 'assets/audio/hit.ogg');
}

Всегда поставляйте как минимум два формата. OGG покрывает Firefox и Chrome; для Safari нужен MP3 или M4A.

Воспроизведение

this.sound.play('hit');                                  // звуковой эффект по принципу «выстрелил и забыл»
this.sound.play('hit', { volume: 0.6, detune: -200 });   // настройка для каждого вызова

// Для всего, на что нужна ссылка:
const music = this.sound.add('music', { loop: true, volume: 0.5 });
music.play();
music.setVolume(0.2);
music.stop();

this.sound.play(key, config) возвращает звук, но не сохраняет ссылку на него — используйте add + play, когда нужно управлять звуком после запуска (пауза, затухание, изменение громкости).

Разблокировка по жесту пользователя

Браузеры не запускают аудио до тех пор, пока страница не получит жест пользователя (клик, нажатие клавиши, касание). Phaser обнаруживает это и разблокирует аудиоконтекст при первом вводе — но любой звук, который вы попытаетесь воспроизвести до этого момента, будет беззвучным.

Защитные паттерны:

// Дождаться разблокировки перед воспроизведением музыки.
if (this.sound.locked) {
	this.sound.once(Phaser.Sound.Events.UNLOCKED, () => music.play());
} else {
	music.play();
}

Для большинства игр это не проблема, потому что первое взаимодействие (клик в меню, «нажмите любую клавишу») естественным образом предшествует любому аудио. Это становится подвохом, когда вы пытаетесь воспроизвести музыку в автоматически запускающейся сцене-заставке.

Музыка: зацикливание и затухание

const music = this.sound.add('music', { loop: true, volume: 0 });
music.play();
this.tweens.add({ targets: music, volume: 0.6, duration: 1500 });

// Перекрёстное затухание на другой трек.
this.tweens.add({ targets: music, volume: 0, duration: 1000, onComplete: () => {
	music.stop();
	const next = this.sound.add('battle', { loop: true, volume: 0 });
	next.play();
	this.tweens.add({ targets: next, volume: 0.6, duration: 1000 });
}});

Для бесшовных циклов исходный файл должен быть точным до сэмпла в точке зацикливания. Vorbis (.ogg) наиболее терпим к этому; MP3 исторически добавляет беззвучные отступы.

Микширование

У менеджера звука есть мастер-громкость:

this.sound.volume = 0.8;       // 0..1, умножается на громкость каждого звука
this.sound.mute = true;        // переключатель, не абсолютное значение
this.sound.pauseAll();
this.sound.resumeAll();

Для отдельных шин музыки и звуковых эффектов сгруппируйте звуки в собственном помощнике и применяйте множители категорий — встроенной поддержки шин Phaser не предоставляет.

Пространственный звук

Для 2D-позиционного аудио используйте поддержку панорамирования бэкенда Web Audio:

const sfx = this.sound.add('engine', { loop: true });
sfx.play();
// Панорамирование от -1 (полностью влево) до 1 (полностью вправо) в зависимости от расстояния до центра камеры.
this.events.on('update', () => {
	const dx = (vehicle.x - this.cameras.main.midPoint.x) / (this.cameras.main.width / 2);
	sfx.setPan(Phaser.Math.Clamp(dx, -1, 1));
	sfx.setVolume(1 / (1 + Math.abs(dx) * 1.5));
});

Для более насыщенного 3D-подобного поведения (HRTF, модели расстояния) спуститесь к чистому Web Audio API — Phaser предоставляет лежащий в основе AudioContext через this.sound.context.

Распространённые ошибки

Забывание удалять звуки при переходах между сценами. Звуки продолжают воспроизводиться между сценами, если вы их не остановите. Останавливайте музыку сцены в shutdown() или привязывайте её к жизненному циклу сцены:

this.events.once('shutdown', () => music.stop());

Конфигурация для отдельного вызова не сохраняется. this.sound.play('hit', { volume: 0.3 }) воспроизводит именно этот звук с громкостью 30% — кэшированная конфигурация для ключа остаётся без изменений.

Стоимость декодирования. Большие аудиофайлы декодируются при первом воспроизведении, что может вызвать заикание. Выполните предварительное декодирование, воспроизведя звук в беззвучном режиме при старте игры, или загружайте файлы меньшего размера.

Связанные материалы