Pull to refresh

Локализация игр в Unreal Engine 4

Reading time7 min
Views15K
Подготовка игры к локализации — важная часть разработки игр.

Мы работаем над игрой "Cat Movies!" в движке Unreal Engine 4. Это экономическая стратегия, в которой достаточно много текста, и его мы планируем переводить на различные языки. Как и многие другие (но это не точно, и, надеюсь, что это не так), мы решили отложить этап настройки локализации на более поздние итерации разработки и, как оказалось, зря.

Локализация в UE4 реализована шикарно, и если помнить, что достаточно весь текст, который будет переводиться, хранить в Ftext (Text в Blueprint'ах) полях, то в целом, с выхватом текста из игры нет никаких проблем. Достаточно открыть Localization Dashboard, потыкать пару кнопок — и вуаля.

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

Дополнено от 16.10.19: Форматирование текста.
Дополнено от 13.09.20: Локализация ассетов.

Как хранить текст


Ребята из Epic Games упростили сборку текста до максимума, сводя все к одному клику и дальнейшему переводу.

Работает это все очень просто — для локализации используется тип данных FText (Text в Blueprint'ах), в который сохраняется текст, и этот текст в дальнейшем собирается системой локализации и предоставляется для перевода.

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

FText — это не стандартный тип данных, который хранит в себе данные. Он, конечно, хранит в себе данные, но не те, которые мы ожидаем.

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

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

image

Когда запускается сбор текста, то система локализации собирает все Text — переменные из всех блюпринтов, виджетов и таблиц, и выкатывает один огромный список текста, который можно экспортировать в *.po и переводить.

Наша игра имеет большой набор виджетов, через которые игрок будет контролировать почти весь игровой процесс. У нас есть много названий (например, отделов или этапов создания фильмов или каких-то навыков, бонусов и т.д.), которые используются в различных виджетах, между собой совсем не связанных. Есть описания каких-то объектов (например, описание бонусов), и это тоже используется не однократно в различных не связанных виджетах.

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

Мы облегчили себе задачу, создав Data Table (таблицы данных), в которых мы начали хранить данные конкретных типов. Например, таблица отделов, которая содержит в себе название, описание, максимальный уровень, кол-во сотрудников на каждый уровень, и т.д. и т.п.
Казалось бы, можно было на этом остановиться, однако возникала сложность в том, что данные в таблице постоянно менялись, что-то переписывалось, что удалялось по мере роста кода, и бывали случаи, когда слетала полностью вся таблица, и нам приходилось откатываться, выдергивать данные, возвращаться в текущее состояние и вставлять потерянные данные обратно.

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

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

Перевод — дело человеческое. Если будет возможность ошибиться, человеки обязательно ошибутся. И чтобы этого избежать или хотя бы минимизировать, нужно сводить все тексты к единому шаблону. То есть, вывести куда-то в одно место, откуда текст будет браться во всех частях кода. И который будет переведен лишь единожды, чтобы свести риски ошибок до минимума. Казалось бы, можно использовать DataTable, но и там кроется ряд проблем — чтобы сохранить просто названия чего-то, нам нужно создавать целые таблицы, которые будут хранить эти названия.

Поковырявшись в документации Epic Games, я обратил внимание на String Tables (далее «строковые таблицы»). Оказалось, это идеальный вариант — хранить текст в отдельной специальной для текстов таблице, которая создана именно для того, чтобы подключаться в FText — переменным. То есть, мы можем создать таблицу, которая будет хранить в себе текст. И этот текст мы можем подключать к любой переменной — будь то переменная в таблице данных, переменная в виджете, переменная в коде — все сводится к одному месту, где хранится текст.

Строковые Таблицы позволяют избегать повторения текста в проекте по нескольку раз чуть более, чем полностью, таким образом избегая ошибок.

Строковые таблицы создаются в движке, как и таблицы данных в разделе «miscellaneous»:

image

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

После того, как строковая таблица создана, текст теперь можно подключать к FText — переменной:

image

image

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

Локализация


Теперь нам остается только собрать весь текст и начать с ним работать. Для этого нам необходимо запустить Localization Dashboard и начать настройки локализации. Запустить таблицу можно через меню Window->Localization Dashboard.

И перед вами откроется примерно такое окно:

image

В этом окне нужно обязательно указать цель (модуль), из которого будет выдергиваться текст. В нашем случае, это игра — Game.

Далее, нужно указать, откуда именно будет в модуле будет выдергиваться текст. В нашем случае, это только блюпринты, так как в сурсах мы не храним текст. Поэтому нужно указать только «Gather from Packages» и указать, в каких папках и какие файлы нам необходимо рассматривать, чтобы искать тексты.

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

Так же в UE4 есть шикарная возможность создавать переводы прямо в движке, не экспортируя тексты для сторонних программ. Для этого нужно собрать весь актуальный текст в игре, нажав на кнопку №1. И после этого запустить редактор (кнопка №2):

image

Откроется окно редактирования перевода на нужный вам язык.

Сборка


При сборке проекта обязательно нужно указать языки, которые в эту сборку должны войти, а так же какой набор языков должен поддерживать ваш проект.

image

В нашем случае, Internationalization Support установлен на All. То есть, наш проект будет поддерживать все типы языков от сложных иероглифов до простых английских букв. А вообще, есть 5 пакетов:

  • English (чистый английский).
  • EFIGS (English, French, Italian, German, and Spanish)
  • EFIGSCJK (То же, что и выше + Chinese, Japanese, and Korean )
  • CJK (Chinese, Japanese, and Korean).
  • All (Все языки).

Однако в случаях, когда важен каждый байт в проекте (пакет All весит 15мбайт, а EFIGS 2мбайта), стоит более внимательно отнестись к тому, какой пакет нужно выбрать.

Переключение локализации


Переключение текста происходит в Runtime, то есть вам не нужно перезагружать игру, не нужно париться на тему оптимизации переключения — все делается легко и просто через метод "SetCurrentCulture", где текстом нужно указать, на какой язык вы хотите переключиться.

image

И вот здесь есть некоторая такая загвоздочка. Дело в том, что разных странах есть свои ответвления языка, так, например, есть основной русский язык (ru), а есть белорусский (ru-BY), казахстанский (ru-KZ), молдавский (ru-MD), украинский (ru-UA) языки, которые попадают под ветку русского языка. Поэтому, при выборе какого-то языка, который не является основным, это нужно учитывать и указывать корректный язык. Если выбрать просто основной, то при переключении достаточно указать «ru».

image

Добавление в текст данных других данных в Blueprint'ах.


Если в самом тексте указать фигурные скобки, а в них указать название данных, то в дальнейшем, можно использовать подстановку этих самых данных в текст.
Например: «Вы желаете изучить технологию {Tech_Name}?»
И, в дальнейшем, в BP можно использовать ноду «Format Text», которая будет учитывать сам текст и какие там указаны параметры и создавать дополнительные пины для подключения этих самых данных:
image

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

Локализация ассетов.


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

Это же касается и текстур, эффектов, моделей, аудио, видео — любых ассетов, которые вы используете в работе над своим проектом.

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

Для этого нажмите по любому ассету в проекте ПКМ -> Asset Localization -> Create Localized Asset -> (выбрать язык).

image

После создания новой копии ассет можно найти в специализированной папке для локализации. Если лень искать — можно кликнуть ПКМ по основному объекту в проекте и через меню локализации выбрать редактирование нужной вам версии.

image

Там же можно и перейти к ассету в браузере. Все очень удобно и практично.

Заключение


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

То же самое касается и Сохранения и Загрузки в играх. Это оказалось не такой уж и простой задачей в условиях стратегии, где каждая мелочь должна фиксироваться там, где она стоит/лежит/имеет определенное состояние. Но об этом я напишу как-нибудь в другой раз. А сейчас вы можете продолжить наблюдать за нашей игрой в нашей группе VK и в Twitter'е =)
Tags:
Hubs:
Total votes 5: ↑4 and ↓1+3
Comments7

Articles