因为是公司内作的项目,不方便开源,就只分享优化过程吧。html
逐日是一个移动端单机小游戏,使用Unity开发,目前已将项目使用的Unity升级到2019.4.14f1c1 (3e5991a5f6ba)
版本。git
在进行优化前,项目资源目录以下,能够看到,项目目录命名杂乱,包含不少需求迭代产生的旧资源、无用场景、未压缩的音视频等内容。github
因为此次主要是对于安装包大小的一些尝试,因此就不会特别关注游戏逻辑,总体能加载完成,不Crash就OK,没有对游戏逻辑上花费过多精力,后面的过程当中可能有小部分图被压糊了,有视频没法加载等问题,不影响缩包的最终结果。编辑器
如图所示,使用新版UnityEditor直接打包以后,生成的游戏Apk大小为218MB,这对于一个只有两个关卡的小游戏而言,明显过于臃肿了,假如上架到GooglePlay以后,光下载内容所消耗的时间和流量就劝退了一部分用户(更况且,如今GooglePlay限制了安装包大小不能大于100M!)。因此,对于安装包大小的优化是势在必行的。ide
- Mono使用即时(JIT)编译,并在运行时按需编译代码。
- IL2CPP使用提早(AOT)编译并在运行以前编译整个应用程序。
使用IL2CPP进行构建有助于提升运行速度,并减小包体大小,IL2CPP的工做流程以下图:工具
将ScriptingBackend设置为IL2CPP后,执行构建获得的安装包大小为210MB,比使用Mono少了8MB(这个项目里游戏逻辑很少,脚本比较少,对于较大的项目来讲,使用IL的优点更大)性能
Unity有提供一个Managed Stripping Level
选项,能够在构建时裁减掉未使用的代码,这个选项又是那个可选级别,IL2CPP不能关闭这个选项而且默认级别为Low
。不一样的裁剪级别对应的规则不一样,裁减掉游泳的代码可能性越高,而且有可能没法检测到经过反射引用其余代码的状况,致使程序崩溃。测试
设置Managed Stripping Level=High
后,安装包大小为209M。优化
对于Android Platform,我对于一个空的Unity3D项目进行过以下打包测试:动画
构建设置 | ApkSize |
---|---|
默认(Mono_Release_DotNetStandard2.0) | 16.9 MB |
ScriptBackend使用IL2CPP | 6.38 MB |
StrippingLevel=High | 6.02 MB |
CompressMethod=LZ4HC | 5.96 MB |
GraphicApi只使用OpenGLES2(Android 2.2+都支持) | 5.73 MB |
再往下就很难减少了......
在Editor中构建完成后,能够打开EditorLog,里面列出了在未压缩的状况下,不一样类型资源的占比以及每一个资源体积从大到小的排列,咱们能够直接根据Log查找哪些资源比较大,针对性地处理:
为何把这一步放在资源优化的第一位?由于先去掉无用的资源后面打包会快不少嘛。。。测试一个属性就要从新打个包有时候甚至须要从新Import整个项目半小时的时间伤不起啊~
在GitHub上随便找了个资源清理工具UnityAssetCleaner,对项目中的无用资源进行了清理:
这个步骤直接让Assets目录的大小从1.27GB
减少到192MB
,因为资源没有放在Resource下,因此未引用的资源并无打包到Apk中,这个步骤以后仅仅让包体大小减小到了208MB。
可是从新Import项目会快不少啊有木有~!这也看出来当时作的时候走了多少弯路。。。
第一次用Unity作游戏时,关卡切换、各类UI面板都是使用独立的场景作的,明明能够用Prefab的> _ <
看了下游戏里的场景,没有须要删掉的。。。若是有无用的场景,删除掉也能够减小包体大小。
在项目中,尽量下降图片和音视频的质量,使用低质量高压缩率的压缩格式,不只内存占用低、性能也更好;当出现质量不知足要求时,再逐步的提高尺寸和压缩格式来知足须要。Link→官方文档:不一样平台下支持的压缩格式
要在不修改源文件的状况下修改尺寸,能够直接在Unity里调整:
如上图所示,游戏里的纹理都是按照最大尺寸来导入的,而且因为使用的是Sprite动画,致使一个动画就要使用不少纹理,因此首要的问题是把不一样的图片调整到适当的尺寸,好比场景地图使用2048或1024,较大的Boss角色和树木使用512,小怪、小火球等纹理使用256或更小。
音效以及视频,也按照相似的方法,适当调低分辨率和码率(视频其实能够直接放CDN)。
调整完以后,从新打了个包,emmm...只有52MB了(固然,有一小部分纹理被我压糊了,适当调整以后最终结果其实相差不大)。
多个NPTO的纹理拼合到一块儿组成一个POT型的图集有利于Unity压缩图片,更能节省空间。建立方式以下(Unity2019+版本默认不使用给纹理打Tag的方式建立图集,而是须要手动建立)
将游戏中大小、用途相似的纹理,添加到各自的图集中,从新打包,包体大小33M。
这多是缩小Apk大小的最好方案了......
若是前面的一系列缩包方法都不能达到想要的效果,就能够考虑把资源单独打包拿出来,用户启动App时进行下载。
首先在Unity里给资源打标签,把资源分到对应的Bundle中。
而后执行构建AssetBundle脚本(脚本官网文档有提供),把AssetBundle包输出到指定路径。
在优化的过程当中我发现,Unity经过BuildSetting中的场景列表来查找全部依赖的资源,因此为了打出来独立的AssetBundle包并断开AssetBundle内的资源与Apk之间的依赖关系,就须要对场景作优化。
将场景中的内容简单粗暴地整合到一个GameObject内,制做成Prefab,用挂载Prefab的方式代替挂载场景,并删掉原来的场景,清理BuildSetting中的场景列表。这样就断开了AssetBundle内的资源与Apk之间的依赖。
个人办法是建立一个Init场景做为整个App的入口,里面只挂一个GameFramework脚原本执行一系列必要的AssetBundle加载过程、管理各个用于代替场景的Prefab。
这样修改以后,咱们构建出来的Apk就只会包含一个空的场景,以及C#脚本构建产生的dll,大小最小能有前面所说的5MB!!!
前面咱们把资源和Apk之间的依赖关系切断了,可是咱们仍是要想办法把它们动态加载回去的,这种方法就是AssetBundle包。
给资源打标签的过程是很是繁琐的,因此就使用脚本自动化处理了,会自动给ResForBundle下的直接子目录按照目录名递归添加标签。
运行了BuildAssetBundle命令以后,咱们就能在输出目录获得一系列相互依赖的AssetBundle文件了(也包括他们的manifest描述文件)。
咱们可使用Unity提供的AssetBundle Api来动态把AssetBundle文件加载到内存里,并根据路径读取它们包含的资源,因为游戏内的资源比较少,大约20~30MB,因此我就直接放在StreamingAssets目录下了(关于这个目录,是和Resource同样特殊的存在,感兴趣的话能够直接查一下)
在GameFramework启动时就加载全部的AssetBundle,并挂载Scene_Start.prefab到场景中,这样咱们的游戏流程又能正常走下去了。
固然这个过程当中还有好多好多细节要处理:
好比须要用宏来判断是在编辑器中仍是在移动端,从而采用不一样的自定义ResourceLoad来加载资源(若是是编辑器的话直接使用AssetDatabase Api经过路径加载资源,移动端则采用上面提到的AssetBundle内的资源);
如何递归地给指定目录下的内容添加标签并自动维护它们;
对场景进行精简优化,只保留一个入口场景加载AssetBundle和加载入口,构建后,Apk大小只有7M。
打出的AssetBundle能够在App启动时经过http拉取到本地,若是不嫌资源包大的话也能够直接放在StreamingAssets目录打到apk内。
直接上图好了:
KeyWorkds:Unity安装包体积优化,Unity减少安装包体积,Unity构建更小的Apk,Unity减少构建的安装包大小,Unity缩小安装包