Unity 性能优化(力荐)

开始以前先分享几款性能优化的插件:html

一、SimpleLOD :android

除了一样拥有Mesh Baker所具备的Mesh合并、Atlas烘焙等功能,它还能提供Mesh的简化,并对动态蒙皮网格进行了很好的支持。ios

该插件可在Run-time和Editor中均可以使用,同时开放了源码,你们能够根据项目的实际状况而做修改。web

http://download.csdn.net/download/jasonczy/10178526算法

 

1、编程

转载自 http://blog.csdn.net/game_jqd/article/details/51899000数组

使用Profiler工具分析内存占用状况
  • System.ExecutableAndDlls:系统可执行程序和DLL,是只读的内存,用来执行全部的脚本和DLL引用。不一样平台和不一样硬件获得的值会不同,能够经过修改Player Setting的Stripping Level来调节大小。

 

Ricky:我试着修改了一下Stripping Level彷佛没什么改变,感受虽占用内存大但不会影响游戏运行。咱们暂时忽略它吧(- -)!缓存

 

  • GfxClientDevice:GFX(图形加速\图形加速器\显卡 (GraphicsForce Express))客户端设备。

 

Ricky:虽占用较大内存,但这也是必备项,没办法优化。继续忽略吧(- -)!!安全

 

  • ManagedHeap.UsedSize:托管堆使用大小。

 

Ricky:重点监控对象,不要让它超过20MB,不然可能会有性能问题!性能优化

 

  • ShaderLab:Unity自带的着色器语言工具相关资源。

 

Ricky:这个东西你们都比较熟悉了,忽略它吧。

 

  • SerializedFile:序列化文件,把显示中的Prefab、Atlas和metadata等资源加载进内存。

 

Ricky:重点监控对象,这里就是你要监控的哪些预设在序列化中在内存中占用大小,根据需求进行优化。

 

 

  • PersistentManager.Remapper:持久化数据重映射管理相关

 

Ricky:与持久化数据相关,好比AssetBundle之类的。注意监控相关的文件。

 

  • ManagedHeap.ReservedUnusedSize:托管堆预留不使用内存大小,只由Mono使用。

 

Ricky:没法优化。

  1. 许多贴图采用的Format格式是ARGB 32 bit因此保真度很高但占用的内存也很大。在不失真的前提下,适当压缩贴图,使用ARGB 16 bit就会减小一倍,若是继续Android采用RGBA Compressed ETC2 8 bits(iOS采用RGBA Compressed PVRTC 4 bits),又能够再减小一倍。把不须要透贴但有alpha通道的贴图,全都转换格式Android:RGB Compressed ETC 4 bits,iOS:RGB Compressed PVRTC 4 bits。
  2. 当加载一个新的Prefab或贴图,不及时回收,它就会永驻在内存中,就算切换场景也不会销毁。应该肯定物体再也不使用或长时间不使用就先把物体制空(null),而后调用Resources.UnloadUnusedAssets(),才能真正释放内存。
  3. 有大量空白的图集贴图,能够用TexturePacker等工具进行优化或考虑合并到其余图集中。

 

 

  • AudioManager:音频管理器

 

Ricky:随着音频文件的增多而增大。

 

  • AudioClip:音效及声音文件

 

Ricky:重点优化对象,播放时长较长的音乐文件须要进行压缩成.mp3或.ogg格式,时长较短的音效文件能够使用.wav 或.aiff格式。

 

  • Cubemap:立方图纹理

 

Ricky:这个通常在天空盒中比较常见,我也不知道如何优化这个。。。

 

  • Mesh:模型网格

 

Ricky:主要检查是否有重复的资源,还有尽可能减小点面数。

  • Mesh:场景中使用的网格模型

 

Ricky:注意网格模型的点面数,能合并的mesh尽可能合并。

1)ManagedHeap.UsedSize: 移动游戏建议不要超过20MB.

2)SerializedFile: 经过异步加载(LoadFromCache、WWW等)的时候留下的序列化文件,可监视是否被卸载.

3)WebStream: 经过异步WWW下载的资源文件在内存中的解压版本,比SerializedFile大几倍或几十倍,不过咱们如今项目中展现没有。

4)Texture2D: 重点检查是否有重复资源和超大Memory是否须要压缩等.

5)AnimationClip: 重点检查是否有重复资源.

6)Mesh: 重点检查是否有重复资源.

1.Device.Present:

1)GPU的presentdevice确实很是耗时,通常出如今使用了很是复杂的shader.

2)GPU运行的很是快,而因为Vsync的缘由,使得它须要等待较长的时间.

3)一样是Vsync的缘由,但其余线程很是耗时,因此致使该等待时间很长,好比:过量AssetBundle加载时容易出现该问题.

4)Shader.CreateGPUProgram:Shader在runtime阶段(非预加载)会出现卡顿(华为K3V2芯片).

5)StackTraceUtility.PostprocessStacktrace()和StackTraceUtility.ExtractStackTrace(): 通常是由Debug.Log或相似API形成,游戏发布后需将Debug API进行屏蔽。

2.Overhead:

1)通常状况为Vsync所致.

2)一般出如今Android设备上.

3.GC.Collect:

缘由:

1)代码分配内存过量(恶性的)

2)必定时间间隔由系统调用(良性的).

占用时间:

1)与现有Garbage size相关

2)与剩余内存使用颗粒相关(好比场景物件过多,利用率低的状况下,GC释放后须要作内存重排)

4.GarbageCollectAssetsProfile:

1)引擎在执行UnloadUnusedAssets操做(该操做是比较耗时的,建议在切场景的时候进行)。

2)尽量地避免使用Unity内建GUI,避免GUI.Repaint过渡GCAllow.

3)if(other.tag == a.tag)改成other.CompareTag(a.tag).由于other.tag为产生180B的GC Allow.

4)少用foreach,由于每次foreach为产生一个enumerator(约16B的内存分配),尽可能改成for.

5)Lambda表达式,使用不当会产生内存泄漏.

5.尽可能少用LINQ:

1)部分功能没法在某些平台使用.

2)会分配大量GC Allow.

6.控制StartCoroutine的次数:

1)开启一个Coroutine(协程),至少分配37B的内存.

2)Coroutine类的实例 -> 21B.

3)Enumerator -> 16B.

7.使用StringBuilder替代字符串直接链接.

8.缓存组件:

1)每次GetComponent均会分配必定的GC Allow.

2)每次Object.name都会分配39B的堆内存.

 
 
 
.框架设计层面。
 
一个相对中大型的游戏,系统很是的多。这时候合理的适时的释放内存有助于游戏的正常体验,甚至能够防止内存快速到达峰值,致使设备Crash。
目前主流平台机型可用内存:
Android平台:在客户端最低配置以上,均需知足如下内存消耗指标(PSS):
1)内存1G如下机型:最高PSS<=150MB
2)内存2G的机型:最高PSS<=200MB
iOS平台:在iPhone4S下运行,消耗内存(real mem)不大于150MB

 

1.场景切换时避开峰值。
当前一个场景还未释放的时候,切换到新的场景。这时候因为两个内存叠加很容易达到内存峰值。解决方案是,在屏幕中间遮盖一个Loading场景。在旧的释放完,而且新的初始化结束后,隐藏Loading场景,使之有效的避开内存大量叠加超过峰值。

 

2.GUI模块加入生命周期管理。

 

主角、强化、技能、商城、进化、背包、任务等等。一般一个游戏都少不了这些系统。但要是所有都打开,或者这个时候再点世界地图,外加一些逻辑数据内存的占用等等。你会发现,内存也很快就达到峰值。
这时候有效的管理系统模块生命周期就很是有必要。首先将模块进行划分:

 

1)常常打开 Cache_10;
2)偶尔打开 Cache_5;
3)只打开一次 Cache_0。

 

建立一个ModuleMananger 类,内部Render方法每分钟轮询一次。若是是“Cache_0”这个类型,一关闭就直接Destroy释放内存;“Cache_10”这个类型为10分钟后自动释放内存;" Cache_5"这种类型为5分钟后自动释放内存。每次打开模块,该模块就会从新计时。这样就能够有效合理的分配内存。

 

 

 

一、  因为实时对战游戏的数据包数量巨大,早期版本的帧同步策略会致使比较明显的卡顿,经过进行数据包的合并与优化逐渐解决了卡顿问题;

二、  频繁建立和销毁的小兵对象让CPU爆表了,大量的小兵若是采用实时内存的分配和回收,会产生大量的内存碎片和系统开销,解决方法之一就是采用高效的对象池进行优化,对每一个内存对象的状态进行操做便可;

三、  性能分析过程当中,发现单人同屏和多人同屏时的开销都很大,经过视野裁剪技术,使得玩家视野外的没必要要的特效和渲染能够所有关闭,极大下降了CPU、GPU和内存的开销;

四、  在高中低三档机型上玩游戏时,分别加载不一样层次的特效包,这也有助于下降CPU和内存的开销;性能分析过程当中发现副本内wwise音频组件占了30%的CPU时间,果断抛弃之,采用Unity自带音频功能,优化很明显;

五、  游戏内界面采用了UGUI的方式实现,但大量的实时UI变化使得副本内每帧会有230以上的drawcall,致使中低端机型感觉到明显卡顿,最终采用UGUI+自研究UI的组合拳,重写了一套紧密结合游戏自身特性的UI来实现战斗血条和浮动文字的效果。

六、    资源使用总量是否在合理范围以内。

七、   一个场景内的资源重复率。

八、   资源对象拷贝的数量是否合理。

九、  场景切换时保留的资源详情。

十、             网格、纹理、音频、动画、GameObject等资源是否超标。

 

十一、             贴图:

十二、             l  控制贴图大小,尽可能不要超过 1024x1024;

1三、             l  尽可能使用2的n次幂大小的贴图,不然GfxDriver里会有2份贴图;

1四、             l  尽可能使用压缩格式减少贴图大小;

1五、             l  若干种贴图合并技术;

1六、             l  去除多余的alpha通道;

1七、             l  不一样设备使用不一样的纹理贴图,分层显示;

1八、              

1九、             模型:

20、             l  尽可能控制模型的面数,小于1500会比较合适;

2一、             l  不一样设备使用不一样的模型面数;

2二、             l  尽可能保持在30根骨骼内;

2三、             l  一个网格不要超过3个material;

2四、             动画:

2五、             l  N种动画压缩方法;

2六、             l  尽可能减小骨骼数量;

2七、             声音:

2八、             l  采用压缩MP3 和 wav;

2九、             资源方面的优化:

30、             l  使用 Resource.Load 方法在须要的时候再读取资源;

3一、             l  各类资源在使用完成后,尽快用Resource.UnloadAsset和UnloadUnusedAsset卸载掉;

3二、             l  灵活运用AssetBundle的Load和Unload方法动态加载资源,避免主要场景内的初始化内存占用太高;(实现起来真的很难…)

3三、             l  采用www加载了AssetBundle后,要用www.Dispose 及时释放;

3四、             l  在关卡内谨慎使用DontDestroyOnLoad,被标注的资源会常驻内存;

3五、             代码的优化:

3六、             l  尽可能避免代码中的任何字符串链接,由于这会给GC带来太多垃圾;

3七、             l  用简单的“for”循环代替“foreach”循环;

3八、             l  为全部游戏内的动态物体使用内存对象池,能够减小系统开销和内存碎片,复用对象实例,构建本身的内存管理模式,减小Instantiate和Destory;

3九、             l  尽可能不使用LINQ命令,由于它们通常会分配中间缓器,而这很容易生成垃圾内存;

40、             l  将引用本地缓存到元件中会减小每次在一个游戏对象中使用 “GetComponent” 获取一个元件引用的需求;

