日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Unity3D 性能优化

發布時間:2023/12/10 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Unity3D 性能优化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

本文轉自:http://www.manew.com/blog-48504-3431.html

Unity性能優化

本文主要從三個方面進行說明:CPU、GPU、和內存。

一、CPU的方面的優化:

CPU優化方向:

1、DrawCalls

2、物理組件(Physics)

3、GC(GC為處理內存,此項為CPU使用GC處理內存時產生的性能損耗)

4、程序代碼

?

Drawcalls:

Drawcall是啥?其實就是對底層圖形程序(比如:OpenGL?ES)接口的調用,以在屏幕上畫出東西。

如何優化:

1)使用Draw?Call?Batching,也就是描繪調用批處理。Unity在運行時可以將一些物體進行合并,從而用一個描繪調用來渲染他們。

靜態批處理Static?Batching,只要是靜態不動的物體且具有相同材質的話就可以使用靜態批處理來降低描繪調用(注:shader不同則會增加紋理的拼合降低渲染效率)

動態批處理Dynamic?Batching:動態批處理是引擎自動進行,無需設置,當物體共享相同的材質,則引擎就會自動對Drawcall進行優化,也就是動態批處理(如實例化預制件)。動態批處理存在約束,稍有不慎就會增加Drawcall

動態批處理的約束:

1、批處理動態物體需要在每個頂點上進行一定的開銷,所以動態批處理僅支持小于900頂點的網格物體。

2、如果你的著色器使用頂點位置,法線和UV值三種屬性,那么你只能批處理300頂點以下的物體;如果你的著色器需要使用頂點位置,法線,UV0,UV1和切向量,那你只能批處理180頂點以下的物體。

3、不要使用縮放。分別擁有縮放大小(1,1,1)?和(2,2,2)的兩個物體將不會進行批處理。

4、統一縮放的物體不會與非統一縮放的物體進行批處理。

5、使用縮放尺度(1,1,1)?和?(1,2,1)的兩個物體將不會進行批處理,但是使用縮放尺度(1,2,1)?和(1,3,1)的兩個物體將可以進行批處理。

6、使用不同材質的實例化物體(instance)將會導致批處理失敗。

7、擁有lightmap的物體含有額外(隱藏)的材質屬性,比如:lightmap的偏移和縮放系數等。所以,擁有lightmap的物體將不會進行批處理(除非他們指向lightmap的同一部分)。

8、多通道的shader會妨礙批處理操作。比如,幾乎unity中所有的著色器在前向渲染中都支持多個光源,并為它們有效地開辟多個通道。

9、預設體的實例會自動地使用相同的網格模型和材質。

所以盡量使用靜態批處理。

2)NGUI和UGUI需將同一界面的UI元素打包圖集。

?

物理組件:

1)設置Fixed?timestep,減少物理計算次數,提高游戲性能。

2)減少FPS,在ProjectSetting->?Quality中的VSync?Count?參數會影響你的FPS,EveryVBlank相當于FPS=60,EverySecondVBlank?=?30;這兩種情況都不符合游戲的FPS的話,我們需要手動調整FPS,首先關閉垂直同步這個功能,然后在代碼的Awake方法里手動設置FPS(Application.targetFrameRate?=?45;)
降低FPS的好處:
1.省電,減少手機發熱的情況;
2.能都穩定游戲FPS,減少出現卡頓的情況。

關于垂直同步:

什么是垂直同步,簡單來說就是顯示器上的所有圖像都是一線一線掃描上去的,顯示器都有兩種同步參數,水平和垂直同步,水平同步信號決定畫出一條屏幕橫線的時間,垂直決定從屏幕頂部畫一條線到底部,再返回原位置的時間,垂直同步決定了顯示器的刷新水平,如果選擇了等待同步信號,但是比較強的顯卡會迅速繪制完一屏的圖像,但是沒有垂直同步信號的到達,顯示器無法繪制下一屏,所以操作系統刷新率會制約FPS,如果選擇不等待,即關閉垂直同步,那就會提升性能,發揮顯卡的最大性能,但是有個弊端,就是正是因為垂直同步的存在才使得游戲進程和顯示器刷新率同步,使得畫面變得更加平滑和穩定。因此是否關閉垂直同步提升幀數需要酌情考慮。

3)盡量不用MeshCollider

如果可以的話,盡量不用MeshCollider,以節省不必要的開銷。如果不能避免的話,盡量用減少Mesh的面片數,或用較少面片的代理體來代替。

4)粒子組件,屏幕上最大粒子數量建議小于200個,粒子應盡可能的小,關閉粒子的碰撞功能。

?

GC:

