Открыть список
Как стать автором
Обновить

HTML5 Audio и Game Development: баги браузеров, проблемы и их решения, идеи

JavaScript
В топике я расскажу о нюансах использования тега <audio> в разных браузерах при разработке игр, о проблемах, с которыми я столкнулся и о том, как их решить. Объяснение будет идти паралельно с написанием обертки для удобной работы.


Элемент Audio имеет очень красивый интерфейс, но нам его надо расширить, потому напишем обертку:
var LibCanvasAudio = function (file) {
this.audio = new Audio;
this.audio.src = file;
};

LibCanvasAudio.prototype = {};


.ogg vs .mp3


Для начала — муть с поддерживаемыми кодеками. Большинство браузеров поддерживает ogg vorbis(для Оперы в Линуксе не забудьте установить gstreamer base и good плагины), но, например, Apple решил выебнуться. Давайте для вменяемых браузеров будем отдавать в .ogg, всем остальным — в .mp3. Все звездочки в имени файла будем заменять на ogg или mp3 соответственно:

var LibCanvasAudio = function (file) {
this.audio = new Audio;
this.src(file);
};

LibCanvasAudio.prototype = {
src : function (file) {
var
codec = this.getSupport();
if (!
codec) throw 'AudioNotSupported';
this.audio.src = file.replace(/\*/g, this.getSupport());
this.audio.load();
return
this;
},
getSupport : function () {
return !
this.audio.canPlayType ? false :
this.audio.canPlayType('audio/ogg;') ? 'ogg' :
this.audio.canPlayType('audio/mpeg;') ? 'mp3' : false;
}
}



Схема Гатлинга


Хорошо. Мы подошли к главной теме. Допустим, мы разрабатываем экшн. У нас огромное количество взрывов, выстрелов, етс. Допустим, один взрыв с эхо длится 5 секунд, а интервал между взрывами может быть 0.5 секунд. Если просто запускать файл сначала, то предыдущий взрыв резко оборвется. Мы могли бы клонировать элемент Audio каждый раз перед запуском, но так мы будем плодить кучу DOM-элементов. Прибилизительно через 5 минут игры все браузеры сходят с ума. Потому предлагаю воспользоваться схемой Гатлинга. Заносим определенное количество элементов в массив и вызываем их по-очереди. Пока сыграет последний элемент первый успеет закончиться. Главное — выставить достаточно количество «стволов» для каждого из звуков.

LibCanvasAudio.prototype = {
// ...
cloneAudio : function () {
audioClone = this.audio.cloneNode(true);
audioClone.load();
return
audioClone;
},
gatling : function (count) {
this.barrels = [];
this.gatIndex = 0;
while (
count--) {
this.barrels.push(this.cloneAudio());
}
return
this;
},
getNext : function () {
var
elem = this.barrels[this.gatIndex];
++
this.gatIndex >= this.barrels.length && (this.gatIndex = 0);
return
elem;
},
playNext : function () {
var
elem = this.getNext();
elem.pause();
elem.currentTime = 0;
elem.play();
return
this;
}
};



Интерфейс у нас получается приблизительно такой:
var shotSound = new LibCanvasAudio('explosion.*').gatling(6);

window.addEventListener('keydown', function (e) {
(
e.keyCode == keys.SPACE) && shotSound.playNext();
},
false);


Облом в Опере


В Опере нас ждёт облом. Детально изучив этот вопрос нашел баг, который я зарепортил с кодом DSK-309302. Клонированный элемент Аудио в Опере не работает:

// var audioOrig = document.createElement('audio'); // аналогично с:
var audioOrig = new Audio();
audioOrig.src = 'shot.ogg';
audioOrig.controls = 'controls';

var audioClone = audioOrig.cloneNode(true);

function
appendToBody (node) {
document.getElementsByTagName('body')[0].appendChild(node);
}

audioOrig.play(); // работает
audioClone.play(); // не работает в Опере

appendToBody(audioOrig); // работает
appendToBody(audioClone); // не работает в Опере


Напишем небольшой фикс для Оперы:
LibCanvasAudio.prototype = {
// ..
cloneAudio : function () {
if (
window.opera) { // Reported Opera bug DSK-309302
var audioClone = new Audio;
audioClone.src = this.audio.src;
} else {
audioClone = this.audio.cloneNode(true);
}
audioClone.load();
return
audioClone;
},
// ..
};


Баг в Фоксе


Другой баг мы можем наблюдать в firefox 3.5 (в 3.6 и 4 уже нету) — при повторном воспроизведении аудиотрека первая секунда± — дублируется. Судя по всему, не только у меня: «the second time you hit the button it plays the “badumm” twice in Firefox». (видео с записью бага). Добавим небольшой фикс — будем отматывать аудио не в начало, а на 25 миллисекунду (минимальное значение установленно экспериментально и равно приблизительно 0.021-0.022 секунды). При желании можно добавить проверку версии и всех кроме 3.5 фокса возвращать в начало (но разница между 25 миллисекундой и нулевой не ощущается, в крайнем случае, зная про этот нюанс можно всё аудио отодвинуть в любимом аудиоредакторе на 25 миллисекунд влево):

LibCanvasAudio.prototype = {
// ..
playNext : function () {
var
elem = this.getNext();
elem.pause();
elem.currentTime = 0.025;
elem.play();
return
this;
}
};


Бонус


В ie9 preview 4 не работает new Audio(), но это очень просто решить, заменив его на document.createElement('audio');

Что получилось в итоге (тыкать в пробел)


Исходник, получившийся в результате: pastebin.com/xG4mhX3w
Теги:схема гатлингаhtml5javascriptlibcanvasaudiogame development
Хабы: JavaScript
Всего голосов 74: ↑64 и ↓10 +54
Просмотры10.4K

Комментарии 85

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Похожие публикации

JavaScript разработчик
от 180 000 ₽SportrecsМоскваМожно удаленно
JavaScript разработчик
от 75 000 до 180 000 ₽WebLab TechnologyМожно удаленно
JavaScript разработчик (React)
от 80 000 до 150 000 ₽Баланс-ПлатформаМоскваМожно удаленно
Javascript разработчик
от 2 800 до 3 300 $ArtezioМогилев
Javascript разработчик
от 160 000 до 220 000 ₽ArtezioМосква

Лучшие публикации за сутки