题外话:昨天晚上11点看了一下任天堂的宝可梦见面会直播,感觉还是蛮期待新作《宝可梦传说-阿尔宙斯》的,所以文章封面就有了
关于动画文件的压缩和优化网上已经有很多解决方案了,大多数都是来自于uwa社区的那篇文章(文末给出参考链接),我这里主要也是参考那篇文章,然后根据自己的理解和项目实操给出总结,也方便以后自己查阅。我们项目使用的Unity版本2019.4.10,animation动画文件是包含在.fbx文件中的,不是单独的.anim文件。
关于Unity中动画文件的压缩和优化,我主要从下面三个方向着手:
文件压缩方式
文件浮点数精度的压缩
文件去除Scale曲线 来减少动画文件的内存占用。
一、文件压缩方式(Anim.Compression)
Off 关闭压缩
Keyframe Reduction 减少没有必要的关键帧
Optimal 优化压缩,官方会选择最优的压缩方式来进行压缩,建议选择这个,我们项目也是选择的这个。
关于这三个压缩之后的文件内存大小,如下图
从图中可以看出压缩和不压缩文件大小差距还是很大的,其中Optimal压缩的文件内存最小,此大小和真机运行时的内存 很接近(因为我测试的时候受到fbx文件本身大小影响),此文件是没有进行Scale曲线删除和精度压缩的,如果进行之后文件还会变小,下面会逐一介绍。
二、文件浮点数精度的压缩
看网上的文章大多数都是说通过把精度进行压缩可以减少动画内存的占用,但是一直没有困惑于原本就是float类型,占四个字节,只是缩短精度了,为什么占用内存会减少。后来看到一篇文章,才明白原来是缩短float类型的精度,导致动画文件内点的位置发生了变化,引起Constant Curve和Dense Curve的数量也有可能发生变化,最终可能导致动画的点更稀疏,而连续相同的点更多了。所以Dense Curve是减少了,Constant Curve是增多了,总的内存是减小了。说白了就是f1=0.1234和f2=0.1233和f3=0.1232经过缩短精度都变成了0.123,结果原本三个点构成的曲线就变成了一条直线了。引擎内部优化的时候会对这样的直线进行处理,导致内存占用减少了。所有这种优化不会减少AB文件的大小,因为float类型始终占用4个字节,从而也无法减少安装包的大小(只是个人理解,没有进行AB打包测试,有待考究)。
测试说明,我测试的动画文件是包含在fbx文件内的。
测试1:Anim.Compression文件类型选择Off,然后对比缩减精度前后文件大小,发现没有变化。
测试2:Anim.Compression文件类型选择Optimal,然后对比缩减精度前后的文件大小,发现还是没有变化。
测试3:Scale曲线保留和剔除,发现缩减精度前后文件大小都没有变化。
测试结论:也许是我测试的动画文件刚好缩减精度后没有引起动画曲线的改变,也许是我测试的环境是在Editor下面看内存大小,没有真机测试缩减精度前后数据对比。从理论上面来说内存应该是会改变的,如果你测试的结论和我不一样或者我测试的方式有什么不对的,可以给我留言,一起讨论,一起学习进步。
三、文件去除Scale曲线
所谓的Scale曲线,就是你选择一个动画文件然后用快捷键Ctrl + 6 打开动画编辑器Animation就可以看到如下图的Scale曲线了。
去除Scale曲线前文件的大小
进行去除Scale曲线后的文件如下图
去除Scale曲线后的文件大小
去除Scale曲线效果很明显,自己测试一下就知道了。唯一需要注意的是:由于fbx文件是只读的,我们在第一次导入的时候Unity会缓存一份fbx文件的数据到项目更目录下的Library文件夹内。我们去除的Scale曲线文件也是保存到这里面的(如果你删除Libray文件夹,从新加载一下项目动画文件,Scale曲线又回来了),所有不受Svn或者Git的管理。我如果你要去除Scale曲线,需要在构建机上面执行一下去除Scale曲线脚本,这样再构建包就可以了。
下面是优化脚本,你只需要找个项目的Editor文件夹,把文件丢进去,邮件选择想要优化的文件夹,然后右键+Reimport,重新导入一下文件就可以了,脚本很简单,就不做解释说明了。
using
System
;
using
System.Collections.Generic
;
using
System.Text
;
using
UnityEditor
;
using
UnityEngine
;
namespace
AssetPostProcessors
/// <summary>
/// 把此文件放到项目内的Editor文件夹下面即可。
/// </summary>
public
class
FbxPostProcessor
: AssetPostprocessor
public
void
OnPostprocessModel(GameObject gameObject)
AnimationClip[] animationClipList = UnityEngine.Object.FindObjectsOfType(
typeof
(AnimationClip))
as
AnimationClip[];
foreach
(AnimationClip theAnimation
in
animationClipList)
//exclude scale curve
foreach
(EditorCurveBinding theCurveBinding
in
AnimationUtility.GetCurveBindings(theAnimation))
string
name = theCurveBinding.propertyName.ToLower;
if
(name.Contains(
"scale"
))
AnimationUtility.SetEditorCurve(theAnimation, theCurveBinding,
null
);
ModelImporter model = (ModelImporter)assetImporter;
if
(model !=
null
)
//set AnimationCompression.Optimal
model.animationCompression = ModelImporterAnimationCompression.Optimal;
ModelImporterClipAnimation[] clipAnims = model.clipAnimations;
//Floating point precision is compressed to f3
for
(
int
ii =
0
; ii < clipAnims.Length; ++ii)
ClipAnimationInfoCurve[] newCurves = clipAnims[ii].curves;
//Debug.LogFormat("OnPostprocessFBX : newCurves Clip Count : {0}", newCurves.Length);
if
(newCurves ==
null
)
continue
;
foreach
(ClipAnimationInfoCurve animCurve
in
newCurves)
if
(animCurve.curve ==
null
)
continue
;
Keyframe[] keyFrames = animCurve.curve.keys;
for
(
int
i =
0
; i < keyFrames.Length; ++i )
Keyframe key = animCurve.curve.keys[i];
key.
value
=
float
.Parse(key.
value
.ToString(
"f3"
));
key.inTangent =
float
.Parse(key.inTangent.ToString(
"f3"
));
key.outTangent =
float
.Parse(key.outTangent.ToString(
"f3"
));
keyFrames[i] = key;
animCurve.curve.keys = keyFrames;
clipAnims[ii].curves = newCurves;
if
(clipAnims.Length >
0
)
AssetDatabase.Refresh;
来源知乎专栏:Unity之旅
返回搜狐,查看更多
责任编辑:
声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。