雖然GC是用來處理內存的,但是卻會增加CPU的開銷,首先我們要明確所謂的GC是Mono運行時的機制,而非引擎的機制,所以GC也主要是針對Mono的對象來說的,而它管理的也是Mono的托管堆。?搞清楚這一點,你也就明白了GC不是用來處理引擎的assets(紋理啦,音效啦等等)的內存釋放的,因為U3D引擎也有自己的內存堆而不是和Mono一起使用所謂的托管堆。其次我們要搞清楚什么東西會被分配到托管堆上?不錯咯,就是引用類型咯。比如類的實例,字符串,數組等等。而作為int,float,包括結構體struct其實都是值類型,它們會被分配在堆棧上而非堆上。所以我們關注的對象無外乎就是類實例,字符串,數組這些了。所以GC的優化說白了也就是代碼的優化。

此部分的代碼優化只針對是否會觸發GC:

1)字符串處理。如頻繁操作單個字符串使用StringBuilder

2)盡量不要使用foreach,而是使用for。foreach其實會涉及到迭代器的使用,而據傳說每一次循環所產生的迭代器會帶來24?Bytes的垃圾。那么循環10次就是240Bytes。

3)不要直接訪問gameobject的tag屬性。比如if?(go.tag?==?“human”)最好換成if?(go.CompareTag?(“human”))。因為訪問物體的tag屬性會在堆上額外的分配空間。如果在循環中這么處理,留下的垃圾就可想而知了。

4)使用“池”,以實現空間的重復利用。

5)最好不用LINQ的命令,因為它們會分配臨時的空間,同樣也是GC收集的目標。

?

代碼:

1)不要頻繁使用GetComponent去頻繁獲取組件,如使用可在Awake函數中持有引用。

2)善于使用OnBecameVisible()和OnBecameVisible()來控制物體的Update()函數的執行以減少開銷。

3)使用內建數組如使用Vector3.zero而不是new?Vector(0,0,0);

4)數組、集合類元素優先使用Array,其次是List;

5)腳本在不使用時腳本禁用之,需要時再啟用;

6)可以使用Ray來代替OnMouseXXX類方法

7)盡量少用模運算和除法運算,比如a/5f,一定要寫成a*0.2f

8)不要使用原生的GUI方法

?

二、GPU的優化:

GPU優化方向:

1.填充率,可以簡單的理解為圖形處理單元每秒渲染的像素數量。

2.像素的復雜度,比如動態陰影,光照,復雜的shader等等

3.幾何體的復雜度(頂點數量)

4.當然還有GPU的顯存帶寬

針對以上4點,可以發現影響GPU性能的無非就是兩個方面,一是定點過多,即模型復雜面數多,另一個就是GPU的顯存帶寬。

?

減少繪制數目:

1)保持材質的數目盡可能少。這使得Unity更容易進行批處理。

2)使用紋理圖集(一張大貼圖里包含了很多子貼圖)來代替一系列單獨的小貼圖。它們可以更快地被加載,具有很少的狀態轉換,而且批處理更友好。

3)如果使用了紋理圖集和共享材質,使用Renderer.sharedMaterial?來代替Renderer.material?。

4)使用光照紋理(lightmap)而非實時燈光。

5)使用LOD,好處就是對那些離得遠,看不清的物體的細節可以忽略。

6)遮擋剔除(Occlusion?culling)

優化顯存帶寬:

1)壓縮圖片,減小顯存帶寬的壓力。

2)使用mipmap。

Mipmap中每一個層級的小圖都是主圖的一個特定比例的縮小細節的復制品。因為存了主圖和它的那些縮小的復制品,所以內存占用會比之前大。但是為何又優化了顯存帶寬呢?因為可以根據實際情況,選擇適合的小圖來渲染。所以,雖然會消耗一些內存,但是為了圖片渲染的質量(比壓縮要好),這種方式也是推薦的。

?

三、內存的優化:

Unity中的內存種類

實際上Unity游戲使用的內存一共有三種:程序代碼、托管堆(Managed?Heap)以及本機堆(Native?Heap)。

程序代碼包括了所有的Unity引擎,使用的庫,以及你所寫的所有的游戲代碼。在編譯后,得到的運行文件將會被加載到設備中執行,并占用一定內存。這部分內存實際上是沒有辦法去“管理”的,它們將在內存中從一開始到最后一直存在。一個空的Unity默認場景,什么代碼都不放,在iOS設備上占?用內存應該在17MB左右,而加上一些自己的代碼很容易就飆到20MB左右。想要減少這部分內存的使用,能做的就是減少使用的庫,稍后再說。

