Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

时间:2024-05-22 18:40:50

1字体的基础知识

本地化的常用手段是文本翻译,而文本翻译除了上一篇文章中提到的翻译质量之外,还需要从技术手段上支持多语言在游戏中的显示。

在电子产品上展示语言或者文本,最常用的方式就是提供一个字体文件。一般来说,操作系统都会自带很多字体文件用于在该操作系统下进行常规的字体显示。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

(windows 7 系统目录下的字体)

比如你现在看到的这篇文章的字体就来源于系统。但这样会有一个坏处,因为调用的是系统字体,那么在不同的平台,因为系统字体库的差异就会导致同一个产品在不同平台阅读的体验不一样。这对于正常的小说或者文章来说无伤大雅,但是对于游戏这样一个艺术商品来说,每一个UI都是经过精心设计和配色的,调用系统平台的字体一定会破坏原有的设计美感。

那么解决该问题的方式就是将自己复制到项目里,让引擎支持从自己的工程里读取字体,这样就可以排除平台差异,让设计效果在所有的平台都能显示一致。事实上,各个游戏引擎也都是这么做的。

但这又会导致另外一个问题,字体源文件的大小会极大影响项目性能,比如我们出了名的碰瓷字体:微软雅黑。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

正常体+黑体就需要34M的大小。这不仅会影响包体大小,也会影响运行时的内存大小。而且这种固定的字体在引擎下还无法拆分,因为一个字体库一般来说会包含这个语言的所有文字,但大部分时候我们的游戏或者常用语里根本用不到这么多的字和符号。无法拆分就意味着无法对这种源文件的字体进行裁剪和优化。

另外一个问题则源自于Unity引擎对字体的处理。现有的字体从实现方式上大概分为两种,一种叫点阵字,一种叫矢量字。最早的计算机并没有这么强大的算力,屏幕的分辨率相对变化也不大,所以最大使用的是点阵的格式。顾名思义,就是字体是由固定阵列的像素点拼出来的,可以把这种字体的字理解为一张小图片,每一个字都是小图片。图片的劣势我们都知道,就是只要在放大或者缩小的时候就会产生马赛克或者锯齿。但在电子屏幕阅读,出于各种复杂的阅读环境或者设计需求,字体不可能只保持一个字号,而放大缩小会失真就会导致阅读的体验感非常差。

为了解决这个问题,早在1980年苹果和微软就共同制定了一种新的字体格式,矢量字体。它保存的不再是字体的图片,而是字体的笔画走势。比如一横的水平角度,弯钩的弧度等数据。这样的好处就是可以在支持的任何平台都可以相同的公式进行还原,不管是放大还是缩小只要遵循公式计算,得出的结果都会是比较清晰的。

这种字体的好处迅速得到各大平台的支持,发展至今基本已经成为字体的主要方式。但是矢量字并不仅仅只是Truetype一种,还有其他的类型,但在这次内容中不重要,就不做介绍了,我会把相关链接放在文章底部,大家有兴趣可以阅读。

2 Unity对字体的处理

前面我们提到了矢量字体固然有它的好处,但是无法拆分和便捷的裁剪是它在移动端不太好用的一个原因。而另外一个问题,则是Unity对字体的格式处理。从官方的手册里我们可以得知:Unity并没有像操作系统一样在使用的时候用公式去还原字体,而是使用它的资产导入管线,将导入的字体都转化为纹理贴图。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

当你导入一个静态字体的时候,Unity会需要你对字体进行一些必要的设置。如下图:

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

这其中最重要的,决定字体质量的就是Font Size。Unity会基于你设置的字号大小来生成指定的纹理贴图。换句话说,它反过来把矢量字体又转化为了点阵字了。个人不负责任的猜测是因为:第一,运行时动态计算会消耗非常大的性能,第二转化后的纹理可以减小字体源文件的大小。

但不管怎么说,一旦转化成点阵字之后,那么点阵字的缺点也就暴露出来了,比如你基于16字号生成的字体,在36号的时候就会非常的模糊,同时在6号的时候则产生较大的锯齿。而这些对于游戏来说都是非常影响感官和品质的。

