Как уменьшить размер шрифтов в Unity до нуля

от автора

Введение

Всем привет! Меня зовут Александр и я Unity Developer более 7 лет. В этой статье мы попробуем решить проблему шрифтов раз и навсегда (в мобильных играх так точно). Способ для Unity не самый очевидный, про него не так много написано и все ответы приходилось собирать по кусочкам, собственно поэтому и решил написать статью. Сразу перейдем к техническому заданию.

Нужно сделать локализацию для мобильной игры, с такими требованиями:

  • Шрифты не должны занимать много места в билде, желательно до 10 мб максимум

  • Шрифты должны быть сгенерированы без лишних заморочек для Text Mesh Pro

  • Шрифты должны поддерживать такие языки: English, Russian, Ukrainian, German, French, Spanish, German, Italian, Portuguese, Arabic, Japanese, Chinese, Korean, Hindi

Проблема

Итак, давайте разбираться в самой задаче по порядку. Первый пункт про размер шрифта: если подбирать шрифт для европейских языков, то такой шрифт будет весить обычно не больше 1 мб. Но чем ближе мы будем двигаться в сторону Азии, тем больше будет размер шрифта (интересное сходство🤔). В итоге получается примерно 24.6 мегабайт, что не мало для мобильной игры. Стоит учесть, что некоторые шрифты взяты в единственном стиле, иначе размер увеличивается практически вдвое.

Language

Font

Size

English, Russian, Ukrainian, German, French, Spanish, German, Italian, Portuguese

Noto Sans

0.6 MB

Arabic

Noto Sans Arabic

0.7 MB

Hindi

Noto Sans Devanagari

0.9 MB

Japanese

Noto Sans Japanese Black

5.7 MB

Korean

Noto Sans Korean Black

6.2 MB

Chinese

Noto Sans Chinese Simplified

10.5 MB

Итого:

24.6 MB

Второй пункт так же не простой из-за не знания всех языков, при генерации TMP шрифта можно упустить какие-то символы. Например для китайского языка есть 3500 часто используемых иероглифов из 20000 только официального словаря. С третьим чуть полегче, нам помогут дизайнеры найти нужные шрифты, и наше дело их добавить в игру.

Решение проблемы

После долгого поиска шрифтов и осознания, что они будут занимать 1/3 размера игры, появилась идея: “В операционных системах же есть уже встроенные шрифты для разных языков, почему бы их не использовать”. Потратив некоторое время на поиски, я наткнулся на форум, где уже этот вопрос обсуждался с разработчиком TMP. В треде добились чтобы разработчик сделал такой функционал. Хоть функционал есть, но шрифты, о которых пишет Apple и Google, находятся не все или названы совсем не так. Тут можно найти списки шрифтов для мобильных платформ

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

private static IEnumerable<(NativeFontData data, string path)> GetNativeFonts(IEnumerable<NativeFontData> data) {   var nativeFonts = Font.GetPathsToOSFonts();    foreach (var fontData in data)   {     var currentFontName = fontData.Name;     var nativeFont = nativeFonts.FirstOrDefault(nf => IsTargetFont(currentFontName, nf));      if (nativeFont is not null)     {       yield return (fontData, nativeFont);     }     else     {       Debug.Log($"Can't find any font with name: {fontData.Name}");     }   }    yield break;    bool IsTargetFont(string target, string current)   {     return target == Path.GetFileNameWithoutExtension(current);   } }

Теперь нам нужно эти шрифты конвертировать в TMP и настроить. При настройке шрифта нужен индивидуальный подход для каждого, так как Японский шрифт с теми же настройками как у Итальянского может хуже читаться или занимать больше места в памяти.

private static IEnumerable<TMP_FontAsset> ConvertNativeFontsToTmp(IEnumerable<(NativeFontData data, string fontPath)> fontPaths) {   foreach (var (data, fontPath) in fontPaths)   {     var font = new Font(fontPath);     var tmpFontAsset = TMP_FontAsset.CreateFontAsset(font, data.PointSize, data.Padding,                     GlyphRenderMode.SDFAA_HINTED,                     data.AtlasSize.x, data.AtlasSize.y);      yield return tmpFontAsset;   } }

Чуть подробнее опишу некоторые конфигурации у шрифта:

  • Point Size — качество рендеринга символа, выше лучше (хороший показатель 40-50)

  • Padding — расстояние между символами в атласе

  • Atlas Size — размер каждого создаваемого атлас

  • Dynamic Font — каждый символ, перед отображением, будет пытаться найти такой же в шрифте и после добавит в атлас

  • Multi Atlas Textures — если заканчивается место для символов создастся новый атлас, иначе не будет добавлен символ

Полный код можно найти здесь. Как видно из кода, в функции есть аргумент текущего языка платформы, в моем случае мы используем только 1 язык без возможности изменения его во время игры. В случае, когда понадобятся все поддерживаемые языки (например для чатов), необходимо немного усовершенствовать функцию. Здесь стоит быть осторожным: память на устройстве не резиновая, и стоит контролировать количество и размеры атласов шрифтов (опять же отсылка к Китайскому языку). Так же я добавил пример настроек для разных платформ, которыми сам пользуюсь.

var fontData = Application.platform switch { RuntimePlatform.Android => new[] { //Android new NativeFontData("NotoNaskhArabic-Regular"), //Arabic new NativeFontData("NotoSansDevanagari-Regular"), //Hindi new NativeFontData("NotoSansCJK-Regular", 40, 3, new Vector2Int(2048, 2048)), //Chinese, Japanese, Korean  new NativeFontData("Roboto-Black"), //Unicode new NativeFontData("Arial"), new NativeFontData("LiberationSans") }, RuntimePlatform.IPhonePlayer => new[] { //iOS new NativeFontData("HiraginoMincho"), //Japanese new NativeFontData("NotoNastaliq"), //Arabic new NativeFontData("DevanagariSangamMN"), //Hindi new NativeFontData("PingFang", 40, 3, new Vector2Int(2048, 2048)), //Chinese new NativeFontData("AppleSDGothicNeo"), //Korean new NativeFontData("SFUI"), //Currency symbols new NativeFontData("Arial"), new NativeFontData("LiberationSans") },   _ => new[] {new NativeFontData("Arial"), new NativeFontData("LiberationSans")} };

Заключение

В финальном результате получилось оптимизировать билд, процессы поиска и добавления шрифтов. Вот небольшой список достижений:

  • В проекте только один шрифт

  • Уменьшился размер всех шрифтов до 500 кб

  • Теперь нет необходимости конвертации шрифта в TMP

  • Поддержка практически любых языков

P.S. Еще раз продублирую ссылку на репозиторий. Если понадобится сделать из этого пакет — напишите об этом в комментах.


ссылка на оригинал статьи https://habr.com/ru/articles/828620/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *