本文机核站地址:https://www.gcores.com/articles/124964

声明: 本插件已经上架Unity Asset Store(http://u3d.as/1USu) 。

这是一篇日志类的文章,给各位一个思路的参考,也希望各位可以从我这儿了解点知识。

今年4月中,我正式着手开始自己的第一个半商业半试验的项目。这个项目,是一款非常注重文本对话,多分支选项,多结局,在环境中调查探索,以及潜入偷听偷窥的游戏。在文章的末尾我会详细谈谈我的游戏的。

黑色洛城就是一款非常注重演出、对话、探索调查的游戏

确立需求

在对这个项目进行技术难点和可行性论证的时候,我认识到了几个重大难点之一,就在于我游戏的文本。这款游戏是一个非常注重剧情和角色塑造的游戏。因为团队的美术能力不强,所以大部分美术都是我这样一位程序员亲自上阵。最终,我们妥协地采用了像素风格。而又因为我们游戏中演出和对话的情况出现的相当频繁,所以,如何在像素画风之下展现出优秀的演出就成了一大问题。本身动画就很难做,更别说像素画的逐帧动画了。

因此我立刻认为应当在另一个方面寻找出路。反观市面上的很多注重剧情的像素类游戏,不难发现,在文字的显示方式和特效上下功夫,是非常便捷且有效的做法。我脑海中迅速想起了一款三年前玩过的游戏——《高尔夫物语》。

当时《高尔夫物语》这款游戏给我留下的最深刻的印象,除了对NS上HD震动的运用,就是他的文本显示了。这也是我第一次见到一款游戏在文字显示方面居然可以如此的多变和多样,利用这个简简单单的粗像素画面展现出了引人入胜的演出。

而且《高尔夫物语》当中人物几乎没有专门做用于过场的动画,只有几个表情符号的显现和人物的移动。这种解决方式相当的符合我的需求。

寻找解决方案

于是我便开始先寻找市面上是否有合适的成型的解决方案,其中Fungus是一个看起来不错的选择。Fungus的好处就是两点,免费商用且方便。Fungus通过节点图的方式实现直观的台词编写,同时Fungus也已经带好了不少特效功能。

下方黄色和橙色的块就是一个个节点,它们共同组成了节点图

然而Fungus的重要问题就是,他与我自己编写的代码交互起来比较困难,而且我想给他添加其他种类的特效也非常不方便。所以我觉得必须要自己写一个才行。因为只有自己写的才能任意的添加各种特效,让整个系统按照自己设想的方式运行。自己要清楚的知道整个系统是怎么运行的,这对于将来的Debug和维护是非常有帮助的。

况且Fungus是一套完整的有限状态机编辑工具,他的功能完备到我可以不写任何代码,只编辑一下节点图就可以做完一个游戏,而我并不需要这么大量且复杂的功能。这样一套复杂的系统在自己的项目中,总是觉得像一块定时炸弹,搞不好就和我的什么其他的代码冲突了。而其他类似的台词系统,要么就是不符合需求,要么就是太贵了,对于我这个需要节省资金的开发人员来说消费不起。

好的插件贵贵贵,便宜的插件不好用

寻找未果,自己动手

当我正式开始着实自己制作一款类似的工具的时候,最大的难点出现了,就是这个节点图的实现。其实我最开始是放弃使用节点图的,因为要在unity中实现类似的功能,实在是太难了。所以一开始我的台词编写器长这样:

我通过列表的形式实现每一句对话,并在中间放了个小人显示这句话的效果,编辑完成之后需要将文件保存为Json格式的文件,然后在Unity中读取。别看这个工具虽然卖相差,但是除了写起台词来还是有点繁琐以外,基本的功能已经具备了。

而对应这样子的特效,在写台词的时候不需要编写任何的代码就可以实现。只需要这样写:

格式:

虽然已经实现了主要功能,但是实际使用起来,编写台词的时候还是很难受,特别是当涉及到选项的时候。选项的编写:

原本的选项功能编写起来非常的复杂,当我想要让选项跳转到某一句话,我需要输入这一句话的编号。而每当我调整对话的顺序,我就需要重新指定编号。虽然后来觉得可以通过唯一码识别每一句话,但是既然如此,为何不做节点图呢?

还是想做节点图

说做就做,一开始我尝试了最复杂的做法,也就是利用UnityEditor内的代码从零开始手写一个节点图。虽然当时已经实现了基础功能,但是节点图的保存和读取成为了一个大问题。后来我了解到Unity开放了一个实验性功能,GraphView,这个功能其实就是Unity最近更新的Shader Graph的一部分。Shader Graph的节点图编辑功能就是用GraphView写成的。

节点图的雏形,每个节点除了包含台词和选项就做不了更多的事情了

GraphView的局限性在于其节点功能的封闭性,对于GraphView来说,其节点功能的可定制性不佳。到这个时候,我已经花了一个多星期的时间在研究各种节点图上了,正当我一筹莫展准备放弃的时候,突然一个叫xNode的插件映入了眼帘。

xNode免费商用的特性,其小巧的体量,基于UnityEditor的源码,并且高度的可自定义化都符合了我对于节点图的需求。在经过快速的学习之后,只用了不到两天时间就完成了我的对话系统的雏形了。

可以看到,相比于前面的台词编写器,这时我的节点编辑器已经像是可以正常使用的样子了。并且,其对于本地化的支持也非常方便,你可以自定义你需要的语言,你游戏中的角色,以及创建一个替换字符列表,这个替换字符列表我暂时还没有做本地化相关的支持,稍后会添加进去。你可以为玩家说话的声音进行定制,也可以使用统一的音效。

来看一下效果吧:

节点图现在已经可以和游戏的实际运行贴合的非常好了。

而且最重要的,我的对话编辑器可以非常简单高效的与其他代码进行交互。

这是一个事件节点,假设我们想要触发一个叫做“PlayText.Test”的事件,然后传递两个变量,一个整数的“0”,一个字符串变量“哈哈哈哈测试测试”。那么只需要一行代码你就可以在自己的代码中获取到这个事件的触发和变量。

我建立了一个事件中心,你只需要在Awake函数以外的任何一个地方添加一个事件监听,并把一个函数委托给事件中心就可以了。在你委托的函数中,你就可以随意的编写自己想要触发的东西。

小小的文字特效却十分的复杂

经历了这么多,我觉得我的对话系统已经非常完善了,现在就差实现几个小小的特效的大功告成了,然而最后这几个小小的特效,着实难到了我。

我想实现的是类似这样的可以让每一个字单独动起来的效果

要实现这样的文字特效,必须要将每一个字都分开来计算。因此我只能放弃Unity的UGUI的Text,转而使用在此之前我几乎没用了解过的TextMesh Pro。TextMesh Pro就是一个将每一个字都用一个单独的TextMesh渲染出来的插件,其功能比Unity的UGUI更加完善多样,但是也更加的复杂。

而且最要命的是,TextMesh Pro的示例里虽然有我想要的效果,但是每当字体有任何的更新,所有的抖动效果都会被重置,这样的话如果我的字是一个一个出现的话,在字出现的过程中,其他的字会不断的被重置到初始位置。如果字出现的速度足够快,其他的字抖动的效果将会非常不明显。

上图就可以看出来,当文字出现的速度足够的快,其他的字连动都不动一下了。直到全部的字都出现,他才开始抖动。

最终这个过程花了我非常多的时间去学习TextMesh Pro的运行原理和使用方法,终于在一天半之后实现了这样的功能:

终于字的出现不会影响前面的文字了。

然后将字体特效和对话编辑器联系起来,就可以实现如下效果了:

而台词只需要这么写: “<sh=5,50>你这个人真的</sh><wa=4>奇怪</wa>”

这个效果看起来非常的夸张,但是不用担心,在写代词的时候可以随意的指定其动量和大小。

至此,大部分的难点都已经攻克,只剩下Debug和完善界面了。

目前本插件已经在Unity Asset Store上出售,感兴趣的可以去体验一下。

链接:http://u3d.as/1USu

补充:关于TextMeshPro的字体问题

这里再提一下,TextMesh Pro需要你生成专门的字体文件。也就是说,如果你想加入自定义的汉字字体,那么你需要提供一个包含汉字的字体文件,然后用TextMesh Pro为每一个汉字单独的生成一张贴图(其实不是一个字一张,而是一张贴图上很多个字)。

最后我做出来的支持显示八国语言的字体文件有144MB的大小。相较于原本的11.7MB的字体文件来说实在是太大了,但是这也是没有办法的办法。这里为大家推荐两种字体的生成方式,导入TextMesh Pro后,通过Window→TextMeshPro→FontAssetCreator打开制作字体的工具。

首先确保你的字体的要在TextMeshPro的Font文件夹下,不然有可能会报错,并且确保你的字体文件名称只有英文和数字,不要有符号和其他文字,可能会报错。

然后重点在于「CharacterSet」这一栏,TextMeshPro并不是你的字体里有多少字他就渲染多少字,而是需要你来指定。指定的方式有很多,推荐「Characters from File」和「Unicode Range(Hex)」网上不少推荐使用Character from File搭配常用汉字列表的,我这里也分享一个:常用的7000个汉字。下载下来解压后是一个txt文件,导入unity然后在「Character from File」里选择这个txt文件就行,而且如果一次性渲染的文字量大,可以将「Atlas Resolution」设置为4096*4096或8192*8192。

虽然这个常用7000个汉字的确可以满足大部分需求,但是如果我们要做更多的语言,该怎么办?这里我就推荐一下我的做法,利用Unicode Range,以下是八国语言的Range:

00-24F,400-52F,3041-30FF,3104-312A,3400-4DB5,4E00-9FA5,AC00-D7A3,F900-FA2C,FF66-FF9E,20,22-23,25-40,5B-5D,7B-7E,2014,2018-2019,201C-201D,2022,2026,2160-2169,2170-2179,2460-2469,3000-3002,3005,300A-300B,3010-3011,3280-3289,FF01,FF08-FF09,FF0C,FF1A-FF1B,FF1F,FFE5

将上面这串代码放进去就行了,其他设置不用动,直接点Generate Font Atlas开始渲染。

渲染完后保存。如果你发现有一些字体文件丢失的话,解决起来也非常简单,新建一个TXT,将缺失的文字放进TXT里,然后选择「Character from File」渲染另外一个字体文件。这个时候注意,如果渲染的量小了,「Atlas Resolution」也要相应的调小。

然后在Project中点击第一个字体文件,在其Inspector面板找到「Fallback Font Asset」,将第二个字体文件拖入,就可以将两个字体文件合并起来了。

不过因为我提供的Range的涵盖面已经很广了,所以基本不用担心会出现无法显示的字。

还有就是利用Fallback Font会导致在我的插件之下字体显示出错,这是因为Textmesh Pro官方的问题,他们对于使用了Fallback Font的字体不支持单个字的动画调整,一旦调整就会出现各种各样匪夷所思意想不到的情况,所以如果要是使用的插件还请各位不要使用Fallback功能。

不使用Fallback的话,遇到无法显示的字符,可以点此前往Unicode编码转换工具

输入你缺少的字符,点击中文转Unicode就行了,这里你输入的不是中文也没问题。得到一个”\u30e9″这样格式的东西后,我们不要前面的”\u”,把你后面的内容输入到「Unicode Range(Hex)」里,并用英文的逗号让其和前面的内容分隔开。

Categories:

Tags:

Comments are closed

作品类型