1:unity 游戏安装包大,运行游戏卡

Unity为什么可以跨平台 因为它内置了mono虚拟机(跨平台) 程序要先在虚拟机运行虚拟机再和操作系统交互   所以包体大运行卡 但是不是一定的原因 (如果没有虚拟机直接游戏运行在操作系统就会快很多)

unity build 安卓卡 unity好卡_加载

2:Draw Call 越大性能消耗越大  Gpu 相关

1:Draw Call 是Cpu对图形绘制接口的调用,Cpu通过调用图形库(directx/opengl)接口,命令Gpu进行渲染操作

2:DrawCall真正的造成性能问题的是cpu 而不是Gpu 是切换渲染状态导致

3:Cpu和Gpu是如何进行工作和交互的 首先采用渲染流水线 cpu和gpu并行工作,独而不相互依赖,这是通过命令缓冲去来实现的  命令缓冲区维护一个命令队列,cpu向其中发送命令,gpu从中提取命令并执行,命令有多种,DrawCall只是其中的一种,其他命令还有改变渲染状态,设置渲染数据流等。这种方式就类似于游戏开发的网络通信:维持一个消息队列,网络线程接收解析消息并将之添加到消息队列,游戏主线程更新时从中取出消息并做派发处理。

4:DrawCall是如何影响性能的 每一次绘制Cpu都要调用DrawCall 而在调用Dw前cpu还要进行很多准备工作:检测渲染状态,提交渲染所需要的数据,提交渲染所需要的状态,但是GPu本身有很强大的计算能力可以很快就处理完渲染任务当Dw过多,cpu就会很多额外开销用于准备工作,cpu本身负载,而这个时候GPU可能闲置了 例如:拷贝1000个总大小1M的文件和单个大小为1M的文件,明显拷贝1000个文件要慢很多,DrawCall调用和这个很类似。

5:Dw优化减少Dw    Dw导致的性能问题是应为dw过多因此要减少dw优化游戏  首先批处理(batching) dw过多会造成cpu性能瓶颈:大量时间消耗在dw准备工作上 优化方向:尽量把小的dw合并到一个大的dw中这个就是 批处理的思想

3:合并批处理的注意事项

1:合并的网格会在一次渲染任务中进行绘制,他们的渲染数据,渲染状态和shader都是一样的,因此合并的条件至少是:同材质,同贴图,同shader.最好网格顶点格式也一致

2:尽量避免使用大量小的网格,当确实需要时,考虑是否要合并

4:避免使用过多材质,尽量共享材质

5:网格合并的顶点数量有上限(unity 中好像是65535)

6:合并本身有消耗,因此尽量在编辑器下进行合并

确实需要运行合并的,将静态的物体和动态的物体分开合并一次就可以,动态的只要物体发生变换就要重新合并

4:Unity Profiler 性能分析工具

Unity Statistisc Game窗口统计面板  Statistics窗口,全称叫做 Rendering Statistics Window,即渲染统计窗口(或渲染数据统计窗口),窗口中罗列出关于声音、图像、网络状况等多种统计信息

FPS 每秒帧数 处理渲染所花费的时间 时间越小帧数越大越流畅  30帧人眼看不出来卡

CPU 计算每帧消耗的时间 越低越好

Render Thead 渲染线程 Gpu渲染图像需要的时间 GPU显卡性能决定 越低越好

Batches 批处理 和DrawCall有关 以DrawCall作为标准  unity内置的Dw批处理技术

Verts 顶点个数 摄像机视野(field of view)内渲染的顶点总数

tris   三角面个数视野内 摄像机视野(field of view)内渲染的的三角面总数量

SetPass Call Shader里面很多 pass方法执行了多少个pass方法

5:Unity在 Player Setting 里的两个功能选项 Static Batching 与 Dynamic Batching。功能描述如下:

Static Batching 是将标明为 Static 的静态物件,如果在使用相同材质球的条件下,Unity 会自动帮你把这两个物件合并成一个 Batch,送往 GPU 来处理。这功能对效能上非常的有帮助,所以是需要付费才有的。

Dynamic Batching 是在物件小于300面的条件下(不论物件是否为静态或动态),在使用相同材质球下,Unity就会自动帮你合合并成一个 Batch 送往 GPU 来处理。

unity build 安卓卡 unity好卡_unity build 安卓卡_02

unity build 安卓卡 unity好卡_加载_03

unity build 安卓卡 unity好卡_批处理_04

unity build 安卓卡 unity好卡_加载_05

尽量不要修改Batches里物体的Scale,因为这样会生成新的Batch

为了提升GPU的渲染效率,应当尽可能的在一个物体上使用较少的材质,减少Batches过多的开销

对于场景中不会运动的物体,考虑设置Static属性,Static声明的物体会自动进行内部批处理优化

6:Unity资源优化

Mesh  

动态模型:面片数< 3000 人物 怪物

材质数 < 3   

骨骼数 < 50

静态模型 顶点数 < 500 场景等   

Audio

长时间的音乐(背景音乐) 压缩格式mp3  减少安装包大小 Compressed In Memory

短时间音乐 (音效) 非压缩格式 wav    Decompress On Load

有三种方式 Load Type

unity build 安卓卡 unity好卡_贴图_06

Decompress On Load

在硬盘上压缩这个文件并在第一次加载到内存的时候解压 试用于较小的压缩声音,避免即时解压的性能开销  用于音效小文件

Compressed In Memory

保持声音在内存中是压缩的并在播放时解压缩 会有更多的cpu开销,但可以提高加载速度并减少内存消耗 适用于大文件

Streaming 选项

直接从磁盘流音频数据。这只使用了原始声音占内存大小的很小一部分。 该方法使用最少的内存和最多的CPU,它有个很明显的缺点就是不能被引用超过一次。试着让 Audio Clip产生多个副本的时候会每个都产生数据缓冲区,如果非要这么做会产生大量的内存和cpu消耗。因此这个选择最好是给单实例的Audio Clip,如背景和环境音效。对于手游而言不要优先考虑使用这种方式。

其他一些加载设置

在场景中把一 个Audio Clip赋值给Audio Source组件,这个音频文件将在场景初始化的时候加载到内存中。但是如果它启用了,加载Audio Clip就变成了一个后台任务它将推迟到场景初始化完成后再加载,换句话说就是在游戏开始后开始加载。 通过启用这个选择我们可以提高场景的启动速度,但Play声音的时候它还在后台加载的话播放将推迟到加载完成,我们可以使用AudioClip对象的loadState 属性来检查是否加载完成以保证声音在不会在一个不恰当的时间播放。

Preload Audio Data 默认是启用的,它表示Unity自动在场景初始化时加载文件。禁用此选项将推迟加载直到 AudioSource的 Play() 或 PlayOneShot() 方法执行的时候。从硬盘加载声音文件,然后解压,再push到内存里然后再播放,这么一系列的动作 可能 会导致 CPU的峰值。

由于播放延迟和性能消耗,不建议在播放的瞬间加载。我们应该控制加载在播放之前的某些方便的时间使用AudioClip对象的LoadAudioData()方法加载。我们也可以用AudioClip对象的UnloadAudioData()方法手动控制声音文件的内存释放。

因为每个Audio Source的播放都会消耗一定量的cpu,所以我们可以控制场景中 Audio Source的数量来节省cpu。

减少AudioClip引用


Texture

贴图长宽  < 1024 必须为2的幂次方   unity 最大贴图支持 2048

本质上来讲它是一个大的颜色数组告诉程序每个像素是什么颜色。

而Sprite是一个2d网格,一个平对着当前摄像机的quad,我习惯读作精灵。

在Texture Type为Texture模式下Unity只给我们看到四个格式选项Compressed, 16-bit,True Color和Crunched

Crunched格式,这个格式压缩需要很长时间,但在运行时减压是非常快。   在新版本的Unity中加入了这个选择可以看到它被压缩成PVRTC格式,推荐在开发手机应用程序时使用 PVRTC 纹理

当使用 PVRTC 时与标准JPEG或PNG图像相比有可能有些图像质量的下降

不建议导入非正方形或者非2的次幂的纹理到我们的应用程序,因为GPU往往需要把纹理是变成正方形和2的次幂导致不必要的处理畸形的纹理尺 寸工作量。如果纹理不是2的次幂,Unity会scale、pad纹理,以使得达到2的次幂,这样会花费更多内存,让加载更慢,所以建议是避免导入非正方形和2的次幂的纹理。

Mip Maps

通过启用 Generate Mip Maps   它会自动产生不同分辨率的多张相同纹理。在运行的时候,GPU根据在透视投影下出现的面积选择适当的Mip Map(这是一种叫做 texel-to-pixel ratio的技术 )

启用Mip Maps还会让最后生成的纹理文件增大33%左右

以下时候应该禁用这个选项:

1:在2d游戏中的几乎所有的纹理(2d游戏正交投影不存在近大远小)

2:UI界面

3:Mesh贴图,Sprites,Particle Effects,因为他们总是在摄像机差不多的距离渲染不会有很明显的距离差

4:打包图集

Shader

尽量减少复杂数学运算 减少discard 操作  耗时的操作

7:模型优化   美术制作方面

1:减少面数,定点数(高低中档模型)

2:贴图优化

3:贴图合并成一个 材质也就是一个了 减少Dw调用

8:如何减少冗余资源和重复资源

  1. Resourse 目录下的资源不管是否被引用,都会打包进安装包不使用的资源不要放在Resourse目录下
  2. 不同目录下的相同资源文件,如果被引用,那么都会打包进资源包,所以保证同一个资源文件在项目中只存放在一个目录位置

9:资源的检测与分析

分析 assetbundle 分析安装包里面的冗余资源

https://www.uwa4d.com/#assetbundle  UWA 分析工具 第三方检测工具

10:渲染优化

Cpu Gpu 分工

11:LOD 层级细节  在不同的距离显示不同精细的模型 占用空间

1:创建个空物体 添加LODGroup 组件  0精细 1一般 2粗略  culled 看不见不显示

unity build 安卓卡 unity好卡_加载_07

 

这个长度站的比例可以调节

unity build 安卓卡 unity好卡_加载_08

在LOD2和LOD1可以修改贴图的品质,Cast Shadow和 ReceiveShadow可以移除  (阴影)

12:Occlusion Culling - 遮挡剔除

只渲染在视野内的物体,视野外的不渲染 场景中很多的物体 如树

1:选择那些物体要进行遮挡剔除

2:选择后要在 Occlusion 里面选择 Object 点击 Bake 烘焙

3:Visualization 里面选择Cameras 选择渲染摄像机(依据哪个Camera进行遮挡剔除的计算)

unity build 安卓卡 unity好卡_unity build 安卓卡_09

unity build 安卓卡 unity好卡_批处理_10

unity build 安卓卡 unity好卡_批处理_11

13:光照贴图 LightMapping

在场景中的物体有很多点光源或者其他环境光源 光照光源计算是很消耗性能的,把这个光照的效果提前计算好,生成一张光照贴图,这样游戏运行时就不用再计算了

unity build 安卓卡 unity好卡_贴图_12

unity build 安卓卡 unity好卡_贴图_13

选择window - Lighting   点击GenreateLighting

unity build 安卓卡 unity好卡_unity build 安卓卡_14

要把需要烘焙的光源设置为baked

unity build 安卓卡 unity好卡_加载_15

工程里面会多一个这样的贴图

unity build 安卓卡 unity好卡_unity build 安卓卡_16

14:Mesh合并  

可以在3dMax 玛雅合并 也可以代码合并  MeshFilter 模型显示的网格  MeshRender 模型显示的渲染材质 shader

1 shader相同的物体才可以合并

 2 要合并的物体必须处于一定的范围内我们在合并之前会将场景划分为多个正方体,每个正方体内的物体才能互相合并在一起,以此避免合并出来物体的mesh分散的情况。

3 物体的光照贴图 lightMapindex 属性相同

//写一个Editor 工具

void GetMesh(Transform ts)
{
       //获取自身和所有子物体中所有MeshFilter组件
       MeshFilter[] meshFilters = ts.GetComponentsInChildren<MeshFilter>(); 
       //新建CombineInstance数组 
        CombineInstance[] combine = new CombineInstance[meshFilters.Length];
//获取自身和所有子物体中所有MeshRenderer组件    
        MeshRenderer[] Renderer = ts.GetComponentsInChildren<MeshRenderer>(); 
 //新建材质球数组
        Material[] mats = new Material[Renderer.Length];                   
        for (int i = 0; i < meshFilters.Length; i++)
        {
 //获取材质球列表
            mats[i] = Renderer[i].sharedMaterial;                          
            combine[i].mesh = meshFilters[i].sharedMesh;
            //矩阵(Matrix)自身空间坐标的点转换成世界空间坐标的点   
            //combines[i].transform = meshFilters[i].transform.localToWorldMatrix;
            //变换矩阵的问题,要保持相对位置不变,要转换为父节点的本地坐标,
            combines[i].transform = ts.worldToLocalMatrix * meshFilters[i].transform.localToWorldMatrix;
//除了根物体,其他物体统统销毁
            if (meshFilters[i].gameObject.name != gameObject.name)      
            {
                Destroy(meshFilters[i].gameObject);
            }
        }

        ts.GetComponent<MeshFilter>().mesh = new Mesh();
/*为mesh.CombineMeshes添加一个 false 参数,表示并不是合并为一个网格,而是一个子网格列表,可以让拥有多个材质球,如果要合并的网格
        用的是同一材质,false改为true,同时将上面的获取Material的代码去掉*/
        ts.GetComponent<MeshFilter>().mesh.CombineMeshes(combine, false);
   //为合并后的GameObject指定材质球数组
        ts.GetComponent<MeshRenderer>().sharedMaterials = mats;       
    }

另外尽量保证根物体的缩放是1:1:1,不然就会发现合并完的模型完全变形了。

15:CPU优化

资源对象池优化

1: 对象池类, (扩展可以分类型获得不同的对象池)

2:对象池大小 (也可以初始化先创建好对象)

3:对象池保存的数据(List,字典等格式)

4: 添加对象方法(清空对象属性方法)

5: 减少对象方法(或者是取出一个对象的方法)

6:初始化或者重置对象属性方法

代码编译优化

放到pluging Editor 文件夹下等