Unity动画文件(AnimationClip)精简容量的研究

一、研究动画精简的原因

最近做一个3D卡牌项目,角色非常的多,每个角色的动作也比较多。
项目打包之后,发现包体容量比较大,然后分析了AssetBundle资源,发现里面的动画文件非常的大,有些角色一个动画文件打包AssetBundle之后居然有1M多,这样一个角色单纯是动画文件就占了超过10M了。
由于这次的美术团队是新合作的,一开始也没有留意动画的制作方式是否有问题,既然发现存在异常了,于是就对美术资源进行分析。
经过一番尝试之后,成果还是很明显的,在保持原有动画效果的基础上,能把原来1m多的的AssetBundle文件减小到不到200k。

二、动画文件容量分析

1、动画文件的导入问题

一般复杂的角色动画,都不会是在Unity里面k出来的,而是从比如3DsMax之类的软件制作,然后导出fbx文件到Unity里面使用,在导入的时候,Unity有针对动画的压缩选项,Anim.Compression:
在这里插入图片描述

具体有3种不同的选项:
(1).Off
(2)Keyframe Reduction
(3)Optimal
具体来研究一下这三个选项对产生的动画文件的区别在哪里:
(1).Off:
顾名思义,就是完全不压缩。但实际上,他还有另外的一个作用,就是烘焙关键帧。
举个例子:
我们有这么一段动画,正常的情况下,关键帧的分布情况是这样的。
在这里插入图片描述

现在我们把动画压缩的选项改为Off
在这里插入图片描述

现在再看关键帧,就变成这样了。
在选择了Off之后,引擎对于动画的每一个节点的每一帧,都做了计算并生成了关键帧,这样做的好处是,动画会变得非常的准确,不再因为动画的曲线过渡,或者打忘记打前后关键帧而导致各种动画的滑步、抖动、动画切换某些节点异常等问题。
不过说句我个人的看法。如果动画真的出现了各种异常,本身就是k动画的过程中的疏忽或者错漏导致的,正常的解决办法应该是去修改原始动画文件,而不是靠烘焙关键帧这种操作来避免错误。
从肉眼可以看出当选择了Off之后,由于关键帧大量增多,所以动画文件也会变得巨大无比。
(2)Keyframe Reduction
选择这种方式,从单词直译,就是减少关键帧。这种情况下,Unity会保持动画在导出Fbx时的关键帧信息,并做一定量的关键帧优化。选择这个选项后,动画关键帧看起来是不会有什么区别的。
在这里插入图片描述
(3)Optimal
从单词直译,这种方式叫做最优的,最佳的。从命名上可以看出,Unity是非常推荐这种压缩方式的。实际上,从关键帧的分布看,Optimal和KeyframeReduction是一样的。从Unity的官方文档看,Optimal是在KeyframeReduction的基础上,进一步选择最优的动画曲线,最终减少内存的占用。
Optimal实际上进行的操作,应该是进一步的去掉重复和多余的关键帧,以最少关键帧作为曲线过渡的形式做插值计算。

单纯从Unity的Api描述,还有实际看Animation时间轴的关键帧分别,我们就可以知道,从容量来说,肯定是Off>KeyframeReduction>Optimal的。所以按道理来说,这里应该是选择Optimal作为动画的压缩方式。
在我的这个项目里面,我发现很多动画选择了Off,我问了一下动画师,原来是因为有些角色的动画从3DsMax里面导入到Unity,发现播放时角色的脚出现了滑步,而原始动作的脚应该是固定的。所以动画师选择了把动画压缩设置Off的方式,想用大量的关键帧去解决这个问题。
这里我想说一个纯美术人员和技术人员之间的明显分歧,篇幅有点长,如果不敢兴趣可以直接跳过到下一小节。
从动画师的角度看,最终动画的确不会滑步了,感觉问题已经解决了,所以也不会去再找技术去解决这个问题。但从技术的角度看,虽然看起来的动画表现好像是没问题的,但实际上产生了容量巨大的动画文件,占用的包体容量和内存也会大幅度的升高。
我一直觉得,美术团队一定要有可靠的技术美术(TA)作为支撑,不能靠纯美术人员凭经验来想办法解决问题。因为所谓的经验,一般就是,以前某个项目是怎样怎样做没问题。但实际的情况是,不同的项目,使用资源的方式和遇到的重点问题可能都不一样,如果在遇到问题的时候,没有从学术性上的理论支撑,而单凭经验来解决问题,其实有很大的运气成分,可能真的解决了问题,也可能根本没有解决,甚至产生了新的问题。
有人可能会说,项目很紧急,来不及思考这么多,就先随便动手解决了。但如果连产生问题的根本原因都不知道的情况下,贸然动手往往是浪费更多人力成本而没有达到目的。花时间做实际的研究分析,虽然前期可能会花更多时间,但实际动手的过程会很快,往往是写几行代码批处理一下就行了,并且问题解决得彻底。我个人习惯是比较喜欢每件事情都搞清楚原因和原理,才去动手做的。
当然了,如果某些团队没有合格的技术人员作为支撑,也的确没有查找问题的能力,到最后就只能碰运气了。
回到我的项目里,遇到了精简关键帧之后出现动画异常的问题时,我认为正确的解决办法是动画师通过分析关键帧过渡出错的地方,然后在正确的位置加关键帧来解决。但这种方式需要动画师有比较高的水平和经验,我这边的美术团队暂时没有这个能力。所以只能我来想办法通过技术手段来解决问题了。