4一、             l  减小角色控制器移动命令的调用。移动角色控制器会同步发生,每次调用都会耗损较大的性能;

4二、             l  最小化碰撞检测请求(例如raycasts和sphere checks),尽可能从每次检查中得到更多信息;

4三、             l  AI逻辑一般会生成大量物理查询,建议让AI更新循环设置低于图像更新循环,以减小CPU负荷;

4四、             l  要尽可能减小Unity回调函数,哪怕是空函数也不要留着;(例如空的Update、FixedUpdate函数)

4五、             l  尽可能少使用FindObjectsOfType函数,这个函数很是慢,尽可能少用且必定不要在Update里调用;

4六、             l  千万必定要控制mono堆内存的大小;

4七、              

4八、             unity3D 对于移动平台的支持无可厚非,可是也有时候用Unity3D 开发出来的应用、游戏在移动终端上的运行有着明显的效率问题,好比卡、画质等各类问题。本身在作游戏开发的时候偶有所得。对于主要影响性能的因素作个总结。

4九、             

50、             主要因素有:

5一、                     1.      Savedby batching 值过大   ---- > 这个值主要是针对Mesh的批处理,这个值越高,应用就越卡   

5二、                     2.     Drawcall值过大 ---- >  Drawcall 值过大,所须要的 GPU 的处理性能较高,从而致使CPU的计算时间过长,因而就卡了

5三、                     3.     点、面过多           ----> 点、面过多,GPU 根据不一样面的效果展开计算,而且CPU计算的数据也多,因此效果出来了,可是卡巴斯基

5四、             因为 Saved by batching 和 Drawcall 值过大所引发的卡的问题我所作的优化方式有:

5五、                     1.    对于模型 :Mesh 合并,有个不错的插件(DrawCallMinimizer   --->  直接上AssetStore 下载便可,免费的,并且有文档,很容易上手)

5六、                     2.    对于UI  :  尽可能避免使用Unity3D自带的 GUI 换用 NGUI或者EZGUI;由于这两个UI插件对于UI中的图片处理是将UI图片放置在一个 Atlas中,一个 Atlas 对应一个Drawcall

5七、                     3.   对于灯光: 能够使用 Unity3D 自带的  Lightmapping插件来烘焙场景中的灯光效果到物体材质上 

5八、                     4.  对于场景: 能够使用 Unity3D 自带的 OcclusionCulling 插件把静止不动的场景元素烘焙出来

5九、                     4.   对于特效:尽可能把材质纹理合并

60、             对于Unity3D 在移动终端上支持的Drawcall 数到底多少,主要是跟机子性能有关的,固然也不是说值小性能就必定没问题(本人亲测,也有17就卡的,主要是模型材质纹理过大所引发的),目前我作的是70左右的,还OK,挺正常的

6一、              

6二、             因为点、面过多所致使的性能问题,最好用简模,用四面体来作复杂的模型,可是面、点也别太多,至于Unity3D 到底支持多少点、面的说法各异,我也搞不懂,总之少些确定OK

6三、              

6四、              

6五、              

6六、             检测方式:

6七、             一,Unity3D 渲染统计窗口

6八、             Game视窗的Stats去查看渲染统计的信息:

6九、             一、FPS

70、             fps其实就是 framesper second,也就是每一秒游戏执行的帧数,这个数值越小,说明游戏越卡。

7一、              

7二、             二、Draw calls

7三、             batching以后渲染mesh的数量,和当前渲染到的网格的材质球数量有关。

7四、              

7五、             三、Saved by batching 

7六、             渲染的批处理数量,这是引擎将多个对象的绘制进行合并从而减小GPU的开销;

7七、             不少GUI插件的一个好处就是合并多个对象的渲染,从而下降DrawCalls ,保证游戏帧数。

7八、              

7九、             四、Tris 当前绘制的三角面数

80、              

8一、             五、Verts 当前绘制的顶点数

8二、              

8三、             六、Used Textures 当前帧用于渲染的图片占用内存大小

8四、              

8五、             七、Render Textures 渲染的图片占用内存大小,也就是固然渲染的物体的材质上的纹理总内存占用

8六、              

8七、             八、VRAM usage 显存的使用状况,VRAM总大小取决于你的显卡的显存

8八、              

8九、             九、VBO Total 渲染过程当中上载到图形卡的网格的数量,这里注意一点就是缩放的物体可能须要额外的开销。

90、              

9一、             十、VisibleSkinned Meshes 蒙皮网格的渲染数量

9二、              

9三、             十一、Animations 播放动画的数量

9四、             注意事项:

9五、             1,运行时尽可能减小 Tris 和 Draw Calls

9六、             预览的时候,可点开 Stats,查看图形渲染的开销状况。特别注意 Tris 和 Draw Calls 这两个参数。

9七、             通常来讲,要作到:

9八、             Tris 保持在 7.5k 如下,有待考证。

9九、             Draw Calls 保持在 20 如下,有待考证。

100、          2,FPS,每一秒游戏执行的帧数,这个数值越小,说明游戏越卡。

10一、          3,Render Textures 渲染的图片占用内存大小。

10二、          4,VRAM usage 显存的使用状况,VRAM总大小取决于你的显卡的显存。

10三、           

10四、          二,代码优化

10五、          1. 尽可能避免每帧处理

10六、          好比:

10七、          function Update() {DoSomeThing(); }

10八、          可改成每5帧处理一次:

10九、          function Update() { if(Time.frameCount% 5 == 0) { DoSomeThing(); } }

1十、          2. 定时重复处理用InvokeRepeating 函数实现

1十一、          好比,启动0.5秒后每隔1秒执行一次 DoSomeThing 函数:

1十二、           

11三、          function Start() {InvokeRepeating("DoSomeThing", 0.5, 1.0); }

11四、           

11五、          3. 优化 Update,FixedUpdate, LateUpdate 等每帧处理的函数

11六、          函数里面的变量尽可能在头部声明。

11七、          好比:

11八、          function Update() { var pos:Vector3 = transform.position; }

11九、          可改成

120、          private var pos: Vector3;function Update(){ pos = transform.position; }

12一、           

12二、          4. 主动回收垃圾

12三、          给某个 GameObject 绑上如下的代码:

12四、          function Update() {if(Time.frameCount % 50 == 0) { System.GC.Collect(); } }

12五、           

12六、          5. 优化数学计算

12七、          好比,若是能够避免使用浮点型(float),尽可能使用整形(int),尽可能少用复杂的数学函数好比 Sin 和 Cos 等等

12八、           

12九、          6,减小固定增量时间

130、          将固定增量时间值设定在0.04-0.067区间(即,每秒15-25帧)。您能够经过Edit->Project Settings->Time来改变这个值。这样作下降了FixedUpdate函数被调用的频率以及物理引擎执行碰撞检测与刚体更新的频率。若是您使用了较低的固定增量时间,而且在主角身上使用了刚体部件,那么您能够启用插值办法来平滑刚体组件。

13一、          7,减小GetComponent的调用

13二、          使用 GetComponent或内置组件访问器会产生明显的开销。您能够经过一次获取组件的引用来避免开销,并将该引用分配给一个变量(有时称为"缓存"的引用)。例如,若是您使用以下的代码:

13三、          function Update () {

13四、          transform.Translate(0, 1, 0);

13五、           

13六、          }

13七、          经过下面的更改您将得到更好的性能:

13八、           

13九、          var myTransform : Transform;

140、          function Awake () {

14一、          myTransform = transform;

14二、          }

14三、          function Update () {

14四、          myTransform.Translate(0, 1, 0);

14五、          }

14六、           

14七、          8,避免分配内存

14八、          您应该避免分配新对象,除非你真的须要,由于他们再也不在使用时,会增长垃圾回收系统的开销。您能够常常重复使用数组和其余对象,而不是分配新的数组或对象。这样作好处则是尽可能减小垃圾的回收工做。同时,在某些可能的状况下,您也能够使用结构(struct)来代替类(class)。这是由于,结构变量主要存放在栈区而非堆区。由于栈的分配较快,而且不调用垃圾回收操做,因此当结构变量比较小时能够提高程序的运行性能。可是当结构体较大时,虽然它仍可避免分配/回收的开销,而它因为"传值"操做也会致使单独的开销,实际上它可能比等效对象类的效率还要低。

14九、           

150、          9,使用iOS脚本调用优化功能

15一、          UnityEngine 命名空间中的函数的大多数是在 C/c + +中实现的。从Mono的脚本调用 C/C++函数也存在着必定的性能开销。您能够使用iOS脚本调用优化功能(菜单:Edit->Project Settings->Player)让每帧节省1-4毫秒。此设置的选项有:

15二、          Slow and Safe – Mono内部默认的处理异常的调用

15三、           

15四、          Fast and Exceptions Unsupported–一个快速执行的Mono内部调用。不过,它并不支持异常,所以应谨慎使用。它对于不须要显式地处理异常(也不须要对异常进行处理)的应用程序来讲,是一个理想的候选项。

15五、           

15六、          10,

15七、          优化垃圾回收

15八、           

15九、          如上文所述,您应该尽可能避免分配操做。可是,考虑到它们是不能彻底杜绝的,因此咱们提供两种方法来让您尽可能减小它们在游戏运行时的使用:

160、          若是堆比较小,则进行快速而频繁的垃圾回收

16一、          这一策略比较适合运行时间较长的游戏,其中帧率是否平滑过渡是主要的考虑因素。像这样的游戏一般会频繁地分配小块内存,但这些小块内存只是暂时地被使用。若是在iOS系统上使用该策略,那么一个典型的堆大小是大约 200 KB,这样在iPhone 3G设备上,垃圾回收操做将耗时大约 5毫秒。若是堆大小增长到1 MB时,该回收操做将耗时大约 7ms。所以,在普通帧的间隔期进行垃圾回收有时候是一个不错的选择。一般,这种作法会让回收操做执行的更加频繁(有些回收操做并非严格必须进行的),但它们能够快速处理而且对游戏的影响很小:

16二、          if (Time.frameCount % 30 == 0)

16三、          {

16四、          System.GC.Collect();

16五、          }

16六、           

16七、          可是,您应该当心地使用这种技术,而且经过检查Profiler来确保这种操做确实能够下降您游戏的垃圾回收时间

16八、          若是堆比较大,则进行缓慢且不频繁的垃圾回收

16九、          这一策略适合于那些内存分配 (和回收)相对不频繁,而且能够在游戏停顿期间进行处理的游戏。若是堆足够大,但尚未大到被系统关掉的话,这种方法是比较适用的。可是,Mono运行时会尽量地避免堆的自动扩大。所以,您须要经过在启动过程当中预分配一些空间来手动扩展堆(ie,你实例化一个纯粹影响内存管理器分配的"无用"对象):

170、           

17一、          function Start() {

17二、           

17三、          var tmp = newSystem.Object[1024];

17四、           

17五、          // make allocations in smallerblocks to avoid them to be treated in a special way, which is designed forlarge blocks

17六、           

17七、          for (var i : int = 0; i <1024; i++)

17八、           

17九、          tmp[i] = new byte[1024];

180、           

18一、          // release reference

18二、           

18三、          tmp = null;

18四、           

18五、          }

18六、           

18七、          游戏中的暂停是用来对堆内存进行回收,而一个足够大的堆应该不会在游戏的暂停与暂停之间被彻底占满。因此,当这种游戏暂停发生时,您能够显式请求一次垃圾回收:

18八、           

18九、          System.GC.Collect();

190、           

19一、          另外,您应该谨慎地使用这一策略并时刻关注Profiler的统计结果,而不是假定它已经达到了您想要的效果。

19二、           

19三、          三,模型

19四、          1,压缩 Mesh

19五、          导入 3D 模型以后,在不影响显示效果的前提下,最好打开 Mesh Compression。