比如下面的两个方案对比,上面的是使用的TMP的方案,我们稍后介绍,下面的则是普通的UGUI的实现,可以看到在字体放大的时候,模糊度会越大越大。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

(动图1 UGUI 字体放大变模糊)

下面是静态的对比图。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

Unity对于字体的处理方式或者说默认的TTF类型字体又会引申出另外一个问题。不管是图片还是算法格式得到的结果都是固定的,也就是说源字体设计是什么样,还原回来还是什么样。这对于常规的UI上的文字使用是足够的,但是对于一些特殊场合,需要夸张设计的字体效果而言就无能力为。

这个时候,我们就需要使用到自定义字体,也就是我们常说的美术字。它需要美术人员单独的出一张图片,然后将图片贴在指定位置当做文字使用,比如下图中的胜利二字。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

除了以上的不便之外,我们还需要关注一下UGUI在字体实现方案上的性能问题。如下图所示,我简单调了一下静态描边效果,上面的是TMP的实现,下面的是UGUI的实现,可以看到TMP的网格Mesh一点都没有增加,但UGUI的Mesh已经增加了几倍之多。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

3 多语言带来的难度

以上的问题在只有一种语言的时候,并不会特别突出。除了多消耗一些性能之外,也不需要做额外的处理,唯一麻烦一点的地方就是在于如何管理美术字,当成自定义字体处理还是放在纹理图集中当成纹理都可以。

但是一旦涉及到多语言的时候,就特别难以管理。因为你需要成建制的替换不同语言所涉及的所有元素,比如刚才说的字体,比如游戏中需要翻译的内容,美术字,语音甚至是CG等等。这在过去的游戏中有一些常用的做法。

最简单的方式就是《皇室战争》的做法(AFK很久了,如果有记错的地方还请指正),对战只能发表情!文字可能全球理解有差别,但是表情包是没有国界的!

然后是《炉石传说》,在对战的时候可以发指定的文字表情,对局外,只能通过加好友之后私聊。这里可以节省2个烦恼。第一,文字可预期,也就是说所有的表情文字都是通过预置在包体内的文字进行的,第二,暴雪不负责私聊的时候彼此能不能看懂对方说话。

前面两种都是没有聊天大厅的,对于策略类的,各自为战的玩法游戏而言,是可以满足的。但是对于MMO或者MOBA这种强社交类型的游戏而言,*聊天是必不可少的功能。这就要求游戏功能必须开放一个世界/全服聊天的频道,所有玩家在里面能在里面*聊天。

这里的难点在于玩家聊天内容是不可预期的,玩家所使用的语言也是不可预期的。他完全可以使用国产的手机,在美国打日文。这就要求,你的聊天频道是需要支持日文显示的。既然是多语言、全球同服,那么韩语呢?越南语呢?泰语呢?中文简体?中文繁体呢?

对于西文倒还好,全部字母加起来也就那么几个字符,但是对于中文、日文、韩文这样的每一个语言都需要一个庞大的源字体,要全部支持的话,光字体文件可能就需要几十上百M。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

(《炉石传说》国服版本的字体打完bundle后是25M)

但遗憾的是,没有哪一个字体的源文件可以包含所有的语言种类,它们大部分时候分散在非常多的字体源文件中,这又带来了识别困难的问题,程序如何得知哪句话是用的哪种语言从而去选择指定的字体来显示呢?

但据我所了解的解决方案而言,大致有几种解决方案,如果有不足的也希望大家可以留言补充。

一是无差别的版本,也就是所有国际版统一使用英文版本。这么做的好处在于开发简单便捷,基本不需要过多考虑本地化的情况。

二是如果不是全球同服的地区,直接只做指定区域的版本翻译,也只支持单种语言的聊天,单从包的功能看,就和国内版或者国际通用版本没区别。但是在开发阶段还是需要对项目进行规划,确保在打指定语言的包的时候,资源能够正确的覆盖。

第三就是较为复杂的区域版本,比如我们常说的新马菲,或者东南亚。我们假定是一个MOBA类型的项目,在东南亚*、越南、泰国等地区联合举办赛事,平常这些地区也是在同一个服务器进行对战。那么我们就需要在游戏中能够动态切换语言,因为你不知道当前玩游戏的人用的是什么语言。

如果二的情况是静态打包时候覆盖资源就可以的话,那么三的情况就是必须把这套架构扩展为动态的,所有需要的资源进包,然后经过统一的语言切换选项进行管控。

然后第四,也就是我们现在需要的,全球同服(除国服外)。也就是这个系列所需要介绍的方案了。所以,循序渐进,我们一点一点的来剥离实现。

4 Text Mesh Pro的优点

最开始,我们想要解决的是字体同源的问题。也就是说我们希望在同一个聊天面板里显示不同的国家的语言。而TMP的构造原理是天然符合我们对于字体的处理。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

那么对于以前没有接触过TMP的开发者来说,可以简单的介绍一下它。Text Mesh Pro (统一简称TMP)是一个Unity的插件,他重新处理了Unity的字体,让开发者可以自己指定字符集进行二次生成。它像Unity一样也需要基于源文件去生成,也是会生成纹理和图集,但是不同之处在于,它保留了矢量的那套算法(算法名字叫 Signed-distance-field 简称SDF),也就是帮助Unity重新用回矢量字。

这里我不会展开去讲解TMP的原理和基本知识,有兴趣的可以看我之前针对TMP的一些基础分享。

1、Text Objects : https://zhuanlan.zhihu.com/p/90759281

2、Font Assets : https://zhuanlan.zhihu.com/p/90937163

3、Rich Text:https://zhuanlan.zhihu.com/p/91035197

4、Shaders:https://zhuanlan.zhihu.com/p/91689792

5、Sprites : https://zhuanlan.zhihu.com/p/93171755

6、Color Gradients : https://zhuanlan.zhihu.com/p/93172017

7、Settings : https://zhuanlan.zhihu.com/p/93172400

静态的效果大家都可以从上面的文章中了解,这里我再分享一下YouTube上,大神基于TMP制作的一些效果展示,由于视频比较长,因此我仅仅录制一些片段供大家参考。不过要提前说明的是,以下所展示的所有效果都是来自于TMP文字效果。

通过调整纹理参数来调整文字纹理感。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

文字和光照相结合

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

调整文字的凹凸质感

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

文字添加纹理背景

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

添加波浪动效

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

霜冻效果

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

快速调整描边效果

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

从上面的效果可以看到,TMP的Shader非常强大强大到你可以调配出游戏可以使用的绝大多数效果!同时它对于图文混排十分友好,并拥有强大的RichText功能。最最最重要的是,这些几乎都是免费的,也就是说 几乎不耗费额外性能 就能够达成的。所有的效果你只需要把参数调整好,保存为材质球,运行时动态替换材质球就可以快速切换效果。

所以TMP一共解决了我们多少的问题呢?总结一下:

1、字体同源,并且内容可控。

2、放大缩小无损。

3、同屏显示多国语言

5、减少包体

6、减小内存

7、减少美术字、图片字的管理成本

8、用Shader的方式增加字体动效

9、聊天表情、图文混排

10、超链接

这些功能几乎已经能涵盖游戏开发中关于文字功能的大部分需求了。

5 Text Mesh Pro的缺点

既然TMP这么强大,有这么多好处,理所当然它也是有不足之处。目前为止,遇到两个最大头的问题,第一个是DrawCall,第二是堆内存。

前面说到,TMP的所有效果都可以在编辑时态调好参数,然后存储为一个材质球。这就意味着每一种效果在运行时就会增加一个DC。比如16号字和18号字哪怕只是改了字号也会多一个出来。和大多数的3D渲染一样,TMP运行时动态赋值(修改参数)的话,也是会使用material而不是sharedMaterial。这造成的结果就是在UI展示的时候,因为多种UI的字体效果,又或者是和UI的图元渲染进行互相的批次打断,从而造成DrawCall增加。

