Звуки в игре на движке Phaser 3

 Игра состоит из многих составляющих, и одной из важных является звуковое сопровождение, например, фоновая музыка, озвучка диалогов, звуковые эффекты. В этой статье мы рассмотрим добавление звуковых эффектов в прототип игры evacuation-simulator.

Проблема звуковых эффектов сводится к следующим вопросам: что должно звучать? Где можно взять звуки? Когда это должно звучать? Как добиться от игрового движка проигрывания звука? В каком формате хранить звуки? 

Начнём с самого начала: что должно звучать? В прототипе есть бегающие человечки, которые должны спастись от огня, пройдя путь по стрелочкам, расставленными игроком. Основными событиями, которые могут быть интересны игроку, являются: распространение огня, сгорание человечка, эвакуация человечка.

Где можно взять необходимые аудиозаписи? Есть множество источников с бесплатными звуками, но многие звуки можно получить, записав свой голос на микрофон и его обработав. С помощью программы Audacity, были записаны несколько образцов звуков "Уиии" для события эвакуации человечка, несколько образцов звуков "Ай", "А", "Оу" для события сгорания человечка, а также был записан и обработан звук "Пшшшш" для события распространения огня.

В Phaser для работы со звуком существуют классы Audio и AudioSprite. Audio необходимо использовать, когда в аудиофайле у нас записан только один звук, в нашем случае это звук распространения огня. AudioSprite же позволяет использовать разные звуки, содержащиеся в одном аудиофайле, в этой статье так были сгруппированы разные вариации звуков для эвакуировавшихся человечков и сгоревших человечков. Помимо аудиофайла, необходимо также описание схемы спрайта. Мы рассмотрим схему в формате json для файла exit.ogg, содержащего звуки успешной эвакуации "Уииии".

{
    "resources": [
        "assets/sounds/exit.ogg"
    ],
    "spritemap": {
        "0": {
            "start": 0,
            "end": 0.84,
            "loop": false
        },
        "1": {
            "start": 1.068,
            "end": 1.765,
            "loop": false
        },
        "2": {
            "start": 1.892,
            "end": 2.728,
            "loop": false
        },
        "3": {
            "start": 2.914,
            "end": 3.518,
            "loop": false
        }
    }
}


Для каждого спрайта указывается отдельный именованный объект. Поскольку в нашем случае все они являются вариацией одного и того же звука, в качестве ключа используется номер. В объекте же указываются время начала и конца спрайта, а также является ли он зацикленным — в простом случае этих параметров достаточно. Аналогичный файл был создан для звуков сгорания человечка.

После того как мы подготовили все файлы, необходимо из загрузить в игру. Добавляем в метод preload подходящей сцены следующие строки


  const soundPath = name => `assets/sounds/${name}`
  this.load.audio('fire-new', ['new_fire.ogg', 'new_fire.mp3'].map(soundPath))
  this.load.audioSprite('exit', soundPath('exit.json'), ['exit.ogg'].map(soundPath))
  this.load.audioSprite('die', soundPath('die.json'), ['die.ogg'].map(soundPath))

Необычным может показаться тот факт, что мы передаём для одного аудио массив путей к файлам. Это необходимо из-за того, что разные браузеры и технологии могут поддерживать разные форматы аудиофайлов, поэтому рекомендуется иметь аудиозапись, сохранённую в разных форматах (например, ogg и mp3). Для примера такой подход используется только для звука появления огня. Для спрайтов необходимо также указать путь в схеме, которую мы описали ранее.

После загрузки мы можем получить объекты-обёртки над аудиозаписями в сцене через менеджер звуков, находящийся в поле sound с помощью методов get(key: string). Также через него можно регулировать общую громкость всех звуков сразу. Если мы хотим только воспроизвести звук, для этого мы можем воспользоваться методами play(key: string, config: object) и playAudioSprite(spriteKey: string, keyInSprite: string, config: object). Следующие строчки были добавлены в метод создания огня, сгорания человечка и эвакуации человечка. Для вариативности звучания распространения огня добавим случайный уровень звука.

this.sound.play('fire-new', { volume: Phaser.Math.RND.realInRange(0.1, 0.2) })
this.sound.playAudioSprite('die', Phaser.Math.RND.between(0, 7).toString(), { volume: 0.5 })
this.sound.playAudioSprite('exit', Phaser.Math.RND.between(0, 3).toString(), { volume: 0.4 })

Ознакомиться с результатом можно здесь, а посмотреть код — в репозитории.

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