这是第140篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间10分钟,认真读完必有收获。
本期目录:
动画骨骼节点批处理
粒子系统崩溃
自定义Shader在iOS上出现异常
Editor里可以达到100帧,真机上FPS一直低于30帧,
多语言下Text组件大小和文本长度的适配
动画
Q:动画系统中开启Optimize GameObject选项后,不必要的骨骼会消失,我们在骨骼下面挂了很多特效挂点,也清楚可以通过 Extra Transforms to Expose暴露出挂点,但是每个模型去手选暴露太过麻烦,能否有批量处理的方法呢?
A1:这个可以通过Asset Postprocessor在导入的时候对模型进行遍历,将符合指定前缀的节点路径加入到Model Importer的Extra Exposed Transform Paths属性里,之后再调用Optimize Transform Hierarchy去实现优化节点的效果,不知道我有没有理解错。
感谢Enak@UWA问答社区提供了回答
UWA:补充楼上,如果是已知确定的节点名字,可以直接赋值,总之都是用OnPreprocessModel,具体规则自己项目组里约定好就好了。
该回答由UWA提供
A3:楼上两个回答都有效,但还有一个更简单的办法。
先说一下Extra Transforms to Expose的大概原理(不一定都对),观察UnityEditor.dll相关实现可知:
1)开发者修改ExtraTransformstoExpose后,Unity会将信息保存在fbx的meta文件的extraExposedTransformPaths属性中。
2)在Unity根据fbx文件生成Prefab时,会依据meta文件中的extraExposedTransformPaths每一个String创建一个对应名字的空GameObject,并且不需考虑父子关系。
3)运行时,(推测)Prefab被Instantiate后,其Animator组件Awake时触发了Animator.Rebind方法,该方法会遍历Transform的子节点,将子节点的name和Avatar中骨骼信息对比,随后自动绑定节点。
这也是Optimized之后Animator组件上必须有Avatar的原因(非Optimized的GameObject可以将Animator组件的Avatar引用置空),想进一步研究Avatar可以将Inspector改为Debug模式查看。
基于以上分析,我们可以模仿这一过程,直接在Optimized之后的Prefab下面创建空的GameObject,随后将该空GameObject的名字改成约定好的骨骼挂点名字,这样就实现了和ExtraExposedTransformPaths相同的功能。
更进一步,我们可以利用Animator.Rebind方法实现运行时动态添加删除挂点的功能。这样做的好处是一来特效和策划同学无需反复修改Prefab(当然也可以离线制作好);二是无需为Prefab绑定所有的挂点,只需创建配置表里实际用到的挂点,甚至可以把挂点GameObject放入对象池回收利用,角色比较多且默认挂点比较多的情况下可以起到优化内存的作用。缺点就是Animator.Rebind耗时比较大,可以考虑随着Instantiate调用。
动态绑定挂点示例:
感谢张迪@UWA问答社区提供了回答
该问答来自UWA问答社区,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5c0a256a28c4e32cba3192eb
崩溃
Q:求助一个低概率随机崩溃问题,下面是崩溃的堆栈信息,我在网上搜索了下 “Abort message: ‘* Assertion at mini-arm.c:2634, condition `pdata.found == 1’ not met” 有人提到是与DLL相关,Android平台上应该是与本地的native code代码相关(当前项目使用了liblua53.so和其他lua相关的.so库),看了下mini-arm.c的源码,的确是在处理 mono_code_manager 相关一些东西,不知道有哪位大神遇见过并解决了的,请不吝赐教!
目前我只是尝试各种参数重新编译.so来测试… 同时准备采用IL2CPP方式来尝试定位。
题主:上周我们项目进行了7天外网小规模(2000左右玩家)测试,在这期间面对的崩溃问题的处理过程和问题原因,仍旧比较深刻。
之前,崩溃在libmono.so “Abort message: Assertion at mini-arm.c:2634, condition `pdata.found == 1’ not met” 的这个低概率随机崩溃问题一直困扰着我,主要是这个问题崩溃在mono,崩溃的栈信息完全与libunity.so,libmain.so无关,更别说基于Unity的逻辑层代码,面对我们的对外测试11月30号时间临近,我在28号的时候对mono进行一次简单的处理:将mono代码mini-arm.c:2634 中的断言 g_assert (pdata.found == 1); 注释掉,换成了普通的错误信息输出,编译后直接提交到项目工程中,毕竟这个改动算是无害处理,然后开始去优化项目中卡顿点去了,也没有继续跟踪这个改动,因为当时想到是小概率事件。
11月29号提交给发行版本后,晚上发行反馈说,感觉这个版本的崩溃率有点高,但说没有实际统计过,得到这个反馈后,我也没有特别在意,继续处理其他问题去了(真后悔当时没有去看bugly)
11月30号中午正式开测,到下午4-5点后,部分同事反馈告诉我说,崩溃率有点高,平时测试都不会闪退,今天闪退2-3次了…其中一个同事告诉我说,挂机15分钟内必崩溃…当时听到这个问题后心里一紧,赶紧去看了下bugly,发现崩溃率到了7.x%,远高于平时我们测试2.x%的统计,我再仔细一看,都集中在各种粒子系统崩溃中:
看到这些崩溃后,回忆了下最近两天的优化,在优化卡顿的问题中,当时对粒子系统组件 ParticleSystem 进行了本地引用缓存,不需要每次播放技能特效时都进行一次GetGetComponentsInChildren
(true),同时对技能特效粒子系统停止时调用stop()修改为pause()(技能特效播放完后就回收到pool中,所以调用pause()不会有残留,此时不需要释放数据,在切换场景时真正释放特效数据),这时我从崩溃栈看到信息首先想到的修改应该是stop()修改pause造成的崩溃,我让程序同学帮我在iOS上挂机复现这个问题,果然连续2次挂机打怪都复现了,8-12分钟内必崩,崩溃栈和bugly上统计一样。
这时我立刻开始在iOS上直接修改编译测试,将pause()还原为stop(),继续测试… 意外的是问题依然存在,15分钟内挂机必崩溃闪退,然后又思考了下,stop()只是停止当前粒子更新和粒子发射,但数据没有清,然后继续修改代码:stop()调用后,立刻调用clear()再次测试,郁闷的是仍旧15分钟内挂机必闪退崩溃…再仔细看了下播放特效的代码上下逻辑处理,找不出问题,平时的崩溃率绝对没有这么高,仔细回想了下,这2天还剩余唯一的改动就是添加了对 ParticleSystem本地引用缓存,于是直接在iOS上修改为播放完后立刻本地引用置空,编译,更新到真机测试…一直测试了30多分钟,40多分钟都没有再闪退崩溃,哪怕改回pause()调用,也没有再崩溃了…这个测试结果有点让我意外,当时没有细想,立刻正式修改热更新到外网…果然外网的崩溃率第二天的记录再下降,但仍然存在,但我心里已经知道问题了,因为还剩余UI特效播放未处理(技能特效和UI特效我们是划分开的,技能有高低配设置,初始化解析不同),同样修改UI特效之后,闪退崩溃几率继续下降,一直降低到1-2%,剩余的崩溃仍旧是UI粒子系统相关的,主要是部分UI功能业务层未主动调用回收特效的接口导致。
这次对外测试结束后,我再回头看看崩溃信息,意外地发现让我最头痛的mono崩溃数据终止在28号,也就是在我28号修改mono代码之后就再也没有报崩溃在libmono.so “Abort message: ‘* Assertion at mini-arm.c:2634, condition `pdata.found == 1’ not met” 这个堆栈数据,回忆了下,以前低概率崩溃本质就是UI粒子系统导致的,有部分业务层逻辑未主动调用释放接口的,而mono-arm.c崩溃行 g_assert (pdata.found == 1) 断言阻断了继续向上层抛出堆栈信息…
我们的Unity版本是5.6.5P4,为什么缓存粒子系统组件不立刻释放会导致崩溃未知,可能是Unity的底层Bug,也可能真的是我们对粒子系统组件ParticleSystem逻辑上下调用有问题。
感谢yangyonggen@UWA问答社区分享了该经验,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5becf477a1dae055a7c051b7
Shader
Q:自定义Shader在安卓手机上正常,在iOS上出现异常, 伴随一大堆的警告,而且资源会都变成黑色。
A1:第一行提示不完整,应该是说是没有顶点Shader;
第二行提示Pass都被移除了;
第三行提示没有可用的Subshaders或者Fallbacks;
总结起来,就是Shader是个空Shader,不可用。所以进行了替换,表现就是黑色,而不是材质丢失。
没用过ShaderForge,但是看过几眼美术同学从ShaderForge导出来的Shader,包含有关于平台的编译选项,大概是编译选项不对,要检查下ShaderForge编译选项设置。
感谢赵林@UWA问答社区提供了回答
A2:首先切换到iOS平台,选择出错的Shader,有可能会显示Shader is not supported on this GPU,点击Compile and Show Code,应该会显示“Compile errors generating this shader.”
如果是这样的话,说明你用了Graphics API不支持的Feature。
感谢凯奥斯@UWA问答社区提供了回答
该问答来自UWA问答社区,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5c07c21b28c4e32cba3192b8
FPS
Q:最近跑一个项目,在真机上无法超过30帧,看了耗时是只是十几毫秒。代码里没有对Application.targetframerate的限制。是什么原因呢?
A:可能是开了垂直同步限帧30帧那一档,垂直同步修改为60帧那一个,或者关闭垂直同步,用Application.targetframerate自己限帧。
感谢赵林@UWA问答社区提供回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5c04d886a7bd4622df98d6f0
UI
Q:多语言下,如何做Text组件大小和文本长度的适配?同一段文本,同一字体大小,不同语言下其长度(文字个数)和文字大小(像素宽高)不同,那么如何防止文本超出Text组件显示范围?如何保证视觉效果合理(文字不会太小也不会太大,位置不会太偏)?有哪些自动化的方案?
A:首先应该根据一种语言规划好文本框的大致大小,建议根据英语来规划,汉字有时候两个字,翻译成其他语言就是一长串。英语较为折中。
一种方式是固定好文本框大小,Bestfit,设置好最小和最大的字体,如果不拓展Bestfit,这种就有点粗暴。我们之前的项目就是这么粗暴;另一种方式是用ContentSizeFitter,可以计算出单个字符的大小,根据自己设定的最大最小值,通过扩展脚本可以动态的实现水平和垂直的扩展。
一般来说,肯定逃不掉特别极端的,所以再扩展个脚本处理这种非常特殊的。
感谢赵林@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5c09e4f8f937bd2cbf9deb03
今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。
领取专属 10元无门槛券
私享最新 技术干货