Интеграция текста в Unity

Интеграция текста в Unity

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

Шрифты

С чего начинается внутриигровой текст? Конечно, со шрифта.

Стандартным средством работы со шрифтами в Unity является компонент UI Text. Однако в 2018 году в Unity появился новый компонент: TextMesh Pro — и нет причин им не пользоваться.

В TextMesh Pro работа со шрифтами реализована через элементы Font Asset.

Если вы будете создавать отдельный Font Asset для каждого языка и копировать в него все символы соответствующего этому языку шрифта, то увеличите объем сборки и снизите быстродействие игры. Ваши игроки это точно не оценят, поэтому процесс требует оптимизации. Решение простое: объединить языки в Font Asset’ы по системе письменности (латиница, кириллица и т. п.) и в дальнейшем комбинировать эти группы.

Для начала рассмотрим простой случай: языки с алфавитной письменностью.

Создадим латинский Font Asset и включим в него все символы расширенной таблицы ASCII. Затем создадим один Font Asset с кириллицей. Подключим кириллический Font Asset к латинскому как Fallback (т. е. дополнительный набор, к которому игра обращается при отсутствии нужного символа в основном наборе). Можно подключить неограниченное количество элементов Fallback, поэтому все языки Европы умещаются всего в нескольких FontAsset’ах!

Алгоритм, по которому игра ищет нужный символ в наборах (например, при пользовательском вводе), выглядит следующим образом:

Языки с иероглифической письменностью требуют иной стратегии.

Вот пример для японского:

  1. Создать Font Asset и импортировать в него символы хираганы (46 уникальных знаков, составляющих основу японской письменности).
  2. Создать по одному Font Asset с символами катаканы (46 уникальных знаков, используемых в основном для записи слов неяпонского происхождения) и кандзи (2136 китайских иероглифов, используемых в японском языке).
  3. Добавить Font Asset’ы катаканы и кандзи к Font Asset хираганы в качестве Fallback.

Алгоритм поиска выглядит так:

Для упрощенного китайского:

  1. Создать Font Asset и импортировать в него все иероглифы, использованные во внутриигровых текстах.
  2. Создать Font Asset для иероглифов из первой категории списка Table of General Standard Chinese Characters (3500 минус те, что уже есть в Primary Font Asset).
  3. Создать Font Asset для иероглифов из второй категории (3000 минус имеющиеся в Primary Font Asset).
  4. Создать Font Asset для иероглифов из третьей категории (1605 минус имеющиеся в Primary Font Asset).
  5. Подключить Font Asset’ы из пп. 2–4 к Font Asset из п. 1.

Для наглядности изобразим отношения подмножеств иероглифов на диаграмме:

*Table of General Standard Chinese Characters

Параметры Sampling Point Size и Padding

При создании Font Asset в проекте (окно Font Asset Creator -> Font Settings) обратите внимание на параметр Sampling Point Size. Он определяет, насколько точно на экране будет отрисован каждый символ из шрифта. Не углубляясь в технические подробности, скажем лишь, что если в своем проекте для текстов вы используете атласы текстур с разрешением 2048 × 2048 (оптимально для мобильной игры), то лучшим выбором будет значение 72 пункта.

С параметром Sampling Point Size тесно связан параметр Padding. Он определяет ряд визуальных эффектов текста: интервал между символами в тексте, смещение тени или сияния символов, толщину контура символов и т. д. Рекомендуется выбирать значение Padding, лежащее в пределах 7–10 % от Sampling Point Size. Так, для Sampling Point Size = 72 лучше выбрать Padding = 6.

Подключая Font Asset в качестве Fallback к основному Font Asset, убедитесь, что соотношение Sampling Point Size / Padding в подключаемом Fallback’е равно или кратно этому же соотношению в основном FontAsset — иначе символы из разных наборов будут отрисовываться по-разному.

Например, в Primary Font Asset мы задали соотношение Sampling Point Size / Padding = 72/6. Тогда для элемента Fallback выберем соотношение Sampling Point Size / Padding = 48/4 (или даже 36/3).

Подробная информация о создании элементов Font Asset приведена здесь. 

Словари

Разобравшись со шрифтами, перейдем к смыслу ваших внутриигровых текстов.

Чтобы игроки в Германии имели шанс понять, что написано на экране, по меньшей мере нужно, чтобы оригинал и перевод соответствовали друг другу. Такие соответствия собираются в отдельные текстовые файлы, называемые в локализации «словарями». Информация в словарях организована в виде пар «ключ: значение». Ключом может быть ID строки или даже сам исходный текст, а значением должен быть перевод этой строки на требуемый язык.

В самом начале работы над проектом нужно ответить на фундаментальный вопрос — какой формат должен иметь словарь? Unity поддерживает около десятка форматов текстовых файлов, но наибольшее распространение у локализаторов получили пять: JSON, XML, YAML, CSV и PO. На последних двух остановимся подробнее.

Формат CSV

Простейший формат для словаря — CSV (Comma-Separated Values). В таком файле хранятся данные, разделенные запятой (или точкой с запятой). Универсальность этого формата позволяет работать с этими файлами в любом табличном процессоре, а также загружать их в специализированные средства автоматизированного перевода (memoQ, Trados и т. п.).

В первом столбце словаря следует расположить ключи, а в остальных —значения:

Если ваш проект не предполагает десятков тысяч слов и вы допускаете фанатскую помощь в локализации, то хорошим выбором для вас станет платформа Google Spreadsheets. Синхронизацию онлайн-словарей в Google Spreadsheets и текста в проекте можно автоматизировать с помощью бесплатного плагина Simple Localization.

Формат Portable Object

Файлы человеко-читаемого формата PO (Portable Object) предусмотрены стандартом gettext — библиотекой проекта GNU для интернационализации. Отличие этой библиотеки в том, что в ней в качестве идентификаторов строк используются сами исходные строки на английском языке, а не замысловатые конструкции. Это несколько облегчает работу, если внутриигровые тексты вы изначально пишете на английском. Кроме того, в библиотеке gettext есть поддержка множественного числа — пользу такой функции для локализации трудно переоценить.

Каждая запись в файле PO содержит связь между оригинальным фрагментом текста и соответствующим ему переводом. Как правило, один файл PO заводится на один язык перевода. Типовая запись в файле PO выглядит так:

 

<пробел>
# комментарий переводчика#. комментарий разработчика для переводчика

#: ссылка на исходный код программы

#, флаг

# | msgid предыдущая строка (на английском)

msgid исходная строка (на английском)

msgstr перевод строки

Подробное объяснение элементов файла PO можно найти здесь.

Важной частью локализации является файл формата POT (Portable Object Template) — заготовка файла PO для перевода на другой язык. Файлы POT используются для создания новых файлов PO в редакторе и для обновления файлов PO при добавлении новых переводов. Файлы POT имеют такую же структуру, что и файлы PO, только строка для переведенного текста пустая.

Переведенные строки не нужно добавлять в файлы PO напрямую — следует обновлять файлы PO из файлов POT. Для этого вам нужен специальный текстовый редактор. Самым популярным редактором файлов PO является Poedit.

Порядок действий выглядит примерно так:

  1. Создаем шаблон файла PO в формате POT и сам файл PO (храним их в папке Resources/Languages).
  2. К текстовому элементу игры, требующему перевода, подключаем скрипт UILocalizeText.cs, который будет контролировать его обновление.
  3. Для UILocalizeText.cs устанавливаем значение в файлах PO и POT.
  4. Инициализируем менеджер языков LanguageManager (в его коде используем метод DontDestroyOnLoad(), чтобы он не удалялся с загрузкой новой сцены).
  5. При смене языка делаем вызов LoadLanguage().
  6. Обновляем внутриигровые тексты вызовом UpdateAllTextBoxes().

Подробную информацию о локализации с помощью файлов PO и листинги скриптов можно найти здесь.

Теперь вы знаете несколько подводных камней и течений локализации в Unity — значит, она вам уже не так страшна.