2、anim文件的分离

先说一个在Unity使用资源的习惯问题。
一般导入3D模型到Unity里面,都是使用fbx格式的模型。在Unity里面,我们可以直接看到Fbx文件展开之后,里面可能有网格模型(Mesh),有动画片段,也可能有贴图,有导入时Unity自动生成的默认材质球等等。
我们是可以直接用这些Fbx里面的资源的一部分,比如只有Mesh,只用动画。但在打包的时候,Unity会默认引用了整个Fbx文件,把我们没有用到的一些资源也作为引用给打包进去了。如果我们不想引用多余的Fbx文件部分,只是想要使用某些东西,我们可以把这部分的内容复制出来。
比如我这里有一个测试技能动画的fbx文件,我可以在展开fbx之后,把里面的skill动画选中,然后按Ctrl+d快捷键,把skill动画片段复制出来。
在这里插入图片描述
由于接下来要分析三种不同压缩设置对文件容量的实际影响,所以我分别设置了Off、KeyframeReduction和Optimal三种情况,并用Ctrl+d把文件复制出来:
在这里插入图片描述

可以看到,原始的fbx文件只有2m左右,Off模式的anim文件竟然达到了差不多22m,而KeyframeReduction和Optimal小一点,也达到了8m多。
看着这个容量,可能大家会说,原来fbx文件这么小,Ctrl+d之后反而变大了,是不是有问题?其实,我们使用引擎资源要习惯不要单纯看文件本身的内存大小。这个就好比使用贴图,不管你用jpg、png把图片压缩得多小,但实际上进入了Unity之后,它还是会还原成RGBA32,实际容量是在引擎里面再次调整的。
这里也是一样的,fbx文件小的原因是他使用二进制格式来存储并压缩。而我们Ctrl+d出来的anim文件,是以纯文本的方式记录数据的,anim文件容量虽然大,但实际上在Unity真正使用的时候,两者是一样的。
复制出来anim之后,我们已经把动画文件和fbx脱离关系了,而且由于它是纯文本,所以我们可以进行各种直接的编辑。
在这里插入图片描述

随便打开一个anim文件看看,里面很容易看懂,就是记录了这段动画的各种曲线,每条曲线里面有各个关键帧的数据

3、不同的导入设置对分离anim文件的具体影响

由于刚才说了,anim文件本身的容量并不能准确表达最终动画的容量,所以我们先把3段动画打包成AssetBundle,然后对比一下容量
在这里插入图片描述

这里可以很直观的看出,Off的动画容量是惨不忍睹的,占了1.4m,KeyframeReduction的容量也达到了674k,而Optimal的是262k。
如果从这样的角度看,只要设置成Optimal,是不是可以很轻松的解决了动画容量的问题呢?答案并没有这么简单。因为策划设置成Off的原因,是有些动画出现了滑步问题。
我们这里对比一下容量,只是为了搞清楚3种不同的动画压缩设置,实际上产生了什么差异而已。我们可以通过对比3个anim文件,看看他们的容量为什么差别这么大。

4、影响文件容量的因素

先看skill_Off和skill_KeyframeReduction这两个anim文件的对比
在这里插入图片描述

Off的容量很大,是因为记录了非常多的关键帧信息,包括了各种位移旋转缩放的曲线、编辑器用的曲线,都是每个物体节点每隔0.03333秒都会记录一个关键帧,这部分数据比KeyframeReduction大很多倍,所以直接导致了文件容量也大很多。其他部分两个文件是一样的。
再来看skill_KeyframeReduction和skill_Optimal这两个anim文件的对比。这两个文件容量是几乎一样的
在这里插入图片描述

