Unity3D高级编程:主程手记
上QQ阅读APP看书,第一时间看更新

3.3 多语言的实现

前面介绍了数据表的几种形式,以及如何让数据表运用更加高效。本节介绍多语言在数据表中的实现方式。

在游戏项目中,文字显示本身就是一件比较头疼的事,再加上多语言,更多问题需要解决。在项目初期,很多时候文本是写进代码里的,但到项目中后期,文字需要由策划设计人员来修改和添加,这导致了程序员要做大量重复的工作。所以文字数据从一开始就应该放在表里,让该考虑文字内容的人去考虑文字内容,我们要把这块工作分离出来,规划好每个人各自的工作。

为了实现这个独立模块,我们需要对数据配置表的导出做些规划设计。

我们来分析一下,一般文字会以Key-Value形式存放在Excel表里,比如Key为“RoleName”、Value为“角色名字”,或者Key为“1101”、Value为“好友分享”等。

这种Key-Value形式一般会以Int-String(整数对字符串)或string-string(字符串对字符串)的形式存在。

下面讨论这两种形式。我们从数据表里获取文字的方式,是喜欢以整数为键值的形式还是字符串形式呢?

使用整数形式获取就会像下面的样式存在:


string content = TextMgr.GetTextString(12);

这种形式看起来不是很美观,对于其他程序员,或者过了几周再回头来看,我们怎么知道12代表什么?只能猜这个12可能是某个字符串。随着代码的增多,文字量的增多,对应数字Key值也增多,我们更难识别这句话代表什么,调试起来会很麻烦。一个项目一般会有10万~30万行代码,如果到处都是这种形式的字符串获取方式,任何人看起来都会崩溃,因为维护性太差,校验检查难度太大,效率太低。

下面我们改用字符串作为Key值来获取文字内容:


string content = TextMgr.GetTextString("FightWin");

这种形式会好一些。至少知道了我们获取的大概是什么内容的字符串。不过仍然有问题,我们用一个字符串去获取另一个字符串,岂不是双份内存。原本只需要存储一个字符串就够了,现在要存储两个,而且用字符串查找字符串的效率也不高。当一串文字内容很多时,为了表达这串文字的大概内容,会把Key值设置得很长,比如用“BattleSceneFightAllianceWin”去取“联盟战胜利了”,这种形式的字符串Key值导致文字数据表变得很大,内存占用量也加大了很多,因为你要另外存储一份常量的字符串。

那么我们使用一个更好的方法吧。既要用简洁的数字去代表文字,又要让键值看起来形象,怎么办?采用与数据配置表列的对齐方式一样的办法,我们可以通过文字表格自动生成一个类,用变量的形式去记录文字的ID,通过文字表生成数据表,同时生成数据定义类,使用变量去代表数字。我们依然在表里填上字符串对应字符串这样的内容,比如上面提到的“BattleSceneFightAllianceWin”对应“联盟战胜利了”,在导出.xls数据文件时,生成一个类文件,专门把Key值按次序写进类中当作变量,代码如下:


Class TextKey{
    public const BattleSceneFightAllianceWin = 1;
    public const BattleSceneFightAllianceLose = 2;}

再把“联盟战胜利了”这种文本数据按次序依次写入数据文件,这样就可以一一对应了。也就是说,第一个变量对应第一个文字,第二个变量对应第二个文字。获取文本的方式改为如下代码:


string content = TextMgr.GetTextString(TextKey.BattleSceneFightAllianceWin);

这时文本数据的排列如下:


联盟战胜利了
联盟战失败了
…

程序变量被生成后,代码如下:


Class TextKey{
    BattleSceneFightAllianceWin = 1;
    BattleSceneFightAllianceLose = 2;}

文字与变量的数字依次对应,既解决了用数字做Key值不够形象的问题,又解决了字符串做Key值太多冗余的问题。

那么多语言部分怎么处理?简单的处理方式就是制作多个表,每个表一门语言。可以根据不同的语言来获取,如图3-4所示。

图3-4 每个表一门语言

代码如下:


string content = TextMgr.GetTextString(TextKey .YouWin,Language.Chinese);

如果只是一对一键值对,每次修改一处的语言内容就要对所有语言内容进行修改,调试起来比较麻烦。所以可以采用一对多的形式合并语言内容数据表,把一个表里的一个Key值对应多个语言的文字内容写在一个表里,如图3-5所示。

Key值如下:


键值 中文  英文  日文   韩文
Win   赢了  Win  勝った 이기다

图3-5 合并语言

将内容合并后,第二列为中文内容,第三列为英文,第四列为日文,第五列是韩文,所有内容都集中在一张表内,用的都是同一个Key值。程序调用语言文字内容接口没变,仍然是如下代码:


string content = TextMgr.GetTextString(TextKey .YouWin,Language.Chinese);

到这里,多语言部分就完美实现了,文字表在使用时兼具了形象、方便、快捷等特性。策划设计人员和运营人员关心的是文字内容,程序员关心的是Key值,它们被完美地拆分开来,各自分线工作。

转换数据时,也需要注意拆分数据表的问题,如果把所有数据表的数据都集中在一个数据文件里,那么游戏在加载数据表时,就需要在一瞬间集中处理,导致CPU阻塞时间过长,发生游戏卡顿现象,这样做并不合理,我们需要让游戏表现得尽可能顺畅,因此要尽可能将阻塞情况平摊在时间线上。

分散读取比较可取,读取表数据时要合理安排读取顺序,将I/O与CPU消耗的时间分散开来,不会一下子对I/O或CPU有大的需求量。很多时候,我们会采用按需读取的方式去读取数据,但多数情况是在某个瞬间需要大部分数据,此时按需读取已经不再有效,最好的办法是在加载时指定读取顺序,并隔帧读取。