Unity 性能优化及一些其他技巧

更新日志:(由于此篇会长期不定时更新,所以添加一个“更新日志”)
Unity 的托管内存优化 2017.09.08
Xcode调试及遇到的问题 2017.09.08

下面片段式的总结一些常用的性能优化技巧和一些常见问题:

  • 模型导入要使用FBX文件,并且关闭 Read/Write Enable。强调一下,如果打开 Read/Write Enable 会导致内存翻倍。

  • 如果模型中不存在动画数据 或者 这个模型计划就是不带动作的,模型的导入设置中的 Animation Type就选择None。如果不这样做,即使这个FBX里面没有动画数据,也会默认添加一个Animator的组件,会增加了内存的占用。

  • FBX导入的时候,如果不使用一些Normalmap等特性,建议关闭。你如果开了Normal Tangent导入也都是浪费了内存空间,一般都是在项目开始初期决定用不用Normal Map,如果不用的话以后可以把这个关闭掉。

  • 音频设置方面,建议iOS使用mp3,安卓采用vorbis。

  • 音频的采用率一般20k就够了

  • 音频文件如果不考虑双声道,最好压缩成单声道。音频文件还要考虑是单声道还是双声道,一般情况下用单声道效果是足够的,我们把它压缩成单声道也可以减少内存的占用。

  • 建议对小音频采用Decompress On Load这种模式的加载方式。因为对小的音频文件的话,你在加载的时候解压缩一次,这样播放的时候不需要重新解压缩,如果每次解压缩会导致手机发烫、续航减少等等问题。

  • 不推荐使用Resources文件夹。Unity官方已经不再推荐使用Resources文件夹了,而且未来可能把这个功能关闭掉。因为你把资源放在这里面的话,它是有几个缺点的,其中一个缺点就是在游戏启动的时候,它第一步会把Resources文件夹内的所有文件构建成索引,以便于你后面可以在这些索引里面动态的加载资源。这样做导致游戏启动比较耗时,会发现启动黑屏的时间很长,这样体验实在不好。还有一个缺点是,构建索引会占用更多系统的内存资源。

  • 不建议使用OnGUI。OnGUI是Unity老的UI系统,现在不建议使用OnGUI了。

  • Unity 5.2之后,UGUI优于NGUI。虽然NGUI很强大,但还是有它的不足。因为它使用C#开发,会导致堆栈的内存分配,运行时会导致内存的操作。

  • 少用后期镜头特效。后期特效其实有多个问题,首先它的GPU消耗比较高,另外一个消耗是会导致你的内存消耗非常大,因为每个特效都有可能分配你一个全屏幕大小的一个RenderTexture。而RenderTexture因为要用来实时渲染,所以是不能压缩,不像普通的游戏纹理可以使用ETC或者PVRTC格式压缩。所以它对内存的占用非常高,我们经常看到有的游戏后期特效分配了几十兆甚至更高的内存。

  • 不需要Alpha通道的图片,一定要把Alpha通道都关闭掉。

  • 发现PutGeometryJobFence耗时较多就代表了“对网格体顶点进行操作的次数太多”。一般是因为使用了很多粒子系统,LineRenderer或者Trail,就会产生这个消耗。

  • 如果是移动端的产品,尽量使用Mobile或者Unlit下的shader。要注意的是,特效人员在制作特效的时候,也需要使用这两个目录下的shader。不然会多少产生一些兼容性和内存问题。

  • 移动端产品的最大粒子数建议小于200,每个粒子发射器的最大粒子数建议不超过50,不是必要的情况不要开启粒子的碰撞。

  • 移动设备上,Drawcall的数量建议不超过100。很多人都会问到这个问题,其实这个是没有一个定数的,我这里给出的也只是一般参考。具体一个游戏在手机端卡不卡,不是Drawcall的数量越少就越流畅的,因为流畅性还受限于GPU的带宽等其他东西。

  • 隐藏物体尽量不要使用GameObject.SetActive这个API,可以使用更改物体的Layer、移动到摄像机外、改变层级等等方式。因为GameObject.SetActive这个API会导致物体的顶点重构,甚至造成卡顿。

  • 光源的性能占用顺序:聚光灯>点光源>平行光

  • 注意Renderer.material和Renderer.sharedMaterial的区别。如果你需要通过脚本来访问材质属性,那么你就要注意,如果改变Renderer.material将会造成一份材质的拷贝。所以,应该尽量使用Renderer.sharedMaterial来保证材质的共享状态。

批处理

  • 动态批处理仅支持小于900顶点的网格物体

  • 如果你的Shader中使用顶点位置,法线和UV值三种属性,那么你只能批处理300顶点以下的物体(这个限制不知道在Unity5中还存不存在)

  • 如果你的着色器需要使用顶点位置,法线,UV0,UV1和切向量,那你只能批处理180顶点以下的物体(这个限制不知道在Unity5中还存不存在)

  • 统一缩放尺度的物体不会与非统一缩放尺度的物体进行批处理。使用缩放尺度(1,1,1)和 (1,2,1)的两个物体将不会进行批处理,但是使用缩放尺度(1,2,1)和(1,3,1)的两个物体将可以进行批处理。

Unity 的托管内存优化

  • 尽量不要动态的Instantiate和Destroy Object,使用Object Pool,也就是对象池

  • 以帧为单位来操作的函数里面,尽量不要进行堆栈分配内存。因为Unity使用C#语言托管了内存的释放,导致开发者不能选择在什么时间去释放内存。而如果使用C#的GC操作又会非常耗时,特别是在场景非常复杂的时候,它需要遍历场景中所有的对象。很多游戏你玩的过程当中发现游戏卡顿都是内存GC导致的卡顿。

  • 不要动态的产生、链接字符串。下面的代码是几个GC“噩梦”。 String的相加操作,会频繁申请内存并释放,导致gc频繁,使用System.Text.StringBuilder代替。

public string StringExample(int[] array)
{  
   string line = array[0].ToString();  
   for (i = 1; i < array.Length; i++) line += ", " + array[i].ToString(); 
   return line;  
}
  • 尽量减少函数调用栈。用x = (x > 0 ? x : -x);代替x = Mathf.Abs(x)

  • 尽量不要在函数中新建Array或者List,而尽量采用传入Array或者List再进行修改的方式。

  • 对实时更新要求不高的地方,Update处理可改为每N帧处理一次。如下:

void Update() 
{ 
  if(Time.frameCount % 5 == 0) //这里是每5帧处理一次
  { 
    // TODO Something
  }
}
  • 尽量不要在Update, FixedUpdate, LateUpdate等每帧处理的函数中开辟新变量。这点在上面也提到了,“尽量不要在以帧为单位来操作的函数里面,进行堆栈分配内存”。示例如下:
void Update() 
{ 
  int a,b,c;
}

改为:

int a,b,c;
void Update() {}
  • 主动回收垃圾,给某个 GameObject 绑上以下的代码。(但是这样做会有一些弊端,最优的情况是“每次GC回收的间隔越大越好,一个很好的项目应该做到500-1000帧进行一次GC”,但是,你要考虑到C#是会自动进行GC回收的,只有代码各方面最优的情况下,才能尽量少的进行GC。)
void Update() { if(Time.frameCount % 50 == 0) { System.GC.Collect(); } }
  • 尽量缓存用到的Componment组件,减少GetComponent的调用。每次GetComponent或使用内置组件访问器(如transform.Translate(0, 0, 5)中的transform就是内置组件访问器)会产生明显的开销。
void Update () {  
    transform.Translate(0, 0, 5);  
}  

//转换为:  

private Transform myTransform;  
void Awake () {  
    myTransform = transform;  
}  
void Update () {  
    myTransform.Translate(0, 0, 5);  
}
  • 适当使用结构(struct)来代替类(class)。结构变量主要存放在栈区而非堆区。因为栈的分配较快,并且不调用垃圾回收操作,所以当结构变量比较小时可以提升程序的运行性能。但是当结构体较大时,虽然它仍可避免分配/回收的开销,而它由于"传值"操作也会导致单独的开销,所以此时它会比等效对象类的效率低。

  • 尽量使用内置数组(也就是尽量使用string[]而不是List<string>),内置数组比ArrayList和List等快上非常多。虽然ArrayList或Array类很容易使用,你可以非常容易的进行添加删除修改等操作,但是内置数组会比ArrayList和List快上非常多。 内置数组是直接嵌入结构数据类型在一个紧密的缓存里,而不需要任何额外类型信息或其他开销。因此,在缓存中遍历它是非常容易的,因为每个元素都是对齐的。



今天就先写到这里,有空再更。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容