Всем привет! Меня зовут Илья, я из команды TinyPlay. В этой статье хотел бы поделиться тем, как мы работаем с аудио. Надеюсь, для вас эта статья будет полезной.

Архитектура аудио

Проект состоит из 3-х составляющих: звуки (шаги, атмосфера, противники, оружие и многое многое другое), музыка (атмосферная, экшн-музыка, дополнительное сопровождение) и озвучка (персонажи, аудио-дневники, громокоговорители). Архитектура аудио разделена следующим образом:

Звуки:

  • Окружающая среда и все что с ней связано (листва, ветер, скрипы деревьев, ворот и пр.);

  • Звуки противников (атака, получение урона, обнаружение, смерть);

  • Звуки шагов для каждой из поверхностей (в общей сложности более 200 звуков под разные физические материалы);

  • Хоррор-звуки (вызываемые по триггерам);

  • Оружие (стрельба, перезарядка и пр.);

  • Звуки коллизий для физических объектов;

  • Звуки эффектов (взрывы, попадания, искры и пр.);

  • Другие звуки;

Музыка:

  • Атмосферная фоновая музыка;

  • Экшн музыка для определенных сцен;

  • Напряженная музыка;

  • Музыка в меню;

Озвучка:

  • Озвучка героев на разных языках;

  • Озвучка аудио-дневников;

  • Озвучка коммутаторов и другая озвучка;

Настройка компрессии для музыки и звуков различная. Музыка - стримится, большие звуки грузятся и распаковываются перед загрузкой сцены, мелкие звуки - налету.

Работа с музыкой и звуками. Микширование

Для достижения плавных переходов, различных звуковых областей и работе с компрессией - мы используем стандартный микшер Unity. Для плавных переходов - DOTween.

Пример того, как плавно переходит музыка в игре:

isSwitchingMusic = true;
MasterMixer.DOSetFloat("MainMusic", -80f, 2f);
MasterMixer.DOSetFloat("SecondMusic", 0f, 2f);

Микшеры также переключаются через особые Volume-зоны, которые меняют активный микшер или его настройки под окружающую среду (к примеру, чтобы в подвале был эффект эхо).

Пример работы с обнаружением материала террэйна:

namespace TinyPlay.Components
{
    using UnityEngine;

    public class TerrainDetector
    {
        private TerrainData terrainData;
        private int alphamapWidth;
        private int alphamapHeight;
        private float[,,] splatmapData;
        private int numTextures;

        public TerrainDetector()
        {
            terrainData = Terrain.activeTerrain.terrainData;
            alphamapWidth = terrainData.alphamapWidth;
            alphamapHeight = terrainData.alphamapHeight;

            splatmapData = terrainData.GetAlphamaps(0, 0, alphamapWidth, alphamapHeight);
            numTextures = splatmapData.Length / (alphamapWidth * alphamapHeight);
        }

        private Vector3 ConvertToSplatMapCoordinate(Vector3 worldPosition)
        {
            Vector3 splatPosition = new Vector3();
            Terrain ter = Terrain.activeTerrain;
            Vector3 terPosition = ter.transform.position;
            splatPosition.x = ((worldPosition.x - terPosition.x) / ter.terrainData.size.x) * ter.terrainData.alphamapWidth;
            splatPosition.z = ((worldPosition.z - terPosition.z) / ter.terrainData.size.z) * ter.terrainData.alphamapHeight;
            return splatPosition;
        }

        public int GetActiveTerrainTextureIdx(Vector3 position)
        {
            Vector3 terrainCord = ConvertToSplatMapCoordinate(position);
            int activeTerrainIndex = 0;
            float largestOpacity = 0f;

            for (int i = 0; i < numTextures; i++)
            {
                if (largestOpacity < splatmapData[(int)terrainCord.z, (int)terrainCord.x, i])
                {
                    activeTerrainIndex = i;
                    largestOpacity = splatmapData[(int)terrainCord.z, (int)terrainCord.x, i];
                }
            }

            return activeTerrainIndex;
        }

    }
}

У нас используется плеер на основе эвентов анимаций, но вы сделать по-другому. Выше описан класс для определения материала террайна. Используя его - можно сопоставить поверхность со звуками.

Объемный звук. Рейтрейсинг и обнаружение геометрии

Мы перебрали несколько вариантов работы с объемным и главное физически-обоснованым звуком. Мы попробовали Steam Audio, FMOD, Microsoft Acoustics и др.

В итоге наш выбор пал на FMOD + Dolby Atmos + Microsoft Acousitcs. Почему не Steam Audio? Ну, мы получали непредвиденные вылеты в билдах на Windows со стороны их библиотеки, которые не могли отладить.

Для чего мы все это используем?

  1. Для автоматического просчета распространения звука в окружающей среде, с учетом её геометрии, материалов и отражаемой способности (каждый объект в игре настраивается отдельно);

  2. Для пространственной обработки аудио при помощи HRTF. В связке с Dolby Atmos звук получается максимально естественным.

Как это работает?

Система учитывает пространственное положение звука, источник его приема, угол и десятки других параметров для корректного восприятия, как в наушниках, так и на 5.1 системах.

Помимо этого, система при помощи рейтрейсинга (трассировки лучей в реальном времени) пробрасывает лучи для определения коллизий и отражений, применимых к звуковым волнам. Для снижения нагрузки эти расчеты производятся на GPU + звук запекается (аналогично Light Probes, но для звука).

Облачные вычисления для запекания звука

Запекать звук на сцене с тысячами 3D моделей и миллионами полигонов достаточно долго, поэтому мы используем облачные вычисления от Azure. Благо у Microsoft есть отличный инструмент на виртуальных машинах, предназначенный для этого - Azure Batch

Используя сервис облачных вычислений удается снизить время запекания звука с 2 часов до 10 минут, что существенно экономит время при достаточно низкой цене (1,5 бакса в час). При этом есть бесплатная подписка на Azure на 12,500Р, что подходит начинающим разработчикам.

Итого

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

Полезные ссылки:

https://valvesoftware.github.io/steam-audio/

https://docs.microsoft.com/ru-ru/gaming/acoustics/

https://www.fmod.com/unity

https://developer.dolby.com/partners/middleware/fabric/

Комментарии (0)