在游戏中的DrawCall越大,游戏性能消耗越大。而在我们2018.4.1中,可以在stats中看到Batches数量 它和DrawCall都是性能消耗显示的参数,当游戏中的模型越多,DrawCall和Batches的数量越大,而优化是降低DrawCall和Batches。
优化方式也分为很多种:
资源优化(模型,声音,贴图)
(GPU)渲染优化:
1.LOD(层级细节)
2.OcclusionCulling遮挡剔除
3.LightMap光照贴图
4.Mesh合并
代码优化
资源优化:
1.模型优化(Mesh):
I.动态模型:面片数<3000、材质数<3、骨骼数<50.
II.静态模型:顶点数<500
(这标准不是唯一标准,视游戏而定)
2.音乐
I.长时间音乐:如背景音乐,建议使用压缩格式.mp3->(LoadType:Compressed In Memory)
II.短时间音乐:建议使用非压缩格式.wav->(LoadType:Decompress On Load)
3.贴图
I.Texture:都去掉alpha通道,作为背景展示的图片,基本都没有透明要求。
II.Loading图这类需要比较精细的,则把图片设置为Automatic TrueColor,设置真彩色,保证不失真
III.地图、缩略图、UI背景图等等要求不精细的,则可以设置为自动压缩格式(有压缩情况,可以在Advance里面设置toNearest)
IIII.图片尽量打包成图集,(图片大小<1024)。
GPU渲染优化
1.LOD层级细节: 当视野离模型比较远的时候用粗糙的模型,当视野离模型最近的时候用精细的模型。
实现方法:在场景中创建一个空物体,然后添加LOD Group组件,依次把最精细到粗糙的模型拉到LOD0-LOD3中.
视野拉近模型显示最精细,视野拉远模型显示最粗糙。
2.遮挡剔除: 只会渲染在视野内的模型,而在视野外的模型则会剔除。
实现方法:选中要遮挡剔除的物体,static ---->Occluder static ,然后打开Window—>Rendering----->OcclusionCulling---->break。会生成一个遮挡剔除的文件:随着摄像机视野的拉近,视野外的物体就被剔除了。
做遮挡剔除之前的Drawcall数量和Batches:
做遮挡剔除后随着摄像机视野拉近DrawCall数量和Batcher依次在减少:
3.光照贴图: 把场景中的大量光源烘焙成贴图。
实现方法:选中被光源照着的物体,static----->LightMap。 选中光源,把Model------->baked。点击Window------>Reading-------->Lighting Settings----->取消勾选 Auto Generate ----->点击 Generate Lighting.把光源删除,发现光照效果还存在,DrawCall和Batches比之前少了很多。
4.Mesh合并:把多个模型的Mesh合并成一个,合并可以在3Dmax中完成也可以在Unity中完成。
实现方法:使用代码进行合并。
// 合并的代码
void Test()
{
MeshFilter[] filters = GetComponentsInChildren<MeshFilter>();
CombineInstance[] combiners = new CombineInstance[filters.Length];
for(int i = 0; i < filters.Length; i++)
{
combiners[i].mesh = filters[i].sharedMesh;
combiners[i].transform = filters[i].transform.localToWorldMatrix;
}
Mesh finalMesh = new Mesh();
finalMesh.CombineMeshes(combiners);
GetComponent<MeshFilter>().sharedMesh = finalMesh;
}
官方提供的动态批处理:
代码优化
对象池:预先生成一部分物体SetActive为false,放到集合或者字典中,使用时设置位置和SetActive为true就行。这里放一下对象池代码:
public class ObjectPool : MonoBehaviour
{
//单例模式
public static ObjectPool instance;
//字典里的String就是坑的名字,每一个坑对应一个GameObject列表
Dictionary<string, List<GameObject>> pool = new Dictionary<string, List<GameObject>>() { };
private void Awake()
{
instance = this;
}
public GameObject GetPool(GameObject go, Vector3 position)
{
string key = go.name + "(Clone)";//要去拿东西的坑名字
GameObject rongqi; //你用来取物体的容器;
//下面分三种情况来分析
if (pool.ContainsKey(key) && pool[key].Count > 0)//如果坑存在,坑里有东西
{
//直接拿走坑里面的第一个
rongqi = pool[key][0];
pool[key].RemoveAt(0);//把第一个位置释放;
}
else if (pool.ContainsKey(key) && pool[key].Count <= 0)//坑存在,坑里没东西
{
//那就直接初始化一个吧
rongqi = Instantiate(go, position, Quaternion.identity) as GameObject;
}
else //没坑
{
//不仅要初始化,还要把坑加上
rongqi = Instantiate(go, position, Quaternion.identity) as GameObject;
pool.Add(key, new List<GameObject>() { });
}
//调整物体初始状态
rongqi.SetActive(true);
//这里加了一个子物体也显示的代码 可以根据自己需要去调整
foreach (Transform child in rongqi.transform)
{
child.gameObject.SetActive(true);
}
//位置初始化
rongqi.transform.position = position;
return rongqi;
}
//放入池子中的方法
public void AddPool(GameObject go)
{
string key = go.name;
pool[key].Add(go);
go.SetActive(false);
}
}
好了就介绍到这里了!