19六、          Off, Low, Medium, High 这几个选项,可酌情选取。

19七、          2,避免大量使用 Unity 自带的 Sphere 等内建 Mesh

19八、          Unity 内建的 Mesh,多边形的数量比较大,若是物体不要求特别圆滑,可导入其余的简单3D模型代替。

 

19九、           

200、          1不是每一个主流手机都支持的技术(就是若是能够不用就不用或有备选方案)

20一、          屏幕特效

20二、          动态的pixel光照计算(如法线)

20三、          实时的阴影

20四、           

20五、          2优化建议

20六、          2.1渲染

20七、          1.不使用或少使用动态光照,使用light mapping和light probes(光照探头)

20八、          2.不使用法线贴图(或者只在主角身上使用),静态物体尽可能将法线渲染到贴图

20九、          3.不适用稠密的粒子,尽可能使用UV动画

2十、          4.不使用fog,使用渐变的面片(参考shadowgun)

2十一、          5.不要使用alpha–test(如那些cutout shader),使用alpha-blend代替

2十二、          6.使用尽可能少的material,使用尽可能少的pass和render次数,如反射、阴影这些操做

21三、          7.若有必要,使用Per-LayerCull Distances,Camera.layerCullDistances

21四、          8.只使用mobile组里面的那些预置shader

21五、          9.使用occlusionculling

21六、          11.远处的物体绘制在skybox上

21七、          12.使用drawcallbatching:

21八、                  对于相邻动态物体:若是使用相同的shader,将texture合并

21九、                  对于静态物体,batching要求很高,详见Unity Manual>Advanced>Optimizing Graphics Performance>Draw Call Batching

220、           

22一、          规格上限

22二、          1.      每一个模型只使用一个skinnedmesh renderer

22三、          2.      每一个mesh不要超过3个material

22四、          3.      骨骼数量不要超过30

22五、          4.      面数在1500之内将获得好的效率

22六、          2.2物理

22七、          1.真实的物理(刚体)很消耗,不要轻易使用,尽可能使用本身的代码模仿假的物理

22八、          2.对于投射物不要使用真实物理的碰撞和刚体,用本身的代码处理

22九、          3.不要使用meshcollider

230、          4.在edit->projectsetting->time中调大FixedTimestep(真实物理的帧率)来减小cpu损耗

23一、          2.3脚本编写

23二、          1.尽可能不要动态的instantiate和destroyobject,使用object pool

23三、          2.尽可能不要再update函数中作复杂计算,若有须要,能够隔N帧计算一次

23四、          3.不要动态的产生字符串,如Debug.Log("boo"+ "hoo"),尽可能预先建立好这些字符串资源

23五、          4.cache一些东西,在update里面尽可能避免search,如GameObject.FindWithTag("")、GetComponent这样的调用,能够在start中预先存起来

23六、          5.尽可能减小函数调用栈,用x= (x > 0 ? x : -x);代替x = Mathf.Abs(x)

23七、          6.下面的代码是几个gc“噩梦”

23八、           String的相加操做,会频繁申请内存并释放,致使gc频繁,使用System.Text.StringBuilder代替

23九、             functionConcatExample(intArray: int[]) {

240、                         varline = intArray[0].ToString();

24一、           

24二、                         for(i = 1; i < intArray.Length; i++) {

24三、                                         line+= ", " + intArray[i].ToString();

24四、                         }

24五、           

24六、                         returnline;

24七、          }

24八、          在函数中动态new array,最好将一个array、传进函数里修改

24九、          functionRandomList(numElements: int) {

250、                    varresult = new float[numElements];

25一、           

25二、                    for(i = 0; i < numElements; i++) {

25三、                                   result[i]= Random.value;

25四、                    }

25五、           

25六、                    returnresult;

25七、          }

25八、           

25九、          2.4 shader编写

260、          1.数据类型

26一、           fixed / lowp -for colors, lighting information and normals,

26二、          half / mediump -for texture UV coordinates,

26三、          float / highp -avoid in pixel shaders, fine to use in vertex shader for position calculations.

26四、          2.少使用的函数:pow,sin,cos等

26五、          2.4 GUI

26六、          1.不要使用内置的onGUii函数处理gui,使用其余方案,如NGUI

26七、           

26八、          3.格式

26九、          1.贴图压缩格式:ios上尽可能使用PVRTC,android上使用ETC

 

 

270、          最简单的优化建议:

1.PC平台的话保持场景中显示的顶点数少于200K~3M,移动设备的话少于10W,一切取决于你的目标GPU与CPU。
2.若是你用U3D自带的SHADER,在表现不差的状况下选择Mobile或Unlit目录下的。它们更高效。
3.尽量共用材质。
4.将不须要移动的物体设为Static,让引擎能够进行其批处理。
5.尽量不用灯光。
6.动态灯光更加不要了。
7.尝试用压缩贴图格式,或用16位代替32位。
8.若是不须要别用雾效(fog)
9.尝试用OcclusionCulling,在房间过道多遮挡物体多的场景很是有用。若不当反而会增长负担。
10.用天空盒去“褪去”远处的物体。
11.shader中用贴图混合的方式去代替多重通道计算。
12.shader中注意float/half/fixed的使用。
13.shader中不要用复杂的计算pow,sin,cos,tan,log等。
14.shader中越少Fragment越好。
15.注意是否有多余的动画脚本,模型自动导入到U3D会有动画脚本,大量的话会严重影响消耗CPU计算。
16.注意碰撞体的碰撞层,没必要要的碰撞检测请舍去。


1.为何须要针对CPU(中央处理器)与GPU(图形处理器)优化?

CPU和GPU都有各自的计算和传输瓶颈,不一样的CPU或GPU他们的性能都不同,因此你的游戏须要为你目标用户的CPU与GPU能力进行针对开发。


2.CPU与GPU的限制

GPU通常具备填充率(Fillrate)和内存带宽(Memory Bandwidth)的限制,若是你的游戏在低质量表现的状况下会快不少,那么,你极可能须要限制你在GPU的填充率。

CPU通常被所须要渲染物体的个数限制,CPU给GPU发送渲染物体命令叫作DrawCalls。通常来讲DrawCalls数量是须要控制的,在能表现效果的前提下越少越好。一般来讲,电脑平台上DrawCalls几千个以内,移动平台上DrawCalls几百个以内。这样就差很少了。固然以上并非绝对的,仅做一个参考。

每每渲染(Rendering)并非一个问题,不管是在GPU和CPU上。极可能是你的脚本代码效率的问题,用Profiler查看下。

关于Profiler介绍:http://docs.unity3d.com/Documentation/Manual/Profiler.html

须要注意的是:
在GPU中显示的RenderTexture.SetActive()占用率很高,是由于你同时打开了编辑窗口的缘由,而不是U3D的BUG。

3.关于顶点数量和顶点计算

CPU和GPU对顶点的计算处理都不少。GPU中渲染的顶点数取决于GPU性能和SHADER的复杂程度,通常来讲,每帧以内,在PC上几百万顶点内,在移动平台上不超过10万顶点。

CPU中的计算主要是在蒙皮骨骼计算,布料模拟,顶点动画,粒子模拟等。GPU则在各类顶点变换、光照、贴图混合等。

【我的认为,具体仍是看各位的项目需求,假设你项目的是3d游戏。你游戏须要兼容低配置的硬件、流畅运行、控制硬件发热的话,还要达到必定效果(LIGHTMAP+雾效),那么顶点数一定不能高。此时同屏2W顶点我认为是个比较合适的数目,DRAWCALL最好低于70。另,控制发热请控制最高上限的帧率,流畅的话,帧率其实不须要过高的。】



4.针对CPU的优化——减小DRAW CALL 的数量

为了渲染物体到显示器上,CPU须要作一些工做,如区分哪一个东西须要渲染、区分开物体是否受光照影响、使用哪一个SHADER而且为SHADER传参、发送绘图命令告诉显示驱动,而后发送命令告诉显卡删除等这些。

假设你有一个上千三角面的模型却用上千个三角型模型来代替,在GPU上花费是差很少的,可是在CPU上则是极其不同,消耗会大不少不少。为了让CPU更少的工做,须要减小可见物的数目:

a.合并相近的模型,手动在模型编辑器中合并或者使用UNITY的Draw call批处理达到相同效果(Draw call batching)。具体方法和注意事项查看如下连接:

Draw call batching :http://docs.unity3d.com/Documentation/Manual/DrawCallBatching.html


b.在项目中使用更少的材质(material),将几个分开的贴图合成一个较大的图集等方式处理。

若是你须要经过脚原本控制单个材质属性,须要注意改变Renderer.material将会形成一份材质的拷贝。所以,你应该使用Renderer.sharedMaterial来保证材质的共享状态。

有一个合并模型材质不错的插件叫Mesh Baker,你们能够考虑试下。

c.尽可能少用一些渲染步骤,例如reflections,shadows,per-pixel light 等。

d.Draw call batching的合并物体,会使每一个物体(合并后的物体)至少有几百个三角面。

假设合并的两个物体(手动合并)但不共享材质,不会有性能表现上的提高。多材质的物体至关于两个物体不用一个贴图。因此,为了提高CPU的性能,你应该确保这些物体使用一样的贴图。

另外,用灯光将会取消(break)引擎的DRAW CALL BATCH,至于为何,查看如下:

Forward Rendering Path Details:
http://docs.unity3d.com/Documentation/Components/RenderTech-ForwardRendering.html

e.使用相关剔除数量直接减小Draw Call数量,下文有相关说起。


5.优化几何模型

最基本的两个优化准则:
a.不要有没必要要的三角面。
b.UV贴图中的接缝和硬边越少越好。

须要注意的是,图形硬件须要处理顶点数并跟硬件报告说的并不同。不是硬件说能渲染几个点就是几个点。模型处理应用通展现的是几何顶点数量。例如,一个由一些不一样顶点构成的模型。在显卡中,一些集合顶点将会被分离(split)成两个或者更多逻辑顶点用做渲染。若是有法线、UV坐标、顶点色的话,这个顶点必须会被分离。因此在游戏中处理的实际数量显然要多不少。


6.关于光照

若不用光确定是最快的。移动端优化能够采用用光照贴图(Lightmapping)去烘培一个静态的贴图,以代替每次的光照计算,在U3D中只须要很是短的时间则能生成。这个方法能大大提升效率,并且有着更好的表现效果(平滑过渡处理,还有附加阴影等)。

在移动设备上和低端电脑上尽可能不要在场景中用真光,用光照贴图。这个方法大大节省了CPU和GPU的计算,CPU获得了更少的DRAWCALL,GPU则须要更少顶点处理和像素栅格化。

Lightmapping : http://docs.unity3d.com/Documentation/Manual/Lightmapping.html


7.对GPU的优化——图片压缩和多重纹理格式

Compressed Textures(图片压缩):

http://docs.unity3d.com/Documentation/Components/class-Texture2D.html

图片压缩将下降你的图片大小(更快地加载更小的内存跨度(footprint)),并且大大提升渲染表现。压缩贴图比起未压缩的32位RGBA贴图占用内存带宽少得多。

以前U3D会议还据说过一个优化,贴图尽可能都用一个大小的格式(512 * 512 , 1024 * 1024),这样在内存之中能获得更好的排序,而不会有内存之间空隙。这个是否真假没获得过测试。

MIPMAps(多重纹理格式):

http://docs.unity3d.com/Documentation/Components/class-Texture2D.html

跟网页上的略缩图原理同样,在3D游戏中咱们为游戏的贴图生成多重纹理贴图,远处显示较小的物体用小的贴图,显示比较大的物体用精细的贴图。这样能更加有效的减小传输给GPU中的数据。


8.LOD 、 Per-Layer Cull Distances 、 Occlusion Culling

LOD (Level Of Detail) 是很经常使用的3D游戏技术了,其功能理解起来则是至关于多重纹理贴图。在以在屏幕中显示模型大小的比例来判断使用高或低层次的模型来减小对GPU的传输数据,和减小GPU所须要的顶点计算。

摄像机分层距离剔除(Per-Layer Cull Distances):为小物体标识层次,而后根据其距离主摄像机的距离判断是否须要显示。

遮挡剔除(Occlusion Culling)其实就是当某个物体在摄像机前被另一个物体彻底挡住的状况,挡住就不发送给GPU渲染,从而直接下降DRAW CALL。不过有些时候在CPU中计算其是否被挡住则会很耗计算,反而得不偿失。

如下是这几个优化技术的相关使用和介绍:

Level Of Detail :
http://docs.unity3d.com/Documentation/Manual/LevelOfDetail.html

Per-Layer Cull Distances :
http://docs.unity3d.com/Documentation/ScriptReference/Camera-layerCullDistances.html

Occlusion Culling :
http://docs.unity3d.com/Documentation/Manual/OcclusionCulling.html


9.关于Realtime Shadows(实时阴影)

实时阴影技术很是棒,但消耗大量计算。为GPU和CPU都带来了昂贵的负担,细节的话参考下面:

http://docs.unity3d.com/Documentation/Manual/Shadows.html


10.对GPU优化:采用高效的shader

a.须要注意的是有些(built-in)Shader是有mobile版本的,这些大大提升了顶点处理的性能。固然也会有一些限制。

b.本身写的shader请注意复杂操做符计算,相似pow,exp,log,cos,sin,tan等都是很耗时的计算,最多只用一次在每一个像素点的计算。不推荐你本身写normalize,dot,inversesqart操做符,内置的确定比你写的好。

c.须要警醒的是alpha test,这个很是耗时。

d.浮点类型运算:精度越低的浮点计算越快。

在CG/HLSL中--

float :32位浮点格式,适合顶点变换运算,但比较慢。
half:16位浮点格式,适合贴图和UV坐标计算,是highp类型计算的两倍。
fixed: 10位浮点格式,适合颜色,光照,和其余。是highp格式计算的四倍。

写Shader优化的小提示:
http://docs.unity3d.com/Documentation/Components/SL-ShaderPerformance.html


11.另外的相关优化:

a.对Draw Call Batching的优化
http://docs.unity3d.com/Documentation/Manual/DrawCallBatching.html

b.对Rendering Statistics Window的说明和提示:
http://docs.unity3d.com/Documentation/Manual/RenderingStatistics.html

c.角色模型的优化建议
用单个蒙皮渲染、尽可能少用材质、少用骨骼节点、移动设备上角色多边形保持在300~1500内(固然还要看具体的需求)、PC平台上1500~4000内(固然还要看具体的需求)。

http://docs.unity3d.com/Documentation/Manual/ModelingOptimizedCharacters.html

 

 

 

渲染顺序

U3D的渲染是有顺序的,U3D的渲染顺序是由咱们控制的,控制好U3D的渲染顺序,你才能控制好DrawCall

一个DrawCall,表示U3D使用这个材质/纹理,来进行一次渲染,那么此次渲染假设有3个对象,那么当3个对象都使用这一个材质/纹理的 时候,就会产生一次DrawCall,能够理解为一次将纹理输送到屏幕上的过程,(实际上引擎大多会使用如双缓冲,缓存这类的手段来优化这个过程,但在这 里咱们只须要这样子认识就能够了),假设3个对象使用不一样的材质/纹理,那么无疑会产生3个DrawCall

接下来咱们的3个对象使用2个材质,A和B使用材质1,C使用材质2,这时候来看,应该是有2个DrawCall,或者3个DrawCall。 应该是2个DrawCall啊,为何会有3个DrawCall???并且是有时候2个,有时候3个。咱们按照上面的DrawCall分析流程来分析一 下:

1.渲染A,使用材质1 
2.渲染B,使用材质1 
3.渲染C,使用材质2

在这种状况下是2个DrawCall,在下面这种状况下,则是3个DrawCall

1.渲染A,使用材质1 
2.渲染C,使用材质2 
3.渲染B,使用材质1

由于咱们没有控制好渲染顺序(或者说没有去特地控制),因此致使了额外的DrawCall,由于A和B不是一次性渲染完的,而是被C打断了,因此致使材质1被分为两次渲染

那么是什么在控制这个渲染顺序呢?首先在多个相机的状况下,U3D会根据相机的深度顺序进行渲染,在每一个相机中,它会根据你距离相机的距离,由远到近进行渲染,在UI相机中,还会根据你UI对象的深度进行渲染

那么咱们要作的就是,对要渲染的对象进行一次规划,正确地排列好它们,规则是,按照Z轴或者深度,对空间进行划分,而后肯定好每一个对象的Z轴和深度,让使用同一个材质的东西,尽可能保持在这个空间内,不要让其余材质的对象进入这个空间,不然就会打断这个空间的渲染顺序

在这个基础上,更细的规则有:

场景中的东西,咱们使用Z轴来进行空间的划分,例如背景层,特效层1,人物层,特效层2 
NGUI中的东西,咱们统一使用Depth来进行空间的划分 
人物模型,当人物模型只是用一个材质,DrawCall只有1,可是用了2个以上的材质,DrawCall就会暴增(或许对材质的RenderQueue 进行规划也能够使DrawCall只有2个,但这个要拆分好才行),3D人物处于复杂3D场景中的时候,咱们的空间规则不免被破坏,这只能在设计的时候尽 量去避免这种状况了 
使用了多个材质的特效,在动画的过程当中,每每会引发DrawCall的波动,在视觉效果能够接受的范围内,能够将特效也进行空间划分,假设这个特效是2D显示,那么能够使用Z轴来划分空间

 

2、

优化: 

1. 更新不透明贴图的压缩格式为ETC 4bit,由于android市场的手机中的GPU有多种,
每家的GPU支持不一样的压缩格式,但他们都兼容ETC格式,

2. 对于透明贴图,咱们只能选择RGBA 16bit 或者RGBA 32bit。

3. 减小FPS,在ProjectSetting-> Quality中的
VSync Count 参数会影响你的FPS,EveryVBlank至关于FPS=60,EverySecondVBlank = 30;
这两种状况都不符合游戏的FPS的话,咱们须要手动调整FPS,首先关闭垂直同步这个功能,而后在代码的Awake方法里手动设置FPS(Application.targetFrameRate = 45;)
下降FPS的好处:
1)省电,减小手机发热的状况;
2)能都稳定游戏FPS,减小出现卡顿的状况。

4. 当咱们设置了FPS后,再调整下Fixed timestep这个参数,
这个参数在ProjectSetting->Time中,目的是减小物理计算的次数,来提升游戏性能。

5. 尽可能少使用Update LateUpdate FixedUpdate,这样也能够提高性能和节省电量。
多使用事件(不是SendMessage,使用本身写的,或者C#中的事件委托)。

6. 待机时,调整游戏的FPS为1,节省电量。

7. 图集大小最好不要高于1024,不然游戏安装以后、低端机直接崩溃、缘由是手机系统版本低于2.二、超过1000的图集没法读取、致使。
2.2 以上没有碰见这个状况。
注意手机的RAM 与 ROM、小于 512M的手机、直接放弃机型适配。

VSCount 垂直同步
   中新建一个场景空的时候,帧速率(FPS老是很低),大概在60~70之间。
一直不太明白是怎么回事,如今基本上明白了。我在这里解释一下缘由,若有错误,欢迎指正。
在Unity3D中当运行场景打开Profiler的时候,咱们会看到VSync 这一项占了很大的比重。
这个是什么呢,这个就是垂直同步,稍后再作解释。
咱们能够关闭VSync来提升帧速率,选择edit->project settings->Quality。

<ignore_js_op>

 


在右侧面板中能够找到VSync Count,把它选成Don't Sync。
<ignore_js_op> 
这就关闭了VSync(垂直同步),如今在运行场景看看,帧速率是否是提升不少。

如今来讲说什么是垂直同步,要知道什么是垂直同步,必需要先明白显示器的工做原理,
显示器上的全部图像都是一线一线的扫描上去的,不管是隔行扫描仍是逐行扫描,
显示器都有两种同步参数——水平同步和垂直同步。

什么叫水平同步?什么叫垂直同步?
垂直和水平是CRT中两个基本的同步信号,水平同步信号决定了CRT画出一条横越屏幕线的时间,
垂直同步信号决定了CRT从屏幕顶部画到底部,再返回原始位置的时间,
而偏偏是垂直同步表明着CRT显示器的刷新率水平。

为何关闭垂直同步信号会影响游戏中的FPS数值?
若是咱们选择等待垂直同步信号(也就是咱们平时所说的垂直同步打开),
那么在游戏中或许强劲的显卡迅速的绘制完一屏的图像,可是没有垂直同步信号的到达,
显卡没法绘制下一屏,只有等85单位的信号到达,才能够绘制。
这样FPS天然要受到操做系统刷新率运行值的制约。

而若是咱们选择不等待垂直同步信号(也就是咱们平时所说的关闭垂直同步),那么游戏中做完一屏画面,
显卡和显示器无需等待垂直同步信号就能够开始下一屏图像的绘制,天然能够彻底发挥显卡的实力。
可是不要忘记,正是由于垂直同步的存在,才能使得游戏进程和显示器刷新率同步,使得画面更加平滑和稳定。
取消了垂直同步信号,当然能够换来更快的速度,可是在图像的连续性上势必打折扣。
这也正是不少朋友抱怨关闭垂直后发现画面不连续的理论缘由。

合并材质球unity 3d中每倒入一次模型就多一个材质球,可个人这些模型都是共用一张贴图的就想共用一个材质球,因此每次都要删除再附上,很麻烦。怎么才能合并这些材质球?
采用TexturePacking吧
一、遍历gameobject,取出material,并根据shader来将material分类
二、调用Unity自带的PackTextures函数来合并每一个shader分类中的material所对应的textures(PackTextures函数有缺陷,不过能够将就用)
三、根据合并的大的texture来更新原有模型的texture、material已经uv坐标值。

须要注意的是:须要合并的纹理应该是物体在场景中距离相近的,若是物体在场景中的距离较远,
则不建议合并纹理,由于这样作颇有可能非但起不到优化的做用,反而下降了运行效率。 

mesh合并 
分为2种方式合并
1.自带的合并必须勾选静态。
<ignore_js_op> 
全部被勾选了“Static”的GameObject,其中的Mesh Filter中的mesh都会被合并到 "Combined Mesha (root: scene)" 中


2.也能够用脚原本合并mesh 。

[C#]  纯文本查看 复制代码
?
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
using UnityEngine;
using System.Collections;
 
public class MyClass : MonoBehaviour
{
     void Start ()
     {
         MeshFilter [] meshFilters = GetComponentsInChildren<MeshFilter> ();
         CombineInstance[] combine = new CombineInstance[meshFilters.Length];
 
         for ( int i = 0; i < meshFilters.Length; i++) {
             combine [i].mesh = meshFilters [i].sharedMesh;
             combine [i].transform = meshFilters [i].transform.localToWorldMatrix;
             meshFilters [i].gameObject.active = false ;
         }
 
             transform.GetComponent<MeshFilter> ().mesh = new Mesh ();
             transform.GetComponent<MeshFilter> ().mesh.CombineMeshes (combine);
             transform.gameObject.active = true ;
     }
}


1. 先在 Unity 中创建 空物件 ( Empty ) 
2. 再建立2个 Cube 方块,并放入 空物件底下 (能够改为你本身的模型)
3. 把 MyClass 代码丟进 空物件上 。
4. (可选) 创建一个 Material 材质,而且丢进 空物件上
5. 执行

<ignore_js_op> 


<ignore_js_op> 
========================================分割线====================================

  • 角色Material数量
2-3
  • 骨骼数量
小于30个
  • 面片数量
300-1500
  • 通常角色应该没有IK结点
这是由于角色的动做大多数都是事先设定好的,并不须要通过IK操做来进行实时计算(Rogdoll除外),因此在模型导入时,不要将IK结点一块儿导入。

 

二、静态实体

  • 不要附加 Component
在静态实体上附加Animation部件虽然对结果没有影响,但却会增长必定的CPU开销来调用这一组件,因此尽可能去掉该组件。
  • 网格顶点数
小于500
  • UV值范围尽可能不要超过(0, 1)区间
尽可能保证UV值不越界,这对于未来的纹理拼合优化颇有帮助。

 

三、地形

  • 地形的分辨率大小
长宽均尽可能小于257。这是由于地形太大,会形成大量顶点数据,给你的内存带宽形成必定的影响,在目前的ios设备中,内存带宽是很是有限的,须要尽可能节省。同时,若是用Unity自带的地形,必定也要使用Occlusion Culling,由于Unity的刷地形工具虽然方便,但倒是framekiller,刷过以后,你会发现drawcall增长的很是多。
  • 混合纹理数量
不要超过4。地形的混合操做是很耗时的,应该尽可能避免。能合并的纹理尽可能合并。

 

四、纹理

  • 纹理格式
建议png或tga。不用转成ios硬件支持的PVRTC格式,由于Unity在发布时会帮你自动转的。
  • 纹理尺寸
长宽小于1024。同时应该尽量地小,够用就好,以保证纹理对内存带宽的影响达到最小。
  • 支持Mipmap
建议生成Mipmap。虽然这种作法会增长一些应用程序的大小,但在游戏运行时,系统会根据需求应用Mipmap来渲染,从而减小内存带宽。
  • 检查Alpha值
若是纹理的alpha通道均为1,则用RGB的24位纹理来代替RGBA的32位纹理。(听说Unity内部会进行自动检测)

 

五、光源

  • 光源“Important”个数
建议1个,通常为方向光。“Important”个数应该越小越少。个数越多,drawcall越多。
  • Pixel Light数目
1-2个。

 

六、粒子特效

  • 屏幕上的最大粒子数
建议小于200个粒子。
  • 每一个粒子发射器发射的最大粒子数
建议不超过50个。
  • 粒子大小
若是能够的话,粒子的size应该尽量地小。由于Unity的粒子系统的shader不管是alpha test仍是alpha blending都是一笔不小的开销。同时,对于很是小的粒子,建议粒子纹理去掉alpha通道。
  • 尽可能不要开启粒子的碰撞功能。
很是耗时。

 

七、音频

  • 游戏中播放时间较长的音乐(如背景音乐)
使用.ogg或.mp3的压缩格式。
  • 较短音乐(如枪声)
使用.wav和.aif的未压缩音频格式。

 

八、相机

  • 裁剪平面
将远平面设置成合适的距离。远平面过大会将一些没必要要的物体加入渲染,下降效率。
  • 根据不一样的物体设置不一样的远裁剪平面
Unity提供了能够根据不一样的layer来设置不一样的view distance,因此咱们能够实现将物体进行分层,大物体层设置的可视距离大些,而小物体层能够设置地小些,另外,一些开销比较大的实体(如粒子系统)能够设置得更小些等等。

 

九、碰撞

  • 尽可能不用MeshCollider
若是能够的话,尽可能不用MeshCollider,以节省没必要要的开销。若是不能避免的话,尽可能用减小Mesh的面片数,或用较少面片的代理体来代替。

 

十、其余

  • Drawcall
尽量地减小Drawcall的数量。IOS设备上建议不超过100。减小的方法主要有以下几种:Frustum Culling,Occlusion Culling,Texture Packing。Frustum Culling是Unity内建的,咱们须要作的就是寻求一个合适的远裁剪平面;Occlusion Culling,遮挡剔除,Unity内嵌了Umbra,一个很是好OC库。但Occlusion Culling也并非放之四海而皆准的,有时候进行OC反而比不进行还要慢,建议在OC以前先肯定本身的场景是否适合利用OC来优化;Texture Packing,或者叫Texture Atlasing,是将同种shader的纹理进行拼合,根据Unity的static batching的特性来减小draw call。建议使用,但也有弊端,那就是必定要将场景中距离相近的实体纹理进行拼合,不然,拼合后极可能会增长每帧渲染所需的纹理大小,加大内存带宽的负担。这也就是为何会出现“DrawCall降了,渲染速度也变慢了”的缘由。

 

  • 非运动物体尽可能打上Static标签
Unity在运行时会对static物体进行自动优化处理,因此应该尽量将非运行实体勾上static标签。

 

  • 场景中尽量地使用prefab
尽量地使用prefab的实例化物体,以下降内存带宽的负担。检查实体的PrefabType,尽可能将其变成PrefabInstance,而不是ModelPrefabInstance。



========================================分割线==================================== 

移动平台相对于PC机,具备体积小,计算弱,带宽少的特色。

所以作手机游戏的开发,优化的方向,与力度对比PC游戏都有所区别。

 

必需要作到优化流程,合理利用资源。

目前在手机上面,还不可以像PC游戏那样追求高质量渲染效果,为了让手机不那么容易发烫,还要控制cpu,gpu,不能让他们全速运算。

 

材质方面:

纹理方面,建议使用压缩纹理,

上面使用ETC1,苹果上面使用PVRTC。

 

UV坐标控制在0到1之间,人物模型面数控制在1500内,骨骼控制在30个之内。

场景中使用一个主光(不能再多了)。

 

尽可能减小alphaTest和alphaBlend材质的使用。在手机上,这是很杀效率的。

 

骨骼动画方面:

在动画方面能够考虑不使用插值,固定的帧率的动画。

若是要作插值,考虑使用四元数(表示旋转)和向量(表示位移)来作插值。

四元数作插值速度比矩阵来的快,Slerp提供了平滑插值。



========================================分割线==================================== 

优化的常规技巧
剖析你的游戏。
不要花费时间来优化那些晦涩的代码或者缩减图形文件的大小,除非这是你游戏的瓶颈。
第一次剖析你的游戏将会使你发现你游戏的瓶颈。Apple's Shark是一个很好的用来剖析基于OpenGL的程序的工具。
再次剖析你的游戏。
优化以后不要忘记再剖析一次你的游戏,这样能够检查你所作的优化是否达到了预期的效果。
固然,这样作也可能会使你发现更多的瓶颈。
流程第1、性能第二。花费时间来使你游戏的建立尽量地流畅。
尽量快地修正游戏中的错误将会使你后期更容易优化你的游戏。
在Scene View中测试场景。
这样作将会使你清楚了解这个场景中的物体或者附加在物体上的脚本是否下降了游戏性能。
若是Scene View反应迟钝,那么有多是图形方面的缘由,若是Scene View反应不迟钝,那么瓶颈可能出在脚本或者物理系统上。
禁用指定游戏物体。
在play模式下,尝试禁用并启用游戏物体来排查出游戏慢的缘由。

网格
若是可能的话,把相邻的物体(网格)合并为一个只有一个材质的物体(网格)。好比,你的游戏中包含一个桌子,上面有一堆东西,你彻底能够在3D程序中将它们合并在一块儿(这可能也须要你将这些物体的纹理合并为一个大的纹理集)。减小须要渲染的物体的数量能够极大地提升游戏性能。

不要有没必要要的网格。
若是你的游戏场景中有一我的物,那么他应该是一个网格。若是你有一个船,那么它也应该只是一个网格。
每个网格只用一种材质。
使用极少的面数的网格(好比500个多边形如下)。
最好把你人物的三角面数量控制在1500-2000个之间。
这个数量能够说是游戏质量和性能之间一个均衡值。若是你的模型有四边形,那么在导入模型的时候,引擎将会把每一个四边形变为两个三角形。

光照
像素光。
像素光可让你的游戏看起来效果很牛逼,可是不要使用过多的像素光。
在你的游戏中能够使用质量管理器来调节像素光的数量来取得一个性能和质量的均衡点.

性能占用顺序:聚光灯>点光源>平行光。
一个好的点亮场景的方法就是先获得你想要的效果,而后看看哪些光更重要;
在保持光效的前提下看看哪些光能够去掉。

点光源和聚光灯只影响它们范围内的网格。
若是一个网格处于点光源或者聚光灯的照射范围以外,而且光源的attenuate开关是打开的,那么这个网格将不会被光源所影响,这样就能够节省性能开销。
这样作理论上来说能够使用不少小的点光源并且依然能有一个好的性能,由于这些光源只影响一小部分物体。
一个网格在有8个以上光源影响的时候,只响应前8个最亮的光源。

贴图
在外观不变的前提下,贴图大小越小越好。
若是你的显卡的显存不够大的话,你游戏中的贴图将会被转存到系统内存中,在显卡调用它们的时候再传到显卡中。
对于比较新的电脑来讲,内存和显卡之间有足够的带宽来达到一个很好的性能;
若是你很无耻地用了巨多的大图片的话,在低显存的电脑上运行你的游戏的时候,你的游戏必然会挂掉。
却是没有必要在图形编辑软件中调整贴图的大小。你能够在unity导入贴图的时候进行调整。

不要使用低质量的图片。
在小播放界面的游戏中使用低质量的jpeg图片或者低色彩的png图片亦或是gif图片没什么问题
在发布游戏的时候,引擎会自动压缩这些图片,多重压缩和解压将会下降图片的质量,因此最好保持贴图文件的分辨率为原始分辨率。
这样就会减小多重压缩和解压所致使的图片失真现象。

Shaders
多重效果的shader就比看起来样式很单一的shader要更耗费资源。
一样在一个拥有贴图和光反射的物体上,使用VertexLit Diffuse shader无疑是最省资源的。


========================================分割线==================================== 

在美术制做场景的过程当中,会使用到大量的粒子系统。
好比场景中的火把。在咱们的一个地下城场景中,美术们放置了大量的火把。整个场景中的各个地方,有100来个火把。

unity中,在摄像机范围外的粒子系统虽然不会被绘制。
可是update是一直持续的。这也就意味着,这100多个火把,不管是否可见都在更新。

这个设计应该是很不合理的,在我看过的其余引擎中,都会有一个开关,来控制不可见的粒子系统是否须要update。
有的粒子系统在不可见的时候须要更新,好比爆炸。有的不须要更新,好比火堆火把。

为了不没必要要的update开销,尤为是最后游戏是要发布到页游平台(web player只能使用一个cpu的核)。
因而写了一个脚本,控制不可见的粒子系统就不更新。

该脚本主要是用到了2个MonoBehaviour的函数。
OnBecameInvisible() 当变为不可见   和   OnBecameVisible() 当变成可见。 

要这2个函数起做用的前提是,该GameObject绑定了MeshRender组件。
因此,咱们要在粒子系统的GameObject放置在一个GameObject  下,且给该GameObject绑定一个MeshRender 与 MeshFilter。
MeshFilter中的mesh能够随便找个cube。

在Start() 的时候,把最GameObject的scale设置为很小,以保证该cube不被看见。
其实遍历全部的child,把active设置为false。

在OnBecameVisible 中 遍历全部child,把active设置为true。
在OnBecameInvisible中 遍历全部child,把active设置为false。


========================================分割线==================================== 

Unity 性能优化 Draw Call 

Unity(或者说基本全部图形引擎)生成一帧画面的处理过程大体能够这样简化描述:引擎首先通过简单的可见性测试,肯定摄像机能够看到的物体,而后把这些物体的顶点(包括本地位置、法线、UV等),索引(顶点如何组成三角形),变换(就是物体的位置、旋转、缩放、以及摄像机位置等),相关光源,纹理,渲染方式(由材质/Shader决定)等数据准备好,而后通知图形API——或者就简单地看做是通知GPU——开始绘制,GPU基于这些数据,通过一系列运算,在屏幕上画出成千上万的三角形,最终构成一幅图像。

在Unity中,每次引擎准备数据并通知GPU的过程称为一次Draw Call。这一过程是逐个物体进行的,对于每一个物体,不仅GPU的渲染,引擎从新设置材质/Shader也是一项很是耗时的操做。所以每帧的Draw Call次数是一项很是重要的性能指标,对于iOS来讲应尽可能控制在20次之内,这个值能够在编辑器的Statistic窗口看到。

Unity内置了Draw Call Batching技术,从名字就能够看出,它的主要目标就是在一次Draw Call中批量处理多个物体。只要物体的变换和材质相同,GPU就能够按彻底相同的方式进行处理,便可以把它们放在一个Draw Call中。Draw Call Batching技术的核心就是在可见性测试以后,检查全部要绘制的物体的材质,把相同材质的分为一组(一个Batch),而后把它们组合成一个物体(统一变换),这样就能够在一个Draw Call中处理多个物体了(其实是组合后的一个物体)。

但Draw Call Batching存在一个缺陷,就是它须要把一个Batch中的全部物体组合到一块儿,至关于建立了一个与这些物体加起来同样大的物体,与此同时就须要分配相应大小的内存。这不只会消耗更多内存,还须要消耗CPU时间。特别是对于移动的物体,每一帧都得从新进行组合,这就须要进行一些权衡,不然得不偿失。但对于静止不动的物体来讲,只须要进行一次组合,以后就能够一直使用,效率要高得多。

Unity提供了Dynamic Batching和Static Batching两种方式。Dynamic Batching是彻底自动进行的,不须要也没法进行任何干预,对于顶点数在300之内的可移动物体,只要使用相同的材质,就会组成Batch。Static Batching则须要把静止的物体标记为Static,而后不管大小,都会组成Batch。如前文所说,Static Batching显然比Dynamic Batching要高效得多,因而,Static Batching功能是收费的……

要有效利用Draw Call Batching,首先是尽可能减小场景中使用的材质数量,即尽可能共享材质,对于仅纹理不一样的材质能够把纹理组合到一张更大的纹理中(称为Texture Atlasing)。而后是把不会移动的物体标记为Static。此外还能够经过CombineChildren脚本(Standard Assets/Scripts/Unity Scripts/CombineChildren)手动把物体组合在一块儿,但这个脚本会影响可见性测试,由于组合在一块儿的物体始终会被看做一个物体,从而会增长GPU要处理的几何体数量,所以要当心使用。

对于复杂的静态场景,还能够考虑自行设计遮挡剔除算法,减小可见的物体数量同时也能够减小Draw Call。

总之,理解Draw Call和Draw Call Batching原理,根据场景特色设计相应的方案来尽可能减小Draw Call次数才是王道,其它方面亦然。



Draw Call Batching (绘制调用批处理)

To draw an object on the screen, the engine has to issue a draw call to the graphics API (OpenGL ES in the case of iOS). Every single draw call requires a significant amount of work on the part of the graphics API, causing significant performance overhead on the CPU side.
在屏幕上渲染物体,引擎须要发出一个绘制调用来访问图形API(iOS系统中为OpenGL ES)。
每一个绘制调用须要进行大量的工做来访问图形API,从而致使了CPU方面显著的性能开销。

Unity combines a number of objects at runtime and draws them together with a single draw call. This operation is called "batching". The more objects Unity can batch together, the better rendering performance you will get.
Unity在运行时能够将一些物体进行合并,从而用一个绘制调用来渲染他们。这一操做,咱们称之为“批处理”。
通常来讲,Unity批处理的物体越多,你就会获得越好的渲染性能。

Built-in batching support in Unity has significant benefit over simply combining geometry in the modeling tool (or using theCombineChildren script from the Standard Assets package). Batching in Unity happensafter visibility determination step. The engine does culling on each object individually, and the amount of rendered geometry is going to be the same as without batching. Combining geometry in the modeling tool, on the other hand, prevents effecient culling and results in much higher amount of geometry being rendered.
Unity中内建的批处理机制所达到的效果要明显强于使用几何建模工具(或使用Standard Assets包中的CombineChildren脚本)的批处理效果。
这是由于,Unity引擎的批处理操做是在物体的可视裁剪操做以后进行的。
Unity先对每一个物体进行裁剪,而后再进行批处理,这样能够使渲染的几何总量在批处理先后保持不变。
可是,使用几何建模工具来拼合物体,会妨碍引擎对其进行有效的裁剪操做,从而致使引擎须要渲染更多的几何面片。

Materials
材质
Only objects sharing the same material can be batched together. Therefore, if you want to achieve good batching, you need to share as many materials among different objects as possible.
只有拥有相同材质的物体才能够进行批处理。
所以,若是你想要获得良好的批处理效果,你须要在程序中尽量地复用材质和物体。

If you have two identical materials which differ only in textures, you can combine those textures into a single big texture - a process often calledtexture atlasing. Once textures are in the same atlas, you can use single material instead.
若是你的两个材质仅仅是纹理不一样,那么你能够经过 纹理拼合 操做来将这两张纹理拼合成一张大的纹理。
一旦纹理拼合在一块儿,你就能够使用这个单一材质来替代以前的两个材质了。

If you need to access shared material properties from the scripts, then it is important to note that modifyingRenderer.material will create a copy of the material. Instead, you should useRenderer.sharedMaterial to keep material shared.
若是你须要经过脚原本访问复用材质属性,那么值得注意的是改变Renderer.material将会形成一份材质的拷贝。
所以,你应该使用Renderer.sharedMaterial来保证材质的共享状态。

Dynamic Batching
动态批处理
Unity can automatically batch moving objects into the same draw call if they share the same material.
若是动态物体共用着相同的材质,那么Unity会自动对这些物体进行批处理。

Dynamic batching is done automatically and does not require any additional effort on your side.
动态批处理操做是自动完成的,并不须要你进行额外的操做。

Tips:
提醒:
一、      Batching dynamic objects has certain overheadper vertex, so batching is applied only to meshes containing less than900 vertex attributes in total.
             批处理动态物体须要在每一个顶点上进行必定的开销,因此动态批处理仅支持小于900顶点的网格物体。

二、      If your shader is using Vertex Position, Normal and single UV, then you can batch up to 300 verts and if your shader is using Vertex Position, Normal, UV0, UV1 and
            Tangent, then only 180 verts.
            Please note: attribute count limit might be changed in future
            若是你的着色器使用顶点位置,法线和UV值三种属性,那么你只能批处理300顶点如下的物体;
若是你的着色器须要使用顶点位置,法线,UV0,UV1和切向量,那你只
            能批处理180顶点如下的物体。
            请注意:属性数量的限制可能会在未来进行改变。

四、      Don't use scale. Objects with scale (1,1,1) and (2,2,2) won't batch.
            不要使用缩放尺度(scale)。分别拥有缩放尺度(1,1,1)和(2,2,2)的两个物体将不会进行批处理。

五、      Uniformly scaled objects won't be batched with non-uniformly scaled ones.
            统一缩放尺度的物体不会与非统一缩放尺度的物体进行批处理。
           Objects with scale (1,1,1) and (1,2,1) won't be batched. On the other hand (1,2,1) and (1,3,1) will be.
           使用缩放尺度(1,1,1)和 (1,2,1)的两个物体将不会进行批处理,可是使用缩放尺度(1,2,1)和(1,3,1)的两个物体将能够进行批处理。

六、     Using different material instances will cause batching to fail.
           使用不一样材质的实例化物体(instance)将会致使批处理失败。

七、     Objects with lightmaps have additional (hidden) material parameter: offset/scale in lightmap, so lightmapped objects won't be batched (unless they point to same
           portions of lightmap)
           拥有lightmap的物体含有额外(隐藏)的材质属性,好比:lightmap的偏移和缩放系数等。因此,拥有lightmap的物体将不会进行批处理(除非他们指向lightmap的同一
           部分)。

八、     Multi-pass shaders will break batching. E.g. Almost all unity shaders supports several lights in forward rendering, effectively doing additional pass for them
           多通道的shader会妨碍批处理操做。好比,几乎unity中全部的着色器在前向渲染中都支持多个光源,并为它们有效地开辟多个通道。

九、     Using instances of a prefab automatically are using the same mesh and material.
           预设体的实例会自动地使用相同的网格模型和材质。

Static Batching
静态批处理

Static batching, on the other hand, allows the engine to reduce draw calls for geometry of any size (provided it does not move and shares the same material). Static batching is significantly more efficient than dynamic batching. You should choose static batching as it will require less CPU power.
相对而言,静态批处理操做容许引擎对任意大小的几何物体进行批处理操做来下降绘制调用(只要这些物体不移动,而且拥有相同的材质)。所以,静态批处理比动态批处理更加有效,你应该尽可能低使用它,由于它须要更少的CPU开销。

In order to take advantage of static batching, you need explicitly specify that certain objects are static and willnot move, rotate or scale in the game. To do so, you can mark objects as static using the Static checkbox in the Inspector:
为了更好地使用静态批处理,你须要明确指出哪些物体是静止的,而且在游戏中永远不会移动、旋转和缩放。想完成这一步,你只须要在检测器(Inspector)中将Static复选框打勾便可,以下图所示:


Using static batching will require additional memory for storing the combined geometry. If several objects shared the same geometry before static batching, then a copy of geometry will be created for each object, either in the Editor or at runtime. This might not always be a good idea - sometimes you will have to sacrifice rendering performance by avoiding static batching for some objects to keep a smaller memory footprint. For example, marking trees as static in a dense forest level can have serious memory impact.
使用静态批处理操做须要额外的内存开销来储存合并后的几何数据。在静态批处理以前,若是一些物体共用了一样的几何数据,那么引擎会在编辑以及运行状态对每一个物体建立一个几何数据的备份。这并不老是一个好的想法,由于有时候,你将不得不牺牲一点渲染性能来防止一些物体的静态批处理,从而保持较少的内存开销。好比,将浓密森里中树设为Static,会致使严重的内存开销。

Static batching is only available in Unity iOS Advanced.
静态批处理目前只支持Unity iOS Advanced。



备注:最近一直在研究Unity3D的性能优化问题,这段时间可能会多翻译这方面的文章。

前两天,MadFinger,就是当今iOS与Android上画质最牛逼闪闪的游戏之一——ShadowGun的开发商,使人惊异地放出了一个ShadowGun的样例关卡以及若干可无偿使用的Shader,国外同行们的分享精神真的是使人赞叹不已。原文在这里,如下是个人一些摘录和笔记。

首先是一些优化常识。针对图形方面的优化主要包括三角形数量,纹理所占内存,以及Shader,前两项基本没什么好讲的,针对设备机能的限制制定相应的指标便可,因此Shader就成为了图形性能优化的关键。

Alpha blending

在Unity官方文档中讲,因为硬件缘由,在iOS设备上使用alpha-test会形成很大的性能开销,应尽可能使用alpha-blend代替。这里提到,在同屏使用alpha-blend的面数,尤为是这些面所占屏幕面积的大小,对性能也会形成很大影响。缘由是使用alpha-blend的面会形成overdraw的增长,这尤为对低性能设备的影响很大。不过没有购买Pro版,没有Occlusion Culling功能的话,就没必要顾虑这一问题了,反正overdraw是必然的。

复杂的Per-pixel shader

Per-pixel shader即Fragment shader,顾名思义是要对每一个渲染到屏幕上的像素作处理的shader,若是per-pixel shader比较复杂且须要处理的像素不少时,也就是使用该shader的面占屏幕面积很大时,对性能的影响甚至要超过alpha blending。所以复杂的per-pixel shader只适用于小物体。

下面是对几个Shader的逐一讲解:

Environment specular maps(Shader Virtual Gloss Per Vertex Additive)
Specular map一般都是利用贴图的alpha通道来定义物体表面的光滑程度(反光度),这个shader的特色是per-vertex计算反光度的,有着至关不错的效果的同时比per-pixel的shader性能要高得多。这个shader很适用于关卡环境等占很大区域的模型。

通过优化的动态角色光照和阴影(Light probes和BRDF Shader)
传统的Lightmaps没法支持动态物体,对此Unity提供了Light probes技术,预先把动态物体的光照信息保存在代理对象(即Light probes)中,运行时动态物体从距离最近的Probe中获取光照信息。

Unity自己还提供了一个效果很是棒的专为移动设备优化过的角色Shader,支持Diffuse、Specular和Normal maps,并经过一个特殊的脚本生成贴图用于模仿BRDF光照效果。最终产生的效果堪比次时代大做中的角色光影效果。

雾和体积光(Shader Blinking Godrays)
目前在移动设备上要开启真正的雾效基本不可行,ShadowGun的方案是经过简单的网格+透明贴图(称为雾面)来模拟雾效。在玩家靠近时,雾面逐渐变淡,同时fog plane的顶点也会移开(即便彻底透明的alpha面也会消耗不少渲染时间)。

使用这个Shader的网格须要通过处理:

顶点的alpha值用于决定顶点是否能够移动(在例子中0为不可动,1为可动)。
顶点法线决定移动的方向
而后Shader经过计算与观察者的距离来控制雾面的淡入/淡出。
这个Shader还能够用来作体积光和其它一些alpha效果。

飞机坠毁的浓烟效果(Shader Scroll 2 Layers Sine Alpha-blended)
经过粒子产生浓烟的代价过高,因此ShadowGun中使用了网格+贴图动画来制做这个效果。经过混合两层贴图并让它们交错移动来产生动画效果。其中顶点alpha值用于让网格的边缘看起来比较柔和,同时使用顶点颜色来模拟从火焰到烟雾的过渡效果。

带动态效果的天空盒(Shader Scroll 2 Layers Multiplicative)
经过两张贴图的混合和移动产生云的动态效果。

旗帜和衣服的飘动效果(Shader Lightmap + Wind)
一样利用顶点alpha值决定哪些顶点能够移动,而后shader的参数用于调整摆动的方向和速度。
=======================分割线========================

1、程序方面
  0一、务必删除脚本中为空或不须要的默认方法;
  0二、只在一个脚本中使用OnGUI方法;
  0三、避免在OnGUI中对变量、方法进行更新、赋值,输出变量建议在Update内;
  0四、同一脚本中频繁使用的变量建议声明其为全局变量,脚本之间频繁调用的变量或方法建议声明为全局静态变量或方法;
  0五、不要去频繁获取组件,将其声明为全局变量;
  0六、数组、集合类元素优先使用Array,其次是List;
  0七、脚本在不使用时脚本禁用之,须要时再启用;
  0八、能够使用Ray来代替OnMouseXXX类方法;
  0九、须要隐藏/显示或实例化来回切换的对象,尽可能不要使用SetActiveRecursively或active,而使用将对象远远移出相机范围和移回原位的作法;
  十、尽可能少用模运算和除法运算,好比a/5f,必定要写成a*0.2f。
  十一、对于不常常调用或更改的变量或方法建议使用Coroutines & Yield;
  十二、尽可能直接声明脚本变量,而不使用GetComponent来获取脚本;
iPhone
  1三、尽可能使用整数数字,由于iPhone的浮点数计算能力不好;
  1四、不要使用原生的GUI方法;
  1五、不要实例化(Instantiate)对象,事先建好对象池,并使用Translate“生成”对象;
 

2、模型方面
  0一、合并使用同贴图的材质球,合并使用相同材质球的Mesh;
  0二、角色的贴图和材质球只要一个,若必须多个则将模型离分离为多个部分;
  0二、骨骼系统不要使用太多;
  0三、当使用多角色时,将动画单独分离出来;
  0四、使用层距离来控制模型的显示距离;
  0五、阴影其实包含两方面阴暗和影子,建议使用实时影子时把阴暗效果烘焙出来,不要使用灯光来调节光线阴暗。
  0六、少用像素灯和使用像素灯的Shader;
  0八、若是硬阴影能够解决问题就不要用软阴影,而且使用不影响效果的低分辨率阴影;
  0八、实时阴影很耗性能,尽可能减少产生阴影的距离;
  0九、容许的话在大场景中使用线性雾,这样能够使远距离对象或阴影不易察觉,所以能够经过减少相机和阴影距离来提升性能;
  十、使用圆滑组来尽可能减小模型的面数;
  十一、项目中若是没有灯光或对象在移动那么就不要使用实时灯光;
  十二、水面、镜子等实时反射/折射的效果单独放在Water图层中,而且根据其实时反射/折射的范围来调整;
  1三、碰撞对效率的影响很小,但碰撞仍是建议使用Box、Sphere碰撞体;
  1四、建材质球时尽可能考虑使用Substance;
  1五、尽可能将全部的实时反射/折射(如水面、镜子、地板等等)都集合成一个面;
  1六、假反射/折射没有必要使用过大分辨率,通常64*64就能够,不建议超过256*256;
  1七、须要更改的材质球,建议实例化一个,而不是使用公共的材质球;
  1八、将不须射线或碰撞事件的对象置于IgnoreRaycast图层;
  1九、将水面或相似效果置于Water图层
  20、将透明通道的对象置于TransparentFX图层;
  2一、养成良好的标签(Tags)、层次(Hieratchy)和图层(Layer)的条理化习惯,将不一样的对象置于不一样的标签或图层,三者有效的结合将很方便的按名称、类别和属性来查找;
  2二、经过Stats和Profile查看对效率影响最大的方面或对象,或者使用禁用部分模型的方式查看问题到底在哪儿;
  2三、使用遮挡剔除(Occlusion Culling)处理大场景,一种较原生的类LOD技术,而且可以“分割”做为总体的一个模型。

3、其它
  场景中若是没有使用灯光和像素灯,就不要使用法线贴图,由于法线效果只有在有光源(Direct Light/Point Light/Angle Light/Pixel Light)的状况下才有效果。

C#语言 优化

1、用属性代替可访问的字段

  一、.NET数据绑定只支持数据绑定,使用属性能够得到数据绑定的好处;

  二、在属性的get和set访问器重可以使用lock添加多线程的支持。

  2、readonly(运行时常量)和const(编译时常量)

  一、const只可用于基元类型、枚举、字符串,而readonly则能够是任何的类型;

  二、const在编译时将替换成具体的常量,这样若是在引用中同时使用了const和readonly两种值,则对readonly的再次改变将会改变设计的初衷,这是须要从新编译所更改的程序集,以从新引用新的常量值。

  三、const比readonly效率高,但失去了应用的灵活性。

  3、is与as

  一、二者都是在运行时进行类型的转换,as操做符只能使用在引用类型,而is能够使用值和引用类型;

  二、一般的作法是用is判断类型,而后选择使用as或强类型转换操做符(用operater定义的转换)有选择地进行。

  4、ConditionalAttribute代替#if #endif条件编译

  一、ConditionalAttribute只用于方法级,对其余的如类型、属性等的添加都是无效的;而#if #endif则不受此限制;

  二、ConditionalAttribute能够添加多个编译条件的或(OR)操做,而#if #endif则能够添加与(AND)[这里能够彻底定义为另外一个单独的符号];

  三、ConditioanlAttribute定义能够放在一个单独的方法中,使得程序更为灵活。

  5、提供ToString()方法

  一、能够更友好的方式提供用户详细的信息;

  二、使用IFormatter.ToString()方法提供更灵活的定制,若是添加IFormatProvider 和ICustomFormatter接口则更有意义的定制消息输出。

  6、值和引用类型的区别

  一、值类型不支持多态,适合存储应用程序操做的数据,而引用则支持多态,适用于定义应用程序的行为;

  二、对于数组定义为值类型能够显著提升程序的性能;

  三、值类型具备较少的堆内存碎片、内存垃圾和间接访问时间,其在方法中的返回是以复制的方式进行,避免暴露内部结构到外界;

  四、值类型应用在以下的场景中:类型的职责主要是用于数据存储;公共接口彻底由一些数据成员存取属性定义;永远没有子类;永远没有多态行为。

  7、值类型尽量实现为常量性和原子性的类型

  一、使咱们的代码更易于编写和维护;

  二、初始化常量的三种策略:在构造中;工厂方法;构造一个可变的辅助类(如StringBuilder)。

  8、确保0为值得有效状态

  一、值类型的默认状态应为0;

  二、枚举类型的0不该为无效的状态;在FlagsAttribute是应确保0值为有效地状态;

  三、在字符串为为空时能够返回一个string.Empty的空字符串;

  9、相等判断的多种表示关系

  一、ReferenceEquals()判断引用相等,须要两个是引用同一个对象时方可返回true;

  二、静态的Equals()方法先进性引用判断再进行值类型判断的;

  三、对于引用类型的判断能够在使用值语义时使用重写Equals()方法;

  四、重写Equals()方法时也应当重写GetHashCode()方法,同时提供operater==()操做。

  10、理解GetHashCode()方法的缺陷

  一、GetHashCode()仅应用在基于散列的集合定义键的散列值,如HashTable或Dictionary;

  二、GetHashCode()应当遵循相应的三条规则:两个相等对象应当返回相同的散列码;应当是一个实例不变式;散列函数应该在全部的整数中产生一个随机的分布;

  11、优先使用foreach循环语句

  一、foreach能够消除编译器对for循环对数组边界的检查;

  二、foreach的循环变量是只读的,且存在一个显式的转换,在集合对象的对象类型不正确时抛出异常;

  三、foreach使用的集合须要有:具有公有的GetEnumberator()方法;显式实现了IEnumberable接口;实现了IEnumerator接口;

  四、foreach能够带来资源管理的好处,由于若是编译器能够肯定IDisposable接口时能够使用优化的try…finally块;

  12、默认字段的初始化优于赋值语句

  一、字段生命默认会将值类型初始化为0,引用类型初始化为null;

  二、对同一个对象进行屡次初始化会下降代码的执行效率;

  三、将字段的初始化放到构造器中有利于进行异常处理。

  十3、使用静态构造器初始化静态成员

  一、静态构造器会在一个类的任何方法、变量或者属性访问以前执行;

  二、静态字段一样会在静态构造器以前运行,同时静态构造器有利于异常处理。

  十4、利用构造器链(在.NET 4.0已经用可选参数解决了这个问题)

  一、用this将初始化工做交给另外一个构造器,用base调用基类的构造器;

  二、类型实例的操做顺序是:将全部的静态字段都设置为0;执行静态字段初始化器;执行基类的静态构造器;执行当前类型的静态构造器;

  将全部的实例字段设置为0;执行实例字段初始化器;执行合适的基类实例构造器;执行当前类型的实例构造器。

  十5、利用using和try/finally语句来清理资源

  在IDisposable接口的Dispose()方法中用GC.SuppressFinalize()可通知垃圾收集器再也不执行终结操做。

  十6、尽可能减小内存垃圾

  一、分配和销毁一个对上的对象都要花费额外的处理器时间;

  二、减小分配对象数量的技巧:常用的局部变量提高为字段;提供一个类,用于存储Singleton对象来表达特定类型的经常使用实例。

  三、用StringBuilder进行复杂的字符串操做。

  十7、尽可能减小装箱和拆箱

  一、关注一个类型到System.Object的隐式转换,同时值类型不该该被替换为System.Object类型;

  二、使用接口而不是使用类型能够避免装箱,即将值类型从接口实现,而后经过接口调用成员。

  十8、实现标准Dispose模式

  一、使用非内存资源,它必须有一个终结器,垃圾收集器在完成没有终结其的内存对象后会将实现了终结器对象的添加到终结队列中,而后垃圾收集器会启动一个新的线程来运行这些对象上的终结器,这种防护性的变成方式是由于若是用户忘记了调用Dispose()方法,垃圾回收器老是会调用终结器方法的,这样能够避免出现非托管的内存资源不被释放引发内存泄漏的问题;

  二、使用IDisposable.Dispose()方法须要作四个方面的工做:释放全部的非托管资源;释放全部的托管资源;设置一个状态标记来表示是否已经执行了Dispose();调用GC.SuppressFinalize(this)取消对象的终结操做;

  三、为须要多态的类型添加一个受保护的虚方法Dispose(),派生类经过重写这个方法来释放本身的任务;

  四、在须要IDisoposable接口的类型中,即便咱们不须要一个终结器也应该实现一个终结器。

  十9、定义并实现接口优于继承类型

  一、不相关的类型能够共同实现一个共同的接口,并且实现接口比继承更容易;

  二、接口比较稳定,他将一组功能封装在一个接口中,做为其余类型的实现合同,而基类则能够随着时间的推移进行扩展。

  二10、明辨接口实现和虚方法重写

  一、在基类中实现一个接口时,派生类须要使用new来隐藏对基类方法的使用;

  二、能够将基类接口的方法申明为虚方法,而后再派生类中实现。

  二11、使用委托表达回调

  一、委托对象自己不提供任何异常捕获,因此任何的多播委托调用都会结束整个调用链;

  二、经过显示调用委托链上的每一个委托目标能够避免多播委托仅返回最后一个委托的输出。

  二12、使用事件定义外部接口

  一、应当声明为共有的事件,让编译器为咱们建立add和renmove方法;

  二、使用System.ComponentModel.EventHandlerList容器来存储各个事件处理器,在类型中包含大量事件时能够使用他来隐藏全部事件的复杂性。

  二十3、避免返回内部类对象的引用

  一、因为值类型对象的访问会建立一个该对象的副本,因此定义一个值类型的的属性彻底不会改变类型对象内部的状态;

  二、常量类型能够避免改变对象的状态;

  三、定义接口将访问限制在一个子集中从而最小化对对象内部状态的破坏;

  四、定义一个包装器对象来限制另外一个对象的访问;

  五、但愿客户代码更改内部数据元素时能够实现Observer模式,以使对象能够对更改进行校验或相应。

  二十4、声明式编程优于命令式编程

  能够避免在多个相似的手工编写的算法中犯错误的可能性,并提供清晰和可读的代码。

  二十5、尽量将类型实现为可序列化的类型

  一、类型表示的不是UI控件、窗口或者表单,都应使类型支持序列化;

  二、在添加了NonSerializedAttribute的反序列化的属性时能够经过实现IDeserializationCallback的OnDeserialization()方法装入默认值;

  三、在版本控制中能够使用ISerializable接口来进行灵活的控制,同时提供一个序列化的构造器来根据流中的数据初始化对象,在实现时还要求SerializationFormatter异常的许可。

  四、若是须要建立派生类则须要提供一个挂钩方法供派生类使用。

  二十6、使用IComparable和IComparer接口实现排序关系

  一、IComparable接口用于为类型实现最天然的排序关系,重载四个比较操做符,能够提供一个重载版的CompareTo()方法,让其接受具体类型做为参数;

  二、IComparer用于提供有别于IComparable的排序关系,或者为咱们提供类型自己说没有实现的排序关系。

  二十7、避免ICloneable接口

  一、对于值类型永远不须要支持ICloneable接口使用默认的赋值操做便可;

  二、对于可能须要支持ICloneable接口的基类,应该为其创造一个受保护的复制构造器,并应当避免支持IConeable接口。

  二十8、避免强制转换操做符

  经过使用构造器来代替转换操做符能够使转换工做变得更清晰,因为在转换后使用的临时对象,容易致使一些诡异的BUG。

  二十9、只有当新版积累致使问题是才考虑使用new修饰符

  三10、尽量实现CLS兼容的程序集

  一、建立一个兼容的程序集须要遵循两条规则:程序集中全部公有和受保护成员所使用的参数和返回值类型都必须与CLS兼容;任何与CLS不兼容的公有和受保护成员都必须有一个与CLS兼容的替代品;

  二、能够经过显式实现接口来避开CLS兼容类型检查,及CLSCompliantAttribute不会检查私有的成员的CLS兼容性。

  三11、尽量实现短小简洁的方法

  一、JIT编译器以方法为单位进行编译,没有被调用的方法不会被JIT编译;

  二、若是将较长的Switch中的Case语句的代码替换成一个一个的方法,则JIT编译器所节省的时间将成倍增长;

  三、短小精悍的方法并选择较少的局部变量能够得到优化的寄存器使用;

  四、方法内的控制分支越少,JIT编译器越容易将变量放入寄存器。

  三12、尽量实现小尺寸、高内聚的程序集

  一、将全部的公有类以及共用的基类放到一些程序集中,把为公有类提供功能的工具类也放入一样的程序集中,把相关的公有接口打包到他们本身的程序集中,最后处理遍及应用程序中水平位置的类;

  二、原则上建立两种组件:一种为小而聚合、具备某项特定功能的程序集,另外一种为大而宽、包含共用功能的程序集。

  三十3、限制类型的可见性

  一、使用接口来暴露类型的功能,能够使咱们更方便地建立内部类,同时又不会限制他们在程序集外的可用性;

  二、向外暴露的公有类型越少,将来扩展和更改实现所拥有的选择就越多。

  三十4、建立大粒度的Web API

  这是在机器之间的交易的频率和载荷都降到最低,将大的操做和细粒度的执行放到服务器执行。

  三十5、重写优于事件处理器

  一、一个事件处理器抛出异常,则事件链上的其余处理器将不会被调用,而重写的虚方法则不会出现这种状况;

  二、重写要比关联事件处理器高效得多,事件处理器须要迭代整个请求列表,这样占用了更多的CPU时间;

  三、事件能在运行时响应,具备更多的灵活性,能够对同一个事件关联多个响应;

  四、通行的规则是处理一个派生类的事件是,重写方式较好。

  三十6、合理使用.NET运行时诊断

  一、System.Diagnostics.Debug\Trace\EventLog为运行时提供了程序添加诊断信息所须要的全部工具,EventLog提供入口时的应用程序能写到系统事件日志中;

  二、最后不要写本身的诊断库,.NET FCL 已经拥有了咱们须要的核心库。

  三十7、使用标准配置机制

  一、.NET框架的System.Windows.Application类为咱们定义了创建通用配置路径的属性;

  二、Application.LocalAppDataPath和Application.userDataPath 会生成本地数据目录和用户数据的路径名;

  三、不要在ProgramFiles和Windows系统目录中写入数据,这些位置须要更高的安全权限,不要期望用户拥有写入的权限。

  三十8、定制和支持数据绑定

  一、BindingMananger和CurrencyManager这两个对象实现了控件和数据源之间的数据传输;

  二、数据绑定的优点:使用数据绑定要比编写本身的代码简单得多;应该将它用于文本数据项以外的范围-其余显示属性也能够被绑定;对于Windowos Forms 数据绑定可以处理多个控件同步的检查相关数据源;

  三、在对象不支持所需的属性时能够经过屏蔽当前的对象而后添加一个想要的对象来支持数据绑定。

  三十9、使用.NET验证

  一、ASP.NET中有五种控件来验证有效性,能够用CustomValidator派生一个新类来增长本身的认证器;

  二、Windows验证须要子System.Windows.Forms.Control.Validating些一个事件处理器。

  四10、根据须要选用恰当的集合

  一、数组有两个比较明显的缺陷:不能动态的调整大小;调整大小很是耗时;

  二、ArrayList混合了一维数组和链表的特征,Queue和Stack是创建在Array基础上的特殊数组;

  三、当程序更加灵活的添加和删除项时,能够使更加健壮的集合类型,当建立一个模拟集合的类时,应当为其实现索引器和IEnumberable接口。

  四11、DataSet优于自定义结构

  一、DataSet有两个缺点个:使用XML序列化机制的DataSet与非.NET 代码之间的交互不是很好;DataSet是一个很是通用的容器;

  二、强类型的DataSet打破了更多的设计规则,其得到的开发效率要远远高于本身编写的看上去更为优雅的设计。

  四12、利用特性简化反射

  经过设计和实现特性类,强制开发人员用他们来声明可被动态使用的类型、方法和属性,能够减小应用程序的运行时错误,提升软件的用户满意度。

  四十3、避免过分使用反射

  一、Invoke成员使用的参数和返回值都是System.Object,在运行时进行类型的转换,但出现问题的可能性也变得更多了;

  二、接口使咱们能够获得一个更为清晰、也更具可维护性的系统,反射式一个很强大的晚期绑定机制.NET框架使用它来实现Windows控件和Web控件的数据绑定。

  四十4、为应用程序建立特定的异常类

  一、须要不一样的异常类的惟一缘由是让用户在编写catch处理器时可以方便地对不一样的错误采起不一样的作法;

  二、可能有不一样的修复行为时咱们才应该建立多种不一样的异常类,经过提供异常基类所支持的全部构造器,能够为应用程序建立功能完整的异常类,使用InnerException属性能够保存更低级别错误条件所产生的全部错误信息。

  四十5、优先选择异常安全保证

  一、强异常保证在从异常中恢复和简化异常处理之间提供了最好的平衡,在操做由于异常而中断,程序的状态保留不变;

  二、对将要修改的数据作防护性的复制,对这些数据的防护性复制进行修改,这中间的操做可能会引起异常,将临时的副本和原对象进行交换;

  三、终结器、Dispose()方法和委托对象所绑定的目标方法在任何状况下都应当确保他们不会抛出异常。

  四十6、最小化互操做

  一、互操做有三个方面的代价:数据在托管堆和非托管堆之间的列举成本,托管代码和非托管代码之间切换的成本,对开发人员来讲与混合环境打交道的开发工做;

  二、在interop中使用blittable类型能够有效地在托管和非托管环境中来回复制,而不受对象内部结构的影响;

  三、使用In/Out特性来确保最贴切的没必要要的屡次复制,经过声明数据如何被列举来提升性能;

  四、使用COM Interop用最简单的方式实现和COM组件的互操做,使用P/Invoke调用Win32 API,或者使用C++编译器的/CLR开关来混合托管和非托管的代码;

  四十7、优先选择安全代码

  一、尽量的避免访问非托管内存,隔离存储不能防止来自托管代码和受信用户的访问;

  二、程序集在Web上运行时能够考虑使用隔离存储,当某些算法确实须要更高的安全许可时,应该将那些代码隔离在一个单独的程序集中。

  四十8、掌握相关工具与资源

  一、使用NUnit创建自动单元测试(集成在VS2010 中了);

  二、FXCop工具会获取程序集中的IL代码,并将其与异族编码规则和最佳实践对照分析,最后报告违例状况;

  三、ILDasm是一个IL反汇编工具,能够帮助咱们洞察细节;

  四、Shared Source CLI是一个包含.NET框架内核和C#编译器的实现源码。

  四十9、为C#2.0作准备(这个规则如今已经没什么意义了,毕竟如今已经到了4.0 )

  五10、了解ECMA标准