通过对比,发现两个文件基本上是一样的,只有m_UseHighQualityCurve这个选项。在KeyframeReduction里面这个值是1,在Optimal里面这个值是0。
我个人的猜测是,当这个值开启的时候,Unity会尽量保留原有的动画曲线数据,当关闭时,Unity会对动画曲线做精简,去掉Unity认为没必要的曲线数据,选择最优的曲线数据保留。由于anim文件其他部分是一样的,所以这个设置的生效阶段,应该是在动画真正使用的时候才会触发,在编译AssetBundle的时候,也会触发。

三、对动画文件进行精简尝试

基于以上的分析我们可以对anim文件做一下的处理

1、去掉重复的帧数

Off的anim产生的文件大,是因为它记录的帧很多,但实际上,大部分的帧都是重复无用的,所以其实可以通过一定的方法,把重复的帧去掉。
我的处理方式是,在anim文件里面进行字符串处理,找到同一个物体的Curve段,然后每个Curve段找到不同的time的段,判断下一个time的各种参数是否和上一个time完全一样,如果完全一样,就把中间的time段全部去掉,只保留一头一尾的time的关键帧信息。

2、修改HighQualityCurve项

匹配m_UseHighQualityCurve: 1字符串,如果存在,就替换成m_UseHighQualityCurve: 0。

3、精简浮点数位数

由于Unity默认生成的anim文件里面的各种浮点数都是保留了8位小数,但实际上太精确的数值作用并不是很大,我认为保留3-4位小数就基本上能满足动画表现了。所以可以通过匹配字符串,找到浮点数,并把小数位保留成4位。
这些操作,我都实现了,先说一下结果:
1、手动去掉帧数的操作,在KeyframeReduction和Optimal模式下,并没有很大的效果,这应该是因为本身这两种模式就会精简关键帧,所以一些很明显的重复的关键帧,本身就会被去掉,所以自己手动去掉的关键帧的意义不是很大。
而且由于有曲线数据的存在,有些看着像是重复的关键帧,实际上是不能直接去掉的,比如原来中间有一帧被删掉了,前后的帧的曲线应该相应调整数值才能达到原有的动画效果。如果直接去掉,动画的效果就可能被改变了。
所以这个操作被我放弃了。
2、手动修改UseHighQualityCurve,是很有效果的,等于是在anim文件脱离了Fbx后,还能再进行一次曲线优化的设置。
3、精简浮点数。这个操作也是很有效果,我猜测的是,在关掉UseHighQualityCurve后,Unity会优化曲线,而精简了浮点数之后,把一些原本就非常接近的关键帧,让Unity认为是可以进行优化的曲线,并进行了合并。
于是,我对之前3个anim文件,进行了字符串修改,得到了下面的结果,后面加x的就是处理过的文件
在这里插入图片描述
在这里插入图片描述

从文本对比看,就是在原有的基础上改了曲线模式和小数点位数

发现文件并没有小很多,我们再打包AssetBundle看看:
在这里插入图片描述

可以看到精简之后,不论原来设置了哪种动画压缩方式,包括off,最终也变成了100多k的容量了。
我们把生成的动画播放一下,发现用Off模式生成出来的anim文件经过精简之后,并没有出现滑步的问题。
最后,我们把6个文件通过AssetBundle加载到实际运行里面,看看运行的内存占用情况:
在这里插入图片描述

可以看出,没有带x的,符合我们的预期,是Off>KeyframeReduction>Optimal,
然后带x的,就算是Off_x也比不带x的Optimal要占用得小。在精简之后,KeyframeReduction和Optimal占用的内存是一样的。

到这里,试验成功,可以些批量工具把整个项目的anim文件全部处理一下,就完事了。

四、结论

1、关于精简的操作

通过AssetBundle文件的容量、播放的效果、占用内存的大小这3方面的对比,可以看出,如果没有特殊情况,动画压缩选择Optimal,并且精简浮点小数位数,是最优的选择。
如果出现脚步滑动等状况,可以选择Off,并且关闭HighQualityCurve,精简浮点小数,也能得到较小的AssetBundle容量和内存占用。
本来我是想写工具,在anim导入的时候,自动修改压缩选项并精简浮点数。不过由于有些情况还是需要使用Off选项,所以后来并没有这么做,而是做了根据文件夹寻找anim文件并批量修改的工具来处理。

2、关于字符串处理的一些经验

