Использование системы локализации Audiokinetic Wwise в игре на Unreal Engine 4

Илья Никулин

Unreal Engine and wwise

Основой для статьи послужила запись в блоге разработчиков игры Fated. Увы, приведенная в ней инструкция оказалась недостаточной, поэтому я решил ее дополнить и переработать.

Wwise известен как самое продвинутое решение для интерактивного звука в игровой индустрии. Движок считается чуть ли не стандартом для AAA игр — он применялся в Doom, Elite: Dangerous, Overwatch, The Witcher 3 и многих других крупных проектах. Поэтому для нас было неожиданностью обнаружить недоработки в интеграции Wwise c Unreal Engine 4, которые затрудняют процесс локализации голосовых ассетов.

В официальной документации Audiokinetic об интеграции с UE4 нет никакой информации об использовании локализации. В Q&A пишут, что локализация вообще пока что не поддерживается, но это не совсем так. Эта функция заложена в плагине, но чтобы ее использовать, нужно редактировать код интеграции.

Дисклеймеры:

  • Несмотря на то, что я старался сделать материал доступным для читателей с минимальным опытом программирования, я настоятельно не рекомендую саунд-дизайнерам без должной квалификации работать с кодом игры. Передайте эту задачу программистам;
  • Здесь мы не будем рассматривать весь процесс локализации, а сконцентрируемся на той его части, которая требует технической доработки со стороны плагина интеграции Wwise;
  • Описанный подход актуален для всех версий интеграции 2016 и 2017 года.

О языках и банках

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

Можно заметить, что после введения второго языка плагин Wwise создает новые папки внутри корневого каталога с банками (*YourProject*/Content/WwiseAudio/*PlatformName*). Тем не менее, при загрузке банков в память плагин ищет их в корневой папке — это легко проверить, если скопировать банки из “языковой” папки в корневую.

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

Теперь нам нужно научить Wwise выяснять, какой язык используется в игре, чтобы загружать нужные банки. В Unreal Engine локализация текста производится с помощью системы Localization Dashboard. В коде проекта нам нужно будет обратиться к этой системе, чтобы забрать текущее значение языка, или в местной терминологии — культуры (culture).

Важно! Прежде чем приступить к редактированию кода, имеет смысл переименовать языки в вашем проекте Wwise. Дело в том, что Unreal Engine отдает значение текущего языка в виде аббревиатуры, например русский — “ru”, английский — “en”, японский — “jp”. Мы будем передавать эти значения напрямую в плагин Wwise, поэтому назвав соответствующим образом языки в проекте, вы избежите хардкода. Это значит, что если вам в будущем понадобится добавить еще один язык к вашему проекту, достаточно будет правильно назвать его внутри Wwise, и он сразу же будет корректно определяться в игре.

Работа с кодом интеграции

Откроем проект игры в Visual Studio и найдем класс AkGameplayStatics.cpp. В основном мы будем работать в нем. Чтобы узнать текущее значение culture, нам нужно добавить пару включений в файл:

#include "Internationalization.h"
#include "Culture.h"

Далее создадим функцию SetLanguageToCurrentCulture(), которая будет запрашивать текущую “культуру” и назначать соответствующий ей язык для загрузки банков.
Прежде чем определить функцию, нам нужно объявить ее в файле AkGameplayStatics.h, который находится в соседней папке Classes:

UFUNCTION(BlueprintCallable, Category = "Localization")
static void SetLanguageToCurrentCulture();

Теперь возвращаемся в AkGameplayStatics.cpp и описываем функцию:

void UAkGameplayStatics::SetLanguageToCurrentCulture()
{
FString currentCulture = FInternationalization::Get().GetCurrentCulture()->GetName();
const TCHAR* result = *currentCulture;
UE_LOG(LogScript, Warning, TEXT("%s"), result);
if (AK::StreamMgr::SetCurrentLanguage(result) == AK_Success) {
UE_LOG(LogScript, Warning, TEXT("Set current Wwise language - success!"));
}
else {
UE_LOG(LogScript, Error, TEXT("Could not set Wwise Language."));
}
}

Функция запрашивает текущее значение culture и отправляет его в Wwise. Дальше интеграция пробует найти и установить заданный ей язык. Если язык успешно установлен, в консоль выводится соответствующее сообщение. Если язык с таким названием не найден, то выводится ошибка.

Наша функция готова, и теперь ее нужно вызвать. В общем случае, мы хотим вызывать ее при загрузке игры. Это значит — после загрузки основного ядра Wwise, но до(!) загрузки саундбанков. Подходящее место — класс AkAudioModule.cpp, функция FAkAudioModule::StartupModule(). Чтобы получить доступ к функциям AkGameplayStatics из AkAudioModule, необходимо сделать включение:

#include "AkGameplayStatics.h"

Обратите внимание, что вызов функции должен обязательно происходить внутри функции StartupModule() после инициализации устройства аудио:

AkAudioDevice = new FAkAudioDevice;
if (!AkAudioDevice)
return;
if (!AkAudioDevice->Init())
{
delete AkAudioDevice;
AkAudioDevice = NULL;
return;
}
UAkGameplayStatics::SetLanguageToCurrentCulture();

На этом все — теперь языковые банки будут загружаться при запуске игры в соответствии с установленным параметром culture. При необходимости можно модифицировать код для динамической загрузки банков при смене языка во время игры. Но не забудьте перед этим выгрузить текущие банки из памяти и загрузить их после смены языка.

Илья Никулин — саунд-дизайнер и музыкант, работает над игровыми VR проектами в компании VRTech. Занимался созданием музыки, саунд-дизайном и архитектурой звуковой системы квеста в реальности The Complex.

Автор выражает благодарность Владимиру n3td0g Ширшову за помощь в подготовке материала.