总结项目优化中两个对包体大小优化比较大的点:
- 动画文件
- 模型默认材质球
一. 动画文件:
对于3d项目模型的动作文件占资源大小还是有蛮大的一部分,所以想在不影响美术要求的情况下能不能对动作文件进行优化,在UWA上还真的发现有前辈已经总结了优化的方法(链接就是),总结来说就是两点:1.压缩浮点数精度 2.去除scale曲线。对比前后.anim文件效果如下:
对于.anim原始文件的大小还是优化很明显的,打bundle后包体大小跟内存大小也相应的减小。
二.模型默认材质球
要优化包体大小,就要清楚知道包里到底有哪些资源,资源在打成bundle后到底有没有冗余,通过 AssetStudio可以查看包里具体的资源, 打开我们打好的bundle所在文件夹,根据Filter Type选项过滤只留Shader类型资源并大小排序,不看不知道,一看吓一跳,内置Standard shader冗余了有两百多个,按每个701900b算总共占据了100M多包体大小,这是非常可怕的。
那么这些Standard shader到底是在哪些bundle包里?为什么会被打进包里?选中资源右键 Show original file 可以看其所在的bundle,通过分析bundle manifest文件得知原来这些Standard shader是模型资源外部导进工程的时候系统赋予了默认材质球,
即使后面做模型预制体的时候使用了美术其它的材质球,这个Standard shader还是会被引用打进bundle。那能不能哪里可以设置让Standard shader只打进一个包里或者说怎样处理不冗余,GraphicsSetting-> always included shader 倒是可以设置,这边设置的shader意思是该shader不会被打进引用它的bundle里,而是会在出包的时候直接当作依赖资源放进游戏包里。我们在GraphicsSetting-> always included shader设置了Standard shader重新打包看下效果:
如意料中的Standard shader全部没了,对比bundle文件夹的大小,少了差不多100M,这总bundle资源大小减的有点多。
不要高兴的太早,我们只是减少了bundle的大小,重新打个apk包你会发现编译的时间好长,而且打的apk包比之前反而大了很多,What?又出了什么鬼问题?查资料发现是由于 GraphicsSetting-> always included shader 设置了内置Standard shader,而Standard shader是有成千上万的变体,被加到always include 的shader会将shader的所有变体打包到游戏,所以在GraphicsSetting-> always included shader 设置内置Standard shader这个方法是行不通的。
那如何处理呢?通常我们模型很少会用Standard shader的材质球,而是用美术给的实际满足效果的shader材质球,所以还有一个办法是去掉模型对默认材质球的依赖,那其实只要在模型导入的时候把模型Renderer引用的材质球置为空就行了,代码如下:
using UnityEngine;
using System.Collections.Generic;
using UnityEditor;
public class DeletFbxDefaultMat
{
private static Dictionary<string, GameObject> fbxs = new Dictionary<string, GameObject>();
public static bool reimportModle = false;
[MenuItem("Assets/删除模型默认材质球")]
public static void DelFbxDefaultMat()
{
string[] guids = null;
List<string> path = new List<string>();
fbxs.Clear();
UnityEngine.Object[] objs = Selection.GetFiltered(typeof(object), SelectionMode.Assets);
if (objs.Length > 0)
{
for (int i = 0; i < objs.Length; i++)
{
if (objs[i].GetType() == typeof(GameObject))
{
string assetPath = AssetDatabase.GetAssetPath(objs[i]);
GetModel(assetPath);
}
else
{
path.Add(AssetDatabase.GetAssetPath(objs[i]));
}
}
if (path.Count > 0)
{
guids = AssetDatabase.FindAssets(string.Format("t:{0}", typeof(GameObject).ToString().Replace("UnityEngine.", "")), path.ToArray());
}
else
{
guids = new string[] { };
}
}
for (int i = 0; i < guids.Length; i++)
{
string assetPath = AssetDatabase.GUIDToAssetPath(guids[i]);
GetModel(assetPath);
}
Debug.Log("模型数量:" + fbxs.Count);
StartDelete();
}
static void GetModel(string path)
{
string pathLower = path.ToLower();
if (pathLower.EndsWith(".fbx") || pathLower.EndsWith(".3ds") || pathLower.EndsWith(".obj"))
{
GameObject go = AssetDatabase.LoadAssetAtPath<GameObject>(path);
if (go != null)
{
fbxs.Add(path, go);
}
}
}
static void StartDelete()
{
reimportModle = true;
int n = 0;
foreach (var item in fbxs)
{
UpdateProgress(++n, fbxs.Count, item.Key);
AssetDatabase.ImportAsset(item.Key);
}
reimportModle = false;
EditorUtility.ClearProgressBar();
AssetDatabase.Refresh();
Debug.Log("delete default mat over---");
}
static void UpdateProgress(int progress, int progressMax, string desc)
{
string title = "Processing...[" + progress + " - " + progressMax + "]";
float value = (float)progress / (float)progressMax;
EditorUtility.DisplayProgressBar(title, desc, value);
}
}
public class ModelMatTool : AssetPostprocessor
{
private void OnPostprocessModel(GameObject model)
{
if (null == model || !DeletFbxDefaultMat.reimportModle) return;
Renderer[] renders = model.GetComponentsInChildren<Renderer>();
if (null == renders) return;
foreach (Renderer render in renders)
{
render.sharedMaterials = new Material[render.sharedMaterials.Length];
}
}
}
处理后效果如下:
发现没,模型默认的材质球没了,模型这时候是粉色的,不过没关系我们要的只是模型的mesh而已,我们做预制体的时候只要把美术用的材质球赋予给它就行了,重新打包包体总体减少了90M左右,效果还是可以的。这边有几个点需要注意的:
- 模型设置的Import Materials不要打勾
- 上面代码处理的是本地工程Library/metadata里的数据,所以出包的时候要在打包工程上处理。