SetPass calls表示在當前攝像機的渲染過程中,Unity切換著色器通道(Shader Pass)來渲染游戲對象的次數。一個著色器(Shader)可以包含多個著色器通道,每個著色器通道可以通過不同的方式來渲染游戲對象。但每次切換著色器通道都會消耗一定的性能,應盡量避免這項數據過大。
大地圖的分塊加載。場景管理器,子場景管理器,整個大地圖是一個數組,里面存著所有的子場景,創建子場景。最多加載四個子場景。
不規則地圖的解封處理,美術在開發大地圖時的工作流程。
玩家的位置和地圖中心點的位置,小于某個值(10),就把地圖加載出來,小于某個值(10)卸載。
場景模型優化,CPU,GPU。CPU從內存讀取數據(比如模型)發出指令,從RAM內存到顯卡的AGP端口。顯卡把數據放進顯存。顯卡的運算單元叫GPU。最后送到顯示器。
硬件機理,
優化總體原則,
場景優化:
??????? 對始終靜止不動的游戲對象使用靜態合批技術。
??????? 盡量使用同一個材質,以便使用動態合批技術。
??????? 使用GPU Instancing技術。
??????? 使用遮擋剔除。
??????? 進入游戲后的第一個場景要盡量簡單,這樣可以減少游戲的啟動時間。可以先進一個簡單的場景,再進行異步加載,之后再進入游戲的主要的場景。
??????? 盡量避免Hierarchy窗口的層級結構過深。例如一個物體有很多個子物體,這些子物體又有其它子物體,這些子物體又有其它子物體,繼續這樣下去就會導致層級結構過深,我們應盡量減少這種情況。
??????? Edit——Project Settings——Quality,可以對不同平臺中游戲的品質進行設置。
??????? 如果使用了后期處理技術,例如Post Processing等插件,調整屏幕效果的屬性,不要使用太絢麗的特效,可以優化性能。
??????? 要優化Terrain地形,可以使用Unity資源商店的插件,例如Terrain To Mesh插件可以把地形烘焙成網格。
??????? 場景要盡可能簡單,盡量多使用預制體,用代碼動態創建它們出來,并管理它們。
合批的材質如果想修改,修改共享材質,Render,shareMaterial來保證材質共享狀態。
一、靜態批處理(房子建筑山)
定點數據和三角形索引(三角形構成)數據。
對于始終靜止不動的物體使用靜態合批后,CPU會把它們合并為一個批次發送給GPU處理,這樣可以減少Draw Call帶來的性能消耗,從而提升游戲性能。
要使用靜態合批,必須確保Edit——Project Settings——Player——Other Settings——Static Batching是勾選的。
把一個物體設置為靜態的方法:
選中該物體,點擊在Inspector窗口右上角的Static右方的下拉菜單,選擇Batching Static。
使用靜態合批雖然可以提升游戲性能,但是設置為靜態的物體在整個游戲中就不能再運動了,強行使它們運動會出問題。
房屋樹木石頭
二、動態合批
動態合批默認是由Unity自動完成。可以在Edit——Project Settings——Player——Other Settings——Dynamic Batching查看。默認Dynamic Batching是勾選的,當條件滿足時,Unity會自動對使用了相同材質(Material)的物體進行動態合批。如果取消勾選,則不會進行動態合批。
1、Unity不能對包含超過900個頂點屬性和225個頂點的網格應用動態批處理。這是因為網格的動態批處理對每個頂點都有開銷。例如,如果你的著色器使用頂點位置、頂點法線和單個UV,那么Unity最多可以批處理225個頂點。然而,如果你的著色器使用頂點位置、頂點法線、UV0、UV1和頂點切線,那么Unity只能批處理180個頂點。
??????? 2、如果GameObjects使用不同的材質實例,Unity就不能將它們批處理在一起,即使它們本質上是相同的。唯一的例外是陰影施法者的渲染。
??????? 3、帶有光貼圖的游戲對象有額外的渲染參數。這意味著,如果你想批處理光照貼圖的游戲對象,它們必須指向相同的光照貼圖位置。
??????? 4、Unity不能完全將動態批處理應用于使用多通道著色器的GameObjects。
幾乎所有的Unity著色器都支持正向渲染中的多個光源。為了實現這一點,他們為每個光處理一個額外的渲染通道。Unity只批處理第一個渲染通道。它不能批處理額外的逐像素燈光的繪制調用。
遺留延遲渲染路徑不支持動態批處理,因為它在兩個渲染通道中繪制GameObjects。第一個通道是燈光預通道,第二個通道渲染GameObjects。
其中我們要注意的是,物體必須使用相同的材質,才有可能成功進行動態合批。
使用動態合批往往能減少CPU和GPU的開銷,提升游戲性能,但同時也會占用一定的內存。
三、GPU Instancing(樹木草地石塊)
使用GPU Instancing可以在一個Draw Call中同時渲染多個相同或類似的物體,從而減少CPU和GPU的開銷。
要啟用GPU Instancing,我們可以選中一個材質,然后在Inspector窗口勾選Enable GPU Instancing,這樣就可以了。
但是即使勾選了Enable GPU Instancing,也不一定會成功。
要成功使用GPU Instancing進行優化,游戲對象必須同時滿足以下條件:
1、使用相同的材質和網格。
2、材質的著色器必須支持GPU Instancing。例如標準著色器和表面著色器就支持GPU Instancing。
3、網格的頂點布局和著色器必須相同。如果網格的頂點布局或著色器不同,那么它們就無法被合并成一個實例。
4、每個實例需要有不同的變換信息(例如位置、旋轉、縮放)。雖然多個實例可以使用相同的材質和網格,但是它們必須擁有不同的變換信息才能被正確地實例化并渲染出來。
????? 另外需要注意的是,GPU Instancing與SRP Batcher不兼容。如果項目使用了SRP Batcher,并且配置為優先使用SRP Batcher而不是GPU實例化,啟用GPU實例化可能不會生效。SRP Batcher是Unity提供的一種渲染優化技術,它可以將多個網格合并成單個批次進行渲染,從而提高性能。在這種情況下,GPU實例化將被忽略。
使用GPU Instancing往往能減少CPU和GPU的開銷,提升游戲性能,但同時也會占用一定的內存。
是否要啟用GPU Instancing,要根據自己的項目來定。可以嘗試啟用,在性能分析器中看看效果如果,如果效果好,再確定啟用它。
一般來說,當場景中有大量重復的網格實例時,可以嘗試啟用GPU Instancing。例如場景中有大量樹木、草地、石塊等,這些實例具有相同的網格和材質,只是位置、顏色等屬性稍有差異,那么啟用GPU Instancing或許能夠顯著提高性能。
四、遮擋剔除
正常情況下,如果一個障礙物A擋住了后面的物體B,雖然我們看不見物體B,但是Unity仍然會消耗性能來渲染這個物體B。這樣CPU和GPU就會有一部分性能白白浪費在渲染物體B身上。
如果想在一個障礙物擋住了后面的物體后,不渲染被擋住的物體,則可以使用遮擋剔除。
以一堵墻擋住幾個小球為例,選中這堵墻,在Inspector窗口右上角的Static右側的下拉菜單處選擇Occluder Static,則這堵墻就是遮擋物。分別選中這些小球,在Inspector窗口右上角的Static右側的下拉菜單處選擇Occludee Static,則這些小球就是被遮擋物。
選中攝像機,要確保它啟用了Occlusion Culling屬性。
創建遮擋區域的方法:
??????? 方法1、打開Occlusion Culling窗口。打開方法:Window——Rendering——Occlusion Culling——Bake。打開之后,選擇Object選項卡,點擊Occlusion Areas,點擊Create New右側的Occlusion Area。
??????? 方法2、創建一個空物體,在它身上添加Occlusion Area組件。
Occlusion Area組件的Size決定了遮擋剔除區域的范圍,它越大,烘焙之后生成的遮擋剔除區域就越大。Center控制遮擋區域中心點的世界坐標。Is View Volume表示是否定義視圖體積,只有啟用了這個選項,Occlusion Area組件才可能生效。
之后,要讓遮擋剔除生效,還要在Occlusion Culling窗口的Bake選項卡中點擊右下方的Bake按鈕,進行烘焙,遮擋剔除才可能生效。而且以后每次調整完場景的遮擋物、被遮擋物、Occlusion Area組件的范圍,都要這樣烘焙一次。如果點擊旁邊的Clear按鈕,則會清除之前烘焙的數據。
Portal組件的遮擋范圍。每次調整完,或者修改過場景,都要重新烘焙。
五、光照優化
減少光源的數量
攝像機離光源近,啟動光源,攝像機離光源遠,禁用光源。
烘焙光照
光照Light組件Type設置為Point;
參與烘焙的物體比如墻,地面的static屬性設置為Contribute GI,
點擊Window===》Redering===》Lighting,點擊New Lighting Settings改名MyLightSettings;點擊Generate Lighting。烘焙比較慢,最好空閑時間烘焙,中午吃飯或者晚上烘焙。
烘焙之前Batches:26,烘焙之后Batches:16。三角面從4.3K變成3k,頂點從8.1k到6.6k。
光照優化
??????? 減少光源。
??????? 調節好每個Light組件的屬性,平衡視覺效果和游戲性能。
??????? 盡量不要用實時光照,而是考慮用烘焙光照或者混合光照,此時可以配合光照探針使用。Lighting窗口可以設置烘焙光照的參數。
??????? 減少啟用的陰影投射。
??????? 根據攝像機距離光源的距離,用腳本來決定是否啟用光源和陰影。但是這樣就會花費一些性能來計算攝像機到光源的距離。
??????? 可以考慮設置光照的陰影。無陰影的性能最好,硬陰影的性能稍差,軟陰影的視覺效果最好,但是性能是這三者中最差的。
??????? (備注:光照和陰影最影響項目的性能,其次才是模型網格和貼圖。把實時光照改成烘焙光照,可以使游戲性能大幅度增加。)
??????? 注意MeshRenderer組件上的屬性,默認情況下,Unity 會啟用陰影投射和接收、光照探針采樣、反射探針采樣和運動矢量計算。如果項目不需要這些功能中的一個或多個,請確保關閉它們。2D游戲尤其要注意,往往都不需要它們。
??????? 遠處的景物,如果確定玩家無法到達,則可以不用模型,而是把遠處的景物做成一張貼圖放到天空盒的材質中,給天空盒使用。也可以使用反射探針烘焙出一張貼圖,然后放到天空盒的材質。
六、圖片的優化
導入圖片之前的優化:
如果這張圖片是應用在移動端的,則導入Unity前,可以對這張圖的每條邊進行調整,確保每條邊的長度都是2的正整數次方個像素。例如2、4、8、16...256、512、1024、2048、4096...。這個做法只對移動端有效。
圖片導入Unity后,可以選中這張圖片,在Inspector窗口設置它的屬性。設置這些屬性,可以在發布不同的平臺,分別對該圖片進行相應的壓縮。可以在合理的范圍內減小Max Size,對于許多移動端的游戲,2048x2048 或 1024x1024 足以滿足紋理圖集的要求,而 512x512 足以滿足應用于3D模型的紋理的要求。如果圖片不需要讀寫,則可以取消勾選Read/Write Enabled,如果勾選可能導致雙倍的內存占用。Filter Mode一般選擇Bilinear即可平衡性能和視覺效果,如果選擇Point(no filter),則視覺效果不太行,但性能開銷也小,如果選擇Trilinera,則視覺效果最好,但性能開銷最大。Aniso Level一般選擇1,只有個別比較重要的圖片才需要設置為大于等于2的值。
??????? 圖片導入Unity后,會默認生成Mip Maps格式。當攝像機到這幅貼圖距離近,則顯示最原始的圖片,當攝像機距離這幅貼圖的距離遠,則這幅貼圖會變模糊,以此降低渲染的性能消耗。但由于之前顯示的一幅圖,現在變成了有多幅,所以這樣會略微增加內存消耗。如果確定本游戲的攝像機到圖片的距離幾乎不怎么變化,則可以禁用這個功能。點擊該貼圖,在Inspector面板的Advanced中取消勾選Generate Mip Maps,這樣就不會生成Mip Maps,增加游戲性能。如果是2D游戲則可以禁用這個功能。如果是UI貼圖,也可以禁用這個功能。
??????? 圖片導入Unity后,可以選中這張圖片,在Inspector窗口設置它在各個平臺的Format和Compressor Quality。
Format可以參考官方文檔:https://docs.unity3d.com/2021.3/Documentation/Manual/class-TextureImporterOverride.html
七、UI的優化
如果一個UGUI的控件不需要進行射線檢測,則可以取消勾選Raycast Target。
盡量避免使用完全透明的圖片和UI控件。因為即使完全透明,我們看不見它,但它仍然會產生一定的性能開銷。如果UI中一定要用到很多張完全透明的圖片,則建議把這些完全透明的圖片由單獨的攝像機進行渲染,且這些UI不要疊加到場景攝像機的渲染范圍內。
盡量避免UI控件的重疊。如果多個UI有重疊的部分,則會稍微增加一些額外的計算和渲染的開銷。雖然這部分開銷通常是非常小的,但我們最好也盡量避免這種情況。
??????? UI的文字使用TextMeshPro比使用Text的性能更好。但是TextMeshPro對中文的支持不太好。盡量避免UI控件的重疊。如果多個UI有重疊的部分,則會稍微增加一些額外的計算和渲染的開銷。雖然這部分開銷通常是非常小的,但我們最好也盡量避免這種情況。
??????? UI的文字使用TextMeshPro比使用Text的性能更好。但是TextMeshPro對中文的支持不太好。
八、模型優化
模型導入Unity后,可以選中這個模型,在Inspector窗口設置它的屬性。在Model選項卡,啟用Mesh Compression可以壓縮模型,壓縮程度越高,模型精度越低,但是模型也會節省一些空間。如果該模型不需要用代碼來讀寫,則可以取消勾選Read/Write Enabled。設置Optimize Game Objects可以優化模型。如果該模型不需要使用法線,則可以把Normals設置為None。如果該模型不需要用混合變形法線,則可以把Blend Shape Normals設置為None。如果該模型不需要使用切線,則可以把Tangents設置為None。如果該模型不需要用光照UV貼圖,則可以取消勾選Swap UVs和Generate Lightmap UVs。
????????對于Rig選項卡,Animation Type如果選擇Generic Rig會比Humanoid Rig性能更好,但是一般使用Humanoid Rig是為了對人型的角色進行動畫重定向,所以要根據自己的情況來選擇。如果模型不需要使用動畫,例如一些完全不會動的石頭等物體,則可以將Animation Type選擇為None。Skin Weights默認是4,對于一些不重要的動畫對象,本變量可以設置為1,這樣可以節省計算量。建議勾選Optimize Bones,這樣會自動剔除沒有蒙皮頂點的骨骼。勾選Optimize Game Object可以提高角色動畫的性能,但是在某些情況下可能會導致角色動畫出現問題,是否勾選要看動畫效果而定。如果角色模型是可以換裝的,則在導入該模型后不要勾選這個選項,而可以在游戲運行時,該角色換裝后,通過AnimatorUtility.OptimizeTransformHierarchy來勾選這個選項。
????????對于Animation選項卡,如果模型不需要使用動畫,則可以取消勾選Import Animation。對于Animation選項卡,設置Anim.Compression可以調整動畫的壓縮方式,Off表示不壓縮動畫,這樣動畫文件可能會占用較大的空間,但是在運行時不會有任何信息損失,Keyframe Reduction表示使用關鍵幀算法來壓縮動畫,這樣會顯著減小動畫文件的大小,同時保持相對較高的動畫質量,Optimal表示會盡可能高地壓縮網格,但是這樣也會導致壓縮時間增加。
????????對于Materials選項卡,如果使用Untiy的默認材質,則可以把Material Creation Mode設置為None。
??????? Edit——Project Settings——Player——勾選Optimize Mesh Data,這樣一來,Unity會在構建的時候中對網格數據進行優化處理,以達到提高游戲性能的效果。但是這樣往往會修改網格,我們勾選之后應該要進行測試,確保沒有問題,再確定啟用它。
??????? 用LOD技術,使用Unity自帶的LOD Group組件,并根據項目的情況來調整該組件的屬性。Untiy資源商店也有一些其它的LOD插件。
??????? 把多個模型的網格合并為一個網格。可以使用自己寫代碼,使用Unity自帶的CombineMeshes方法,也可以使用資源商店的插件,在資源商店搜Mesh Combine可以搜索到相關的插件,例如Easy Mesh Combine Tool等插件。
??????? 減少模型的頂點、面、材質、骨骼、蒙皮網格。這一般由美術人員來完成。
九、LOD Group
????????LOD的原理,就是我們可以為一個游戲對象設定多個模型,這些模型消耗的游戲性能由高到低排列。會根據攝像機距離模型的遠近自動顯示對應的模型。
???????? 近的時候顯示最精細的模型,距離中等的時候顯示沒那么精細的模型,遠的時候顯示粗糙的模型,最遠的時候可以隱藏該模型。
????????使用LOD技術能起到優化渲染性能的效果。但是使用LOD技術也會增加內存占用。
????????在Unity中可以使用LODGroup組件來實現LOD技術。
????????LOD級別LOD 0、LOD 1、LOD 2分別表示攝像機從近處看、從中等距離處看、從遠處看時,所使用的模型的信息。Culled表示不渲染該模型。
????????點開下方的LOD 0、LOD 1、LOD 2,點擊+號可以添加在這種情況下要顯示的模型。
把LODGroup組件添加在一個空物體身上。這個空物體身上不添加MeshRenderer組件,也不添加MeshFilter組件,但可以添加碰撞器。如果要添加剛體、腳本等,也可以添加到這個空物體身上的。
???????? 這個空物體要渲染的每一個模型都要作為它的子物體,它們的身上要添加MeshRenderer組件和MeshFilter組件,用于渲染這個模型,但是不要給它們添加碰撞器。
???????? 在該空物體的LODGroup組件中,點擊選中要設置的LOD級別,在Renderers下方點擊Add可以選擇要顯示的游戲對象,點擊-號可以移除該游戲對象。
????????Edit——Project Settings——Quality中有控制整個項目LOD的參數。
???????? LOD Bias的值小,則攝像機離物體的距離稍微有些變化,則不同的LOD級別就會切換。如果LOD Bias的值大,則攝像機需要與物體有比較大的距離變化,不同的LOD級別才會切換。
十、合并網格
合并網格就是把多個網格合并為一個整體,從而提升游戲性能。
但是這樣一來,我們就不能單獨控制這些網格運動了,只能控制合并后的整個網格運動。而且合并的網格使用的材質必須是相同的,合并才可能成功,否則合并之后依然不能提升性能。
可以在Unity資源商店搜Mesh Combine,下載相應的插件來實現合并網格的功能,例如Easy Mesh Combine Tool等插件。
??????? Easy Mesh Comline Tool插件,Package Manager。從資源商店導入插件。
Window===》Easy Mesh Combine Tool 打開之后點擊Make Group and
Combile。
十一、動畫優化
????????設置Animator組件的Culling Mode。Always Animate表示如果該動畫不可見,也會播放它。Cull Update Transformations表示如果該動畫不可見,則不會渲染該動畫,但是依然會根據該動畫的播放來改變游戲對象的位置、旋轉、縮放,這樣是常用的選項。Cull Completely表示完全不會播放該動畫,不但不會渲染該動畫,而且也不會改變游戲對象的位置、旋轉、縮放。
????????
????????禁用SkinMesh Renderer組件的Update When Offscreen可以讓角色在不可見的時候動畫不更新,這樣可以減少計算量,提升性能。
??????? 對于Animator組件,可以使用Animator.StringToHash方法獲得指定字符串的哈希值,再把它作為參數傳入Animator型對象.GetXXX方法和Animator型對象.SetXXX方法中進行使用。
??????? int hash = Animator.StringToHash("Jumping");
??????? animator.SetBool(hash,true);
??????? 不用的Animation組件和Animator組件可以考慮刪掉,因為只要它們存在,就會消耗性能來檢測當前的狀態和過渡條件。
????????
一些簡單的動畫可以使用DoTween、iTween等插件實現,而不需要每個動畫都用Animator來實現。
十二、音頻優化
????????Unity支持后綴為.wav、.ogg、.mp3的音頻文件,但建議使用.wav,因為Unity對它的支持特別好。注意:Unity在構建項目時總是會自動重新壓縮音頻文件,因此無需刻意提前壓縮一個音頻文件再導入Unity,因為這樣只會降低該音頻文件最終的質量。
??????? 把音頻文件導入Unity后,選中它,可以在Inspector窗口設置它的屬性。勾選Force To Mono,這樣就會把這個音頻文件設置為單聲道。可以節省該資源所占據的空間。因為很少有移動設備實際配備立體聲揚聲器。在移動平臺項目中,將導入的音頻剪輯強制設置為單聲道會使其內存消耗減半。此設置也適用于沒有立體聲效果的任何音頻,例如大多數UI聲音效果。
????????對于Load Type選項,小文件(小于200kb)選擇Decompress on Load,中等大小的文件(大于等于200kb)選擇Compressed In Memory,比較大的文件(如背景音樂)選擇Streaming。????????????????
????????對于Compression Format的選項,PCM表示不壓縮,Vorbis表示壓縮,但也會盡量保證音頻的質量,ADPCM表示壓縮,且壓縮的程度比Vobis更高。由于PCM不會壓縮音頻,所以占用的空間大,應盡量少用,長時間的音頻文件可以使用Vorbis,短時間的音頻文件可以使用ADPCM。
Sample Rate Setting用于控制音頻文件的采樣率,對于移動平臺,采樣率不需要太高,建議選擇Override Sample Rate,然后在下方的Sample Rate選擇22050Hz,一般這樣就夠用了。
十三、物理優化
????????使用簡單的碰撞器進行碰撞檢測,如球體碰撞器、盒子碰撞器、膠囊體碰撞器,少用網格碰撞器等復雜的碰撞器。即使用多個簡單的碰撞器組合在一起,也往往比使用網格碰撞器的性能要好。
??????? 如果要把多個碰撞器組合成一個碰撞器,可以用復合碰撞器。
????????
????????如果同一個功能既可以用碰撞器來做,也可以用觸發器來做,則往往使用觸發器來做,性能更好。
??????? 盡量減少剛體組件,因為剛體組件的物理計算較多。
??????? 如果勾選剛體組件的Is Kinematic,則性能會有所提高。但這樣一來,這個剛體只會給別的剛體施加力,自己不會受到別的剛體施加的力的作用。
??????? Edit——Project Settings——Player——勾選Optimization下方的Prebake Collision Meshes,可以提高碰撞的效率,但是構建游戲的時間會增長。
??????? Edit——Project Settings——Physics或者Physics 2D——設置Layer Collision Matrix。它規定了哪些Layer層的游戲對象可以彼此碰撞,哪些Layer層的游戲對象會忽略碰撞。如果有些Layer層的游戲對象之前不需要進行碰撞,則可以在這里設置,取消勾選則表示不會碰撞。
??????? Edit——Project Settings——Time——稍微調大Fixed Timestep,這樣可以稍微提升游戲性能,但是物體的運動可能會出現問題。
十四、代碼優化
代碼優化:
??????? 使用AssetBundle作為資源加載方案。而且經常一起使用的資源可以打在同一個AssetBundle包中。盡量避免同一個資源被打包進多個AB包中。壓縮方式盡量使用LZ4,少用或不要用LZMA的壓縮方式。
????????如果確定后續開發不會升級Unity版本,則可以嘗試啟用打包選項BuildAssetBundleOption.DisableWriteType,這樣TypeTree信息不會被打到AB包中,可以極大減小包體大小以及運行加載時的內存開銷。
??????? 使用AssetBundle或者Addressables加載的資源,如果不使用,要記得卸載它們,否則會造成內存泄漏。
??????? 不用的資源要釋放掉,不用的引用類型的變量也要賦值為null,不要讓它們一直占著內存中。
??????? 加載資源時盡量使用異步加載。
?????? ?
??????? 頻繁創建和銷毀對象,可以使用對象池。
?????? ?
??????? 切換場景時,舊的場景要釋放掉,不用的資源也可以考慮釋放掉,也可以考慮用System.GC.Collect來進行一次垃圾回收。
??????? 鎖定游戲的幀率 。幀率為30,游戲會明顯卡頓,但是對于手游來說,消耗手機的電量比較少。幀率為45,游戲有一點點卡,但還湊合,消耗電量中等。幀率為60,游戲很流暢,但消耗手機的電量會比較多。可以用Application.targetFrameRate來鎖定幀率,也可以用UnityEngine.Rendering命名空間中的OnDemandRendering.renderFrameInterval來鎖定幀率。
??????? 盡量少用foreach語句,可以改為for語句。因為每次使用foreach語句會造成微量的內存垃圾。
????????
????????要判斷GameObject型對象.tag是不是某個標簽,使用GameObject型對象.CompareTag方法會更高效。
????????
????????盡量少用GameObject.Find方法和Object.FindObjectOfType方法來查找游戲對象,可以提前把要查找的游戲對象存儲在變量、列表、字典等容器中,方便查找。也可以用GameObject.FindGameObjectWithTag方法來查找游戲對象。
????????
在UI顯示字符串的時候,如果一些內容是固定的,我們可以把它拆分開來,這樣可以減少使用+號來拼接的次數,減少內存垃圾的產生。例如“殺敵數:999”,其中“殺敵數:”是固定的,冒號后面的數字才是會變的,那么我們可以用兩個Text組件分別記錄它們,改變的時候只改變冒號后面的數字。
????????
????????頻繁對字符串賦新的值,或者頻繁拼接字符串的時候,可以使用StringBuilder代替string
??????? 如果要頻繁操作某腳本,不要每次都用GetComponent方法來獲取這些腳本。可以用一個變量存儲起獲得的這個腳本,之后要訪問它,就直接訪問這個變量即可。也可以考慮在生命周期方法Awake或者Start中聲明變量來存儲,之后訪問這個變量即可。
????????
????????盡量少用正則表達式。雖然正則表達式的形式看上去比較簡便,但是使用它會造成一定的性能消耗,且會產生內存垃圾。
??????? 盡量少用LINQ語法,因為每次使用LINQ都會產生一定量的內存垃圾。
??????? 盡量少用Camera.main來訪問主攝像機,因為每次訪問它,實際上Unity都是從場景中查找它的。可以聲明一個變量存儲它,在生命周期方法Awake或Start中獲取主攝像機的應用。
??????? ???????
????????在Animator、Shader中使用Get方法和Set方法時,不傳入字符串作為參數,而是傳入哈希值。例如Animator組件可以使用Animator.StringToHash方法獲得指定字符串的哈希值,再把它作為參數傳入Animator組件的Get方法或Set方法中進行使用。例如Shader,則可以用Shader.PropertyToID方法來獲取指定屬性的ID
????????
????????使用非分配物理API。例如使用Physics.RaycastNonAlloc方法代替Physics.RaycastAll方法,使用Physics.SphereCastNonAlloc方法代替Physics.SphereCastAll方法,以此類推。Physics2D類也有類似的方法。
??????? 一般情況下,整數的數學運算比浮點數的數學運算效率高,浮點數的數學運算比矢量的數學運算效率高。可以靈活運用數學的加法交換律、加法結合律、乘法交換律、乘法結合律,在保證結果不變的前提下,調整運算順序,減少浮點數的數學運算和矢量的數學運算。
????????
????????使用高效的算法進行計算
??????? 每次執行Debug.Log來打印信息會消耗極少量的性能,如果要在游戲正式發布之后不執行某些Debug.Log的語句,但又不想把這些代碼刪掉,則可以使用宏來禁止在游戲正式發布之后執行Deubg.Log的語句。例如使用#if語句或者Conditional特性。
??????? 盡量減少在生命周期方法Update、FixedUpdate、LateUpdate中的邏輯。其中有些不需要頻繁執行的邏輯,可以使用協程或者Invoke方法,每隔指定的秒數執行一次或每隔指定的幀數執行一次。
????????
????????盡量避免頻繁的裝箱拆箱操作。也可以使用泛型,這樣就能避免裝箱拆箱。但是要注意,Lua熱更新對泛型的支持不太好。
??????? 如果物體身上添加了剛體組件,則盡量用剛體組件的方法來移動它,而不是用Transform類的方法來移動它。
??????? 如果物體身上添加了CharacterController組件,則盡量用CharacterController組件的方法來移動它,而不是用Transform類的方法來移動它。同理,如果物體身上添加了剛體組件,則應盡量用剛體組件的方法來移動它,而不是用Transform類的方法來移動它。
??????? 應盡量避免DontDestroyOnLoad中加載的資源過多,因為它在切換場景的時候不會被釋放,聲明的變量以及加載的資源會一直占用著內存。我們可以考慮把一些資源不用的資源釋放掉,需要的時候再加載它。
??????? 不使用組件可以刪掉,這樣可以節省一些內存。常見的有AudioSource組件、Animator組件、Animation組件等,如果它們不需要使用,則可以刪掉。
??????? 寫一個類繼承AssetPostProcessor,然后定義里面特定的方法,以此來自動設置資源導入Unity之后的屬性。
??????? 盡量避免閉包。因為閉包會產生額外的內存開銷。
Shader優化:
??????? 修改Shader的代碼,或者自定義一個Shader
??????? 修改渲染管線的源碼,改成符合自己項目的渲染管線,或者自定義渲染管線。