ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FGUI字体描边实战:从编辑器配置到性能优化全攻略

FGUI字体描边实战:从编辑器配置到性能优化全攻略 做项目的时候最烦的就是UI文字在复杂背景下看不清。尤其是排行榜、角色名、战斗飘字这类位置底图一花白字瞬间糊成一片。我最早在FGUIFairyGUI里处理字体描边也是从被美术追着改稿开始的。后来踩了一圈坑才摸着门道FGUI里的字体描边看着只是个属性开关但背后涉及动态字体、位图字体、贴图采样、顶点开销一堆东西用不对轻则效果差重则直接把UI性能拖垮。这篇就把我在FGUI里做字体描边的完整思路和实操记录写下来从编辑器配置、代码控制到性能优化和排坑都过一遍给同样被描边折磨的朋友做个参考。1. 先搞清楚FGUI的字体描边到底做了什么1.1 从需求说起为什么文本非要加描边在很多游戏UI里文本不是放在纯色底上的。头像旁边挂个名字下面压着的是半透明背景图战斗里的飘字背后是不断变化的场景。这种情况下文本和背景的对比度完全不受控字色再深也可能被背景吃掉。描边是最粗暴也最有效的解决方式。用一圈和字色相反的轮廓把文字边缘包起来无论是亮背景还是暗背景文字都能保持可读。FGUI里的字体描边指的就是这个给GTextField、GRichTextField这些文本组件在文字边缘渲染一圈指定颜色和宽度的轮廓线。很多刚接触FGUI的人会以为描边只是文字看起来更好看的装饰功能其实它对可读性的提升才是核心价值。排行榜里区分玩家名字和普通NPC名字Buff倒计时需要一眼看清数字这些都依赖描边把文字从背景里剥离出来。没有它UI设计师就得给每个文本单独配一版带底图的文字美术资源工作量直接翻倍。1.2 FGUI里描边的几种实现路径先做选型我在FGUI里折腾描边总结下来有三条路可以走。第一条路是编辑器直接配置。在FGUI编辑器的属性面板里选中文本组件设置描边颜色和描边大小发布后就能直接看到效果。这条路最简单适合静态文本、策划配置表里写死的文本或者UI原型阶段快速验证。第二条路是代码动态控制。通过GTextField的stroke属性在运行时设置描边参数。适合那些需要根据游戏逻辑动态变化的状态比如玩家名字里称号变色时同步调整描边或者飘字系统统一给所有浮字套描边。第三条路是用位图字体预渲染描边。制作BMFont字体时直接把描边效果画进字体贴图FGUI里当作普通字体使用运行时完全不需要额外计算描边。这条路性能最好但做字体麻烦适合高频使用、需要极致性能的文本比如伤害数字。这三条路不是互斥的实际项目里经常混着用。我在做飘字系统的时候核心伤害数字用的位图字体预渲染描边普通系统提示用的代码动态描边编辑器直接配的只留给固定界面里的标题文本。选哪条路取决于这个文本出现的频率、是否动态变化、以及对性能的要求。2. 编辑器里的描边从0配置一个可用效果2.1 文本组件属性面板描边设置步骤FGUI里给文本加描边编辑器操作其实很快。我拿一个排行榜的玩家名字为例演示一下完整流程。先在FGUI编辑器里创建一个文本组件。如果是从零开始在资源库里右键新建一个组件双击后从组件列表里拖一个文本进去。选中这个文本对象右侧属性面板会出现文本相关配置。在属性面板里找到描边选项FGUI里描边默认是不勾选的。点击展开或者直接勾选会看到两个关键参数描边颜色和描边大小。描边颜色建议直接用色块右侧的取色器选也可以用十六进制色号精确控制。描边大小我一般先给2然后调整到视觉舒服为止。这里有个细节我试了好多次才总结出来描边大小不是越大越好。大小给到3以上描边就会开始侵蚀文字内部空间尤其是中文字比较少笔画的字笔画之间本来就挤描边一大笔画和描边糊在一起文字反而变难辨认。实际项目里常规UI文字描边1到2就够用飘字、标题这种大字可以给到3甚至4。注意FGUI编辑器里的描边设置会直接存在组件数据里发布后运行时读取不需要代码参与。但如果之后发现文本描边在编辑器里看着正常、发布到手机上却变粗或者消失多半是字体和贴图分辨率的问题这点在后面排查章节单独说。2.2 把描边配置做成可复用的文本资源如果一个界面里十几个文本都要用同款描边一个个去属性面板里设置就太蠢了。FGUI里有两个思路可以做复用。第一个思路是复制粘贴属性。在FGUI编辑器里选中已经配好描边的文本组件右键选择复制格式再选中其他文本组件右键粘贴格式字体、字号、颜色、描边这些配置就能一次性带过去。这个操作快适合界面内批量统一。第二个思路是把文本封装进一个公共组件。比如我做一个带描边的名字标签组件里面放GTextField并配置好描边然后在各个界面里复用这个组件。好处是以后要调描边参数只需要改这一个组件发布后所有用到它的界面全部生效不需要一个个去同步。第二个思路在项目里价值更大。我们的VIP玩家名字策划要求在不同界面里颜色不一样但描边必须是统一的深色加粗效果。如果每个界面复制粘贴策划调整一次描边方案我就要把所有界面重新过一遍。做成公共组件之后只需要改组件里的描边设置重新发布所有界面自动更新省了太多事儿。2.3 位图字体的描边把描边画进贴图里如果你对字符外观有完全的控制需求比如描边必须带渐变、带阴影、带内外两层轮廓编辑器自带的描边就捉襟见肘了。这时候需要用位图字体Bitmap Font方案。做位图字体的流程我简单说一下。用BMFont这类工具选好字库设置好字体大小然后打开描边选项设置描边宽度和颜色生成贴图和fnt文件。注意BMFont里生成的描边效果会直接绘制在字符位图边缘画面最终效果完全取决于生成时的参数。然后把生成的贴图和fnt导入FGUIFGUI会自动识别为位图字体。在文本组件里把字体选成这个位图字体同时确保属性面板里的描边开关是关闭的否则会和字体里本来就有的描边叠加导致双重描边丑得很。位图字体描边最大的优势是运行时零计算。描边早就固定在图里了不占任何额外的顶点开销和Shader计算性能非常好。缺点也很明显不能做动态文案字库有多大就支持多少个字超出字库的字会显示成方块。所以我的习惯是伤害飘字、Buff图标数字这种字符集固定、高频复用的文本用位图字体其余所有情况优先考虑常规字体加描边。3. 代码控制描边动态交互场景下的正确姿势3.1 GTextField的核心描边接口与参数说明编辑器配置只能解决固定不变的文本。遇到运行时动态创建的文本或者文本内容会随着游戏状态频繁变化的场景就必须靠代码来控制描边了。在FGUI里GTextField是文本组件的核心类GRichTextField继承自GTextField所以这两个组件都能用同一套描边接口。核心就两个属性stroke和strokeColor。stroke是描边宽度类型是int默认值是00代表没有描边。strokeColor是描边颜色类型是Color。注意FGUI里stroke的单位不是像素FGUI内部会把stroke值按照当前UI缩放比和字体大小做换算所以不同分辨率下视觉上描边粗细保持一致。从代码设置描边的方式非常简单但这里我踩过坑必须确保文本内容已经赋值、并且文本已经显示之后再设置stroke否则某些引擎的底层文本组件会重建mesh把已经设置好的描边效果冲掉。我一般在textField.text xxx之后立刻设置描边如果是在列表item里复用文本要小心列表滚动导致的刷新需要在item的显示回调里重新设置一遍。3.2 不同引擎下的FGUI描边代码示例FGUI本身是跨引擎的不同游戏引擎接入FGUI后描边代码的写法差别不大但调用细节有差异。我在Unity和Cocos Creator里都写过分别说一下。Unity环境下GTextField的描边配置代码如下using FairyGUI; // 获取FGUI文本组件 GTextField nameLabel this.GetChild(nameLabel).asTextField; // 设置文本内容 nameLabel.text 玩家代号; // 设置描边宽度和颜色 nameLabel.stroke 2; nameLabel.strokeColor new Color(0f, 0f, 0f, 1f);如果想要描边效果自适应不同分辨率的屏幕可以结合GRoot的缩放比调整stroke值float scaleFactor GRoot.inst.scaleX; nameLabel.stroke Mathf.RoundToInt(2 * scaleFactor);Cocos Creator环境下FGUI的组件操作方式大同小异TypeScript写法如下import fgui fgui; const nameLabel this.getChild(nameLabel).asTextField; nameLabel.text 玩家代号; nameLabel.stroke 2; nameLabel.strokeColor new fgui.Color(0, 0, 0, 1);代码控制描边最大的意义不只是设置参数而是可以根据游戏状态动态调整。比如我的项目里玩家死亡后名字颜色变灰同时要求描边也变淡。我直接写了个方法输入颜色和描边透明度返回给文本赋值一套逻辑通吃所有需要名字变色的地方。3.3 富文本里的描边GBRichTextField的用法限制GRichTextField是FGUI的富文本组件支持UBB标签像颜色、加粗、斜体、超链接这些都是标准能力。但有一个限制经常坑到人FGUI的UBB标签不支持局部描边。我一开始以为富文本里能像这样写[color#FF0000]红色[/color]也能用类似[stroke]标签给局部文字加描边。实际测试下来FGUI里没有这个标签。富文本的描边只能整体设置也就是通过richTextField.stroke设置整个组件所有文字的描边效果没办法做到一句话里前半句有描边、后半句没有。所以遇到这种一句话里部分文字需要特殊描边的需求别死磕富文本标签换个思路。要么拆成两个文本组件叠加排布要么把那个特殊文字直接用图片资源替代。我做过一个副本奖励提示里面史诗装备四个字需要加粗金边其他文字普通最后方案就是把史诗装备做成一个小图片塞进富文本里效果和性能都可控。提示富文本组件用UBB图片标签[img]xxx[/img]嵌入图标时图片不会受描边影响但文字描边宽度过大可能影响图片和文字的换行布局调试时注意观察整体排版。4. 描边的性能账为什么不能无脑加描边4.1 FGUI描边底层的顶点与OverDraw原理FGUI里动态字体加描边看似只是改个数字但底层开销并没有那么轻松。我最初以为描边就是文字Shader里加个轮廓计算后来调试性能才知道FGUI在大部分引擎的底层实现上是把文字本体和描边分别当成不同的网格处理的一个字符加上四周偏移的描边副本相当于生成多份字符网格顶点数成倍数增长。我拿一段12个字的中文文本举例。没有描边时一个字符可能是4到8个顶点12个字撑死百来个顶点。一旦开了2像素描边每个字符额外生成4个方向的描边副本顶点数直接翻好几倍。一段文字看不出来什么但如果界面上有几十个带描边的文本顶点数的增长会直接影响构建网格的时间和CPU的开销。更隐蔽的坑是OverDraw。描边说白了是用额外的透明像素填在文字周围这些透明像素也会参与渲染导致像素着色器的工作量增加。在手机上OverDraw是UI性能的隐形杀手尤其是低端机大量半透明描边叠在一起fps肉眼可见地往下掉。所以我做项目的一个硬性规定是描边不是默认打开的。每个文本组件要不要描边title团队要明确给出需求而不是觉得加个描边更保险就全员加上。4.2 减少描边开销的几条实用手段既然描边有开销那怎么在保证效果的同时尽可能省钱我说几条我在项目里实测有效的方法。第一能用位图字体预渲染描边的地方绝不用动态字体描边。伤害飘字、击杀提示、经验值变化这些高频文本直接上BMFont做带描边的字库。这个方案顶点开销只有正常文本一份性能最好。第二动态字体描边只在必要文本上开控制数量。我的经验是单个界面同时存在超过20个带动态描边的文本组件就开始谨慎了。如果界面文本特别多优先把高频或重要的文本用位图字体普通提示文本干脆放弃描边改用更省钱的方式提高可读性比如给文本加一个半透明底板。第三描边大小不必追求大。描边从1调到2视觉差异不大但顶点数是线性增长的字体越大增加越明显。能用1解决的不开2。第四尽量避免描边和阴影同时开。FGUI的阴影和描边底层都是靠额外网格实现的两个一起上顶点数叠加性能差且效果容易脏。我见过不少UI为了好看又描边又阴影结果在低端机上卡得不行。建议只保留描边阴影用美术切图实现或者干脆不搞。4.3 描边与图集、字体贴图的关系还有一个很多人忽略的点字体贴图的大小会影响描边效果和性能。FGUI里动态字体用的是系统字体但如果某个项目里为了让显示效果一致用了自定义字体文件字体会被FGUI动态生成到一张字体贴图上。描边宽度太大时描边区域需要更大的贴图采样范围如果字体贴图满足不了这么大范围就会出现描边断裂或者模糊的问题。我遇到过一次某个界面用了大字号标题描边给到3结果手机上标题描边边缘出现毛刺。排查到最后发现是字体贴图被FGUI限制得太大字体本身加上描边范围超出了纹理可用区域导致的。后来把描边降到2并且提升了该字体的纹理采样级别毛刺消失了。这条经验告诉我们描边不只是文字样式问题它和字体贴图、纹理资源是强相关的。设计阶段确定描边方案后最好尽早用目标机器实测不要让描边宽度的变更拖到后期才暴露。5. 常见问题与排查实录5.1 描边完全没效果先分清是编辑器问题还是代码问题代码里明明设了textField.stroke 2UI上却一点描边都没有这个问题我至少被问过五次。排查的时候先确认两点。第一确认FGUI版本。老版本FGUI对描边的支持不完善某些引擎适配层可能没有实现stroke效果升级FGUI到最新版试试。第二确认你改的是不是显示出来的那个组件。FGUI的组件挂点很多我遇到过开发同事拿到的是父节点的GComponent转成GTextField后set值但界面上显示的是另一个子文本当然没效果。还有一种情况是字体问题。某些系统字体本身就不支持描边渲染或者FGUI在某个引擎上把文本走了特殊的字体渲染管线导致描边参数被忽略。排查手段很简单把字体临时改成FGUI默认字体再确认描边是否出现。能出现说明是字体兼容性不出现说明是代码或版本问题。5.2 描边展示粗糙、边缘有锯齿描边出来但很丑通常有两个方向的问题。第一个方向是描边宽度和字体大小的比例失衡。字号小但描边宽边缘就粗糙。解决办法很简单字号小描边调小或者把整个字体做大一点再等比缩小显示。第二个方向是字体贴图分辨率不足。FGUI会把动态字体烘焙到贴图上如果贴图空间不够烘焙出来的字体本身分辨率就低描边边缘自然就烂。给字体设置更大的FontSize重新生成贴图会改善很多。还有就是别把字号只有20的字硬放大到100来用宁可把字体资源做成分辨率更高的版本。这里我特别想说很多人觉得描边效果差是FGUI不行其实大部分情况都是字体贴图规格没跟上。你在编辑器里看是清晰的因为编辑器里不做贴图优化一旦发布到目标平台字体被重新贴图问题就暴露了。所以做描边UI的时候一定要在真机上验收不要只在编辑器里看。5.3 描边把文字吃了或者盖住了旁边的字描边太宽文字的笔画之间会被填充看起来字变粗变糊这就是吃字。另外描边是会向外延伸的如果两个文本组件靠得太近描边可能会盖住旁边的图标或者其他文字视觉上像被叠了一层脏东西。处理吃字的办法前面说过了减少描边宽度或者换高对比度的描边颜色而不是靠加粗硬撑。处理遮挡问题需要给文本组件之间预留足够的间距或者排版时手动加一个描边安全边距。我项目里有一个经验值如果文本组件需要加2以上宽度的描边相邻元素之间的间距至少留足一个描边宽度否则就等着改审美吧。5.4 打开描边后性能明显下降UI没改什么就是给几个文本加了描边帧率直线下降。这不是玄学多半是加描边的文本数量太多并且描边宽度过大。优先检查界面上哪些文本开了stroke数量是否超过预估。如果确实需要特别多描边文本把文本分类处理固定的用位图字体动态的用代码描边再不行就砍需求给那些低频文本换成带底色底板。还有一种容易被忽略的情况一个文本组件的描边会连带父组件刷新。如果这个文本处于频繁刷新的列表item里描边的额外网格会让列表构建时间变长。我踩过列表滑动掉帧的坑最后发现是列表里所有item的文本都开了描边让列表UI的网格重建成本翻了好几倍。优化方式就是把列表里的动态文本描边全部去掉改用在列表底图上预置一个半透明底板效果可比描边干净多了。最后分享一个我自己的习惯做了这么多FGUI项目之后我养成了一个习惯任何界面设计阶段我都会先问一遍文本描边的必要性和数量。这个看起来很小的功能其实牵涉到字体贴图、顶点开销、列表刷新、真机渲染方方面面。它可以是锦上添花的装饰也可以是可读性的保底方案但一定不能变成性能的黑洞。以后再有人问我FGUI字体描边怎么加我会先反问一句你的文本是静态还是动态出现频率高不高有没有在真机上验证过性能想清楚这几个问题描边配置本身只是顺手的事。如果你也在做FGUI相关的UI优化希望这篇对你有帮助。有更好的方案也欢迎交流这玩意儿的坑基本都是踩一个填一个互相分享省事很多。
返回列表