但这种情况对于一般的UI来说性能影响不是很大,但是对于战斗中大量的飘字和世界沙盘中大量的HUD而言是一个性能重灾区。我们解决方式方式也很简单,分为两个步骤,对于UI上的文字效果,进行提前的收集,并和美术UI的同学确定好预设,最终统计10-20种字体效果,形成缓存池,通过增加组件的方式,让所有相同效果的文字共用一个sharedMaterial来实现UI上批次的减少。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

而在战斗飘字和世界地图方面,为了防止TMP之间互相打断批次,会针对不同的功能用途人为的指定渲染层级,强制彼此之间分离渲染顺序,以避免彼此打断。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

这里其实又引出了另外一个问题,由于在PhotoShop中字体还原的算法和TMP的SDF字体算法有差异,导致美术UI的效果不能完全按照字号或者描边的参数进行还原。(比如描边,PS用的是像素,TMP用的是比例)所以我们需要负责UI的客户端同学和UI同学坐在一起,把效果调整到基本一致的时候,程序保存参数值。之后使用的时候,美术只需要标注预设,而程序这边从下拉菜单中选取预设即可。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

第二个就是堆内存问题。TMP在字体变化的时候,需要对Mesh和三角形等做重运算,也就会有不同大小的数组产生。TMP的做法是直接使用Array.Resize的方式扩充数组,而这个函数导致了大量的堆内存产生。鉴于TMP的框架原因,我们选择使用缓存改写了这一局部的逻辑。目前堆内存的问题已经得到控制,但这一方案仍然有更进一步优化的空间。

由于改动涉及TMP组件本身,所以大家可以根据自己团队的实际情况进行修改。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

除了以上的不便之处之外,暂时还未发现其他关于TMP的较严重的问题。

另外,TMP插件更新比较慢,并且在不同的Unity版本使用了不同的特性,所以版本更新都是分为3个(目前)版本来更新的。在2018上,使用的是1.x的版本(我们做技术选型的时候是1.4,所以一直沿用的是1.4版本),在更高的版本根据使用的版本特性分别还有2.x和3.x版本。不过开发者无需自己考虑应该使用哪个版本,打开PackageManager直接下载的时候,就会自动帮你适配好。

Unity手游实战:从0开始SLG——本地化篇(二)TMP的优势与缺点

由于我们没有使用非1.X版本的TMP,所以在非1.X版本的TMP使用是否会有其他问题,尚不得而知,如果大家在其他版本遇到问题的话,可以留言给大家一起讨论,有好的踩坑经验也欢迎留言分享。那么本章的内容先介绍到这里,下面会给一些关于TMP的学习资料,大家有空可以更加深入的去了解。

扩展阅读

1、计算机中字体的原理是什么?

https://www.zhihu.com/question/22301687

2、TrueType 字体

https://zh.wikipedia.org/zh-hans/TrueType

3、truetype技术和矢量字库的技术原理及实现

https://blog.****.net/xjbclz/article/details/51771523

4、TrueType字体简介

https://wenku.baidu.com/view/21ac0b0e87c24028915fc35b.html#

5、Unity 字体实现

https://docs.unity3d.com/Manual/class-Font.html

6、TextMeshPro 中文字符集

https://www.cnblogs.com/koshio0219/p/11643268.html

绍TMP的视频:

Introduction to Text Mesh Pro for Unity - Advanced Text Rendering https://youtu.be/xfo0NrLJe_k

TMP Release 1.4.0 Upgrade Process https://youtu.be/iAfEdyAO8oo

TextMesh Pro - Font Fallback & Dynamic SDF System overview

https://www.youtube.com/watch?v=slvRsAYS7zw&t=325s

TMP视频作者主页:

https://www.youtube.com/channel/UCfZ4egVzhZrnilkOu1Y7w6g

Fallback Setup for Multiple Languages

https://www.youtube.com/watch?v=pLW2B98W5AU

GetStart:

https://www.youtube.com/watch?v=olnxlo-Wri4&feature=youtu.be

Font Asset Creation

https://www.youtube.com/watch?v=qzJNIGCFFtY&feature=youtu.be

Working with Material Presets

https://www.youtube.com/watch?v=d2MARbDNeaA&feature=youtu.be

forum

http://digitalnativestudios.com/forum/index.php?board=4.0