我这里没有直接给出精简anim文件的工具代码,因为这些毕竟是公司项目在使用的工具。如果有这方面疑问,可以私下联系我进行交流。
我可以提供一些我做替换时遇到的问题,各位可以自己注意一下:
一开始,我是使用正则匹配来查找和替换文本的,这样代码写起来很简单。但在实际的运行中,发现有些anim文件实在很大,纯文本可能达到上百万行。这样巨大的文本,用正则匹配速度有点慢,不过也还勉强能接受。但到了替换这一步,如果直接操作巨大的字符串,会发现不论是用String自带的Replace方法,还是正则的Replace方法,都会非常非常的慢,甚至会卡死。
后来我改了实现的方式,改为了把巨大的字符串通过’\n’来分割为多行,然后每一行单独的处理查找和替换工作,最后,使用string.Join方法,把多行数据用’\n’重新拼接起来,这样的速度就非常的快,上百万行的文件,2-3秒就能处理完。
在匹配小数的时候,还要留意有没有小数是用科学计数法来表达的,也要把这种情况给匹配出来。

### 回答1: 对于优化 Unity 打包的 WebGL 的卡顿问题,可以尝试以下几种方法: 1. 减少网页的内容和资源大小,可以使用图片压缩、资源压缩等方式来减小网页的大小。 2. 减少 WebGL 渲染的复杂度,可以尝试简化模型、减少纹理的大小、减少灯光的数量等。 3. 使用合适的浏览器和设备,不同的浏览器和设备对 WebGL 的支持程度和效果都有所不同,因此选择适合的浏览器和设备可以有效地优化性能。 4. 调整 Unity 的设置,比如减少渲染帧率、调整 Quality Settings 等。 希望这些方法能够帮助你解决卡顿问题。 ### 回答2: Unity 打包 WebGL 运行卡顿是由于性能不足引起的,可以采取以下措施来优化: 1. 减少三维模型和纹理的数量和大小:大量使用高分辨率的模型和纹理会增加网页加载时间和渲染负荷。可以优化模型和纹理的分辨率,减少使用复杂的模型和减小纹理的尺寸,以降低资源消耗和提高性能。 2. 合并网格和减少渲染批次:通过将多个物体合并成一个网格,可以减少渲染调用,减少渲染批次,从而提高性能。可以使用Unity的网格合并功能来合并网格。同时,还可以使用批处理技术将具有相同材质的物体合并到一个批次中。 3. 减少动画和粒子效果:过多和复杂的动画和粒子效果会消耗大量的计算资源和内存,导致性能下降。可以减少或简化动画和粒子效果的数量和复杂度,以改善性能。 4. 使用低多边形模型和简化碰撞体:高多边形模型和复杂的碰撞体会增加渲染和碰撞检测的计算量,导致性能下降。可以使用低多边形模型来代替高多边形模型,并使用简化的碰撞体来加速碰撞检测。 5. 异步加载资源:将资源的加载和初始化分散到多个帧中进行,以避免一次性加载过多资源导致卡顿。可以使用Unity的异步加载资源的功能来实现。 6. 使用LOD(层次细节)技术:通过使用不同层次的细节模型,根据距离将更高细节的模型替换为更低细节的模型,可以减少渲染负荷,提高性能。 7. 编写高效的脚本和代码:避免使用复杂和低效的脚本和代码,优化关键路径的性能,避免频繁的内存分配和垃圾回收。 8. 使用GPU实例化:使用Unity的GPU实例化功能来复制和渲染大量相同或相似的物体,以提高渲染性能。 通过以上优化措施,可以减少资源消耗、提高渲染性能,从而解决Unity打包WebGL运行卡顿的问题。 ### 回答3: Unity打包WebGL运行卡顿的原因可能有很多,以下是一些优化建议: 1. 减少包的大小:首先,要确保你的资源文件合理压缩,不要包含过多的无用资源。可以使用压缩的图片格式,限制纹理的大小,移除不必要的模型和音频等。 2. 降低渲染负荷:Unity中的多个物体和效果绘制会增加渲染负荷。可以尝试优化物体的数量和复杂性,减少特效的使用等。 3. 使用合理的光照与阴影:光照和阴影是游戏中产生高额渲染开销的因素之一。可以降低光照和阴影的质量,或者使用更轻量级的光照模型,如平行光。 4. 合理优化脚本和代码:检查你的脚本和代码,尽量避免使用耗时的操作,如频繁的循环,频繁的内存分配等。可以使用Unity Profiler工具来分析和优化性能。 5. 合理设置物理引擎:物理引擎可以导致额外的计算开销。如果游戏中没有必要的物理模拟,可以禁用物理引擎或减少物理引擎的计算步长。 6. 打包设置优化:在Unity打包设置中,可以根据具体需求调整WebGL的压缩和优化设置,如物理内存大小、压缩选项等。 7. 平台适配:尽可能在WebGL平台上测试和优化你的游戏,确保它在WebGL上运行得更流畅。在开发过程中,可以使用真机测试来评估性能表现。 总的来说,优化WebGL性能是一个综合性的工作,需要考虑多个因素。通过以上的方法和工具,可以逐步提高游戏在WebGL平台上的运行效果。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值