托管堆是被Mono使用的一部分內存。Mono項目一個開源的.net框架的一種實現,對于Unity開發,其實充當了基本類庫的角色。托管堆用來存放類的實例(比如用new生成的列表,實例中的各種聲明的變量等)。“托管”的意思是Mono“應該”自動地改變堆的大小來適應你所需要的內存,并且定時地使用垃圾回收(GC)來釋放已經不需要的內存。關鍵在于,有時候你會忘記清除對已經不需要再使用的內存的引用,從而導致Mono認為這塊內存一直有用,而無法回收。

本機堆是Unity引擎進行申請和操作的地方,比如貼圖,音效,關卡數據等。Unity使用了自己的一套內存管理機制來使這塊內存具有和托管堆類似的功能。基本理念是,如果在這個關卡里需要某個資源,那么在需要時就加載,之后在沒有任何引用時進行卸載。聽起來很美好也和托管堆一樣,但是由于Unity有一套自動加載和卸載資源的機制,讓兩者變得差別很大。自動加載資源可以為開發者省不少事兒,但是同時也意味著開發者失去了手動管理所有加載資源的權力,這非常容易導致大量的內存占用(貼圖什么的你懂的),也是Unity給人留下“吃內存”印象的罪魁禍首。

?

優化程序代碼的內存占用

這部分的優化相對簡單,因為能做的事情并不多:主要就是減少打包時的引用庫,改一改build設置即可。

對于一個新項目來說不會有太大問題,但是如果是已經存在的項目,可能改變會導致原來所需要的庫的缺失(雖說一般來說這種可能性不大),因此有可能無法做到最優。

當使用Unity開發時,默認的Mono包含庫可以說大部分用不上,在Player?Setting(Edit->Project?Setting->Player或者Shift+Ctrl(Command)+B里的Player?Setting按鈕)面板里,將最下方的Optimization欄目中“Api?Compatibility?Level”選為.NET?2.0?Subset,表示你只會使用到部分的.NET?2.0?Subset,不需要Unity將全部.NET的Api包含進去。接下來的“Stripping?Level”表示從build的庫中剝離的力度,每一個剝離選項都將從打包好的庫中去掉一部分內容。你需要保證你的代碼沒有用到這部分被剝離的功能,選為“Use?micro?mscorlib”的話將使用最小的庫(一般來說也沒啥問題,不行的話可以試試之前的兩個)。庫剝離可以極大地降低打包后的程序的尺寸以及程序代碼的內存占用,唯一的缺點是這個功能只支持Pro版的Unity。

這部分優化的力度需要根據代碼所用到的.NET的功能來進行調整,有可能不能使用Subset或者最大的剝離力度。如果超出了限度,很可能會在需要該功能時因為找不到相應的庫而crash掉(iOS的話很可能在Xcode編譯時就報錯了)。比較好地解決方案是仍然用最強的剝離,并輔以較小的第三方的類庫來完成所需功能。一個最常見問題是最大剝離時Sysytem.Xml是不被Subset和micro支持的,如果只是為了xml,完全可以導入一個輕量級的xml庫來解決依賴(Unity官方推薦這個)。

關于每個設定對應支持的庫的詳細列表,可以在這里找到。關于每個剝離級別到底做了什么,Unity的文檔也有說明。

實際上,在游戲開發中絕大多數被剝離的功能使用不上的,因此不管如何,庫剝離的優化方法都值得一試。

?

托管堆優化

首先需要明確,托管堆中存儲的是你在你的代碼中申請的內存(不論是用js,還是C#寫的)。一般來說,無非是new或者Instantiate兩種生成object的方法(事實上Instantiate中也是調用了new)。在接收到請求后,托管堆在其上為要新生成的對象實例以及其實例變量分配內存,如果可用空間不足,則向系統申請更多空間。

當你使用完一個實例對象之后,通常來說在腳本中就不會再有對該對象的引用了(這包括將變量設置為null或其他引用,超出了變量的作用域,或者對Unity對象發送Destory())。在每隔一段時間,Mono的垃圾回收機制將檢測內存,將沒有再被引用的內存釋放回收。總的來說,你要做的就是在盡可能早的時間將不需要的引用去除掉,這樣回收機制才能正確地把不需要的內存清理出來。但是需要注意在內存清理時有可能造成游戲的短時間卡頓,這將會很影響游戲體驗,因此如果有大量的內存回收工作要進行的話,需要盡量選擇合適的時間。

如果在你的游戲里,有特別多的類似實例,并需要對它們經常發送Destroy()的話,游戲性能上會相當難看。比如小熊推金幣中的金幣實例,按理說每枚金幣落下臺子后都需要對其Destory(),然后新的金幣進入臺子時又需要Instantiate,這對性能是極大的浪費。一種通常的做法是在不需要時,不摧毀這個GameObject,而只是隱藏它,并將其放入一個重用數組中。之后需要時,再從重用數組中找到可用的實例并顯示。這將極大地改善游戲的性能,相應的代價是消耗部分內存,一般來說這是可以接受的。

如果不是必要,應該在游戲進行的過程中盡量減少對GameObject的Instantiate()和Destroy()調用,因為對計算資源會有很大消耗。在便攜設備上短時間大量生成和摧毀物體的話,很容易造成瞬時卡頓。如果內存沒有問題的話,盡量選擇先將他們收集起來,然后在合適的時候(比如按暫停鍵或者是關卡切換),將它們批量地銷毀并?且回收內存。Mono的內存回收會在后臺自動進行,系統會選擇合適的時間進行垃圾回收。在合適的時候,也可以手動地調用?System.GC.Collect()來建議系統進行一次垃圾回收。

要注意的是這里的調用真的僅僅只是建議,可能系統會在一段時間后在進行回收,也可能完全不理會這條請求,不過在大部分時間里,這個調用還是靠譜的。

?

本機堆的優化

當你加載完成一個Unity的scene的時候,scene中的所有用到的asset(包括Hierarchy中所有GameObject上以及腳本中賦值了的的材質,貼圖,動畫,聲音等素材),都會被自動加載(這正是Unity的智能之處)。也就是說,當關卡呈現在用戶面前的時候,所有Unity編輯器能認識的本關卡的資源都已經被預先加?入內存了,這樣在本關卡中,用戶將有良好的體驗,不論是更換貼圖,聲音,還是播放動畫時,都不會有額外的加載,這樣的代價是內存占用將變多。Unity最?初的設計目的還是面向臺式機,幾乎無限的內存和虛擬內存使得這樣的占用似乎不是問題,但是這樣的內存策略在之后移動平臺的興起和大量移動設備游戲的制作中出現了弊端,因為移動設?備能使用的資源始終非常有限。因此在面向移動設備游戲的制作時,盡量減少在Hierarchy對資源的直接引用,而是使用Resource.Load的方?法,在需要的時候從硬盤中讀取資源,在使用后用Resource.UnloadAsset()和Resources.UnloadUnusedAssets()盡快將其卸載掉。總之,這里是一個處理時間和占用內存空間的trade?off,如何達到最好的效果沒有標準答案,需要自己權衡。

在關卡結束的時候,這個關卡中所使用的所有資源將會被卸載掉(除非被標記了DontDestroyOnLoad)的資源。注意不僅是DontDestroyOnLoad的資源本身,其相關的所有資源在關卡切換時都不會被卸載。DontDestroyOnLoad一般被用來在關卡之間保存一些玩家的狀態,比如分數,級別等偏向文?本的信息。如果DontDestroyOnLoad了一個包含很多資源(比如大量貼圖或者聲音等大內存占用的東西)的話,這部分資源在場景切換時無法卸?載,將一直占用內存,這種情況應該盡量避免。

另外一種需要注意的情況是腳本中對資源的引用。大部分腳本將在場景轉換時隨之失效并被回收,但是,在場景之間被保持的腳本不在此列(通常情況是被附?著在DontDestroyOnLoad的GameObject上了)。而這些腳本很可能含有對其他物體的Component或者資源的引用,這樣相關的?資源就都得不到釋放,這絕對是不想要的情況。另外,static的單例(singleton)在場景切換時也不會被摧毀,同樣地,如果這種單例含有大量的對資源的引用,也會成為大問題。

因此,盡量減少代碼的耦合和對其他腳本的依賴是十分有必要的。如果確實無法避免這種情況,那應當手動地對這些不再使用的引用對象調用Destroy()或者將其設置為null。這樣在垃圾回收的時候,這些內存將被認為已經無用而被回收。

需要注意的是,Unity在一個場景開始時,根據場景構成和引用關系所自動讀取的資源,只有在讀取一個新的場景或者reset當前場景時,才會得到清理。

因此這部分內存占用是不可避免的。在小內存環境中,這部分初始內存的占用十分重要,因為它決定了你的關卡是否能夠被正常加載。因此在計算資源充足或是關卡開始之后還有機會進行加載時,盡量減少Hierarchy中的引用,變為手動用Resource.Load,將大大減少內存占用。在?Resource.UnloadAsset()和Resources.UnloadUnusedAssets()時,只有那些真正沒有任何引用指向的資源?會被回收,因此請確保在資源不再使用時,將所有對該資源的引用設置為null或者Destroy。

同樣需要注意,這兩個Unload方法僅僅對Resource.Load拿到的資源有效,而不能回收任何場景開始時自動加載的資源。與此類似的還有?AssetBundle的Load和Unload方法,靈活使用這些手動自愿加載和卸載的方法,是優化Unity內存占用的不二法則~

總結

以上是生活随笔為你收集整理的Unity3D 性能优化的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。