Unity3D培訓:MMORPG游戲優(yōu)化經(jīng)驗分享(一)
來源:
奇酷教育 發(fā)表于:
奇酷教育-unity3D培訓_unity3D游戲_unity3D教程
Unity3D培訓:MMORPG游戲在優(yōu)化
Unity3d游戲時,我們一般從四個方面:CPU、GPU、內(nèi)存、工程配置等入手,它們都可能是影響游戲性能瓶頸的關(guān)鍵。
我們平常游戲的很多性能瓶頸都在CPU。例如:MONO內(nèi)存分配帶來CPU開銷,當Mono內(nèi)存從50M、60M、70M,一直增大到100M,這些內(nèi)存分配都相當于CPU的開銷。當在Update函數(shù)中存在比較復雜的邏輯時,很容易出現(xiàn)每一幀都觸發(fā)內(nèi)存分配,如圖01所示。
雖然截圖中一幀里的GC Alloc只有0.6KB,但是當游戲運行很長時間后,累計數(shù)量是相當高的,這就讓每一幀都存在GC Alloc帶來的CPU開銷。
處理客戶端與服務器通信的數(shù)據(jù)包時,會存在序列化與反序列化,如果實現(xiàn)方式不合理時,會帶來多余的內(nèi)存分配。一般很多項目都現(xiàn)在使用Protobuff,如果是自行設計的數(shù)據(jù)包格式,就要考慮如何控制序列化與反序列化的內(nèi)存分配。
靜態(tài)數(shù)據(jù)表如果使用Json、xml等格式時,同時解析邏輯與數(shù)據(jù)結(jié)構(gòu)設計不良,在初始化數(shù)據(jù)表時容易由于過大的內(nèi)存分配而撐大MONO堆內(nèi)存。所以要在項目設計時找到最優(yōu)化的方式來實現(xiàn)功能需求與性能需求。
String是一個很常用的引用類型對象。當代碼里存在字符串拼接、直接或間接調(diào)用ToString()函數(shù)時,會生成字符串的副本,也就產(chǎn)生了內(nèi)存分配。例如:調(diào)用Object.name屬性,即使每次返回值是固定的,依然是不同的String對象,因為這里每次返回都是一個對象拷貝。所以建議可以通過把這類字符串預先緩存,或者在打包時生成一個名字的列表作為靜態(tài)數(shù)據(jù),提供給運行時的邏輯直接讀取。
部分Unity內(nèi)置API在被調(diào)用時,都是返回對象拷貝。例如:Getcomponents、Sprite.Vertices、Input.Touches等。從設計角度是考慮代碼安全性,防止外部直接去修改真正的對象數(shù)據(jù)。所以,這些屬性返回值要做緩存?;蛘咄ㄟ^其他API來實現(xiàn)需求從而規(guī)避掉這個問題。請注意,Getcomponent只會在編輯器環(huán)境下存在內(nèi)存開銷,真機上不存在,大家在Profiling時不要被誤導。
通常Debug.Log一類的日志函數(shù)應該只存在Debug階段,但是很多時候這些函數(shù)沒有屏蔽。如果它們出現(xiàn)在調(diào)用次數(shù)較多的邏輯中,就帶來額外的CPU開銷。同樣Warning和Log存在相同的情況。雖然日常在console或真機Log里常見,但是經(jīng)常沒有被處理。建議對待Warning也要找到它的觸發(fā)原因并解決,防止在Release中出現(xiàn)。Log函數(shù)不會因為打包為release版本就會自動屏蔽,需要使用宏定義來屏蔽。
閉包與匿名函數(shù)盡可能不要使用。閉包中調(diào)用外部變量,需要創(chuàng)建一個臨時class對象來包含外部變量并且傳給閉包函數(shù),從而帶來內(nèi)存開銷。匿名函數(shù)在作為一個函數(shù)的參數(shù)傳入時,也存在內(nèi)存分配。il2cpp中如果使用匿名函數(shù)當參數(shù),不要用預聲明的函數(shù)。
ParticleSystem API在Unity 2017.2之前的版本中,Stop和Simulate內(nèi)部實現(xiàn)使用了閉包。粒子系統(tǒng)的一些API,例如:Start、Stop、Pause、Clear、Simulate在調(diào)用它們時會遞歸調(diào)用當前粒子節(jié)點下面的所有子級節(jié)點,并會觸發(fā)GetComponent,這帶來了一定的CPU開銷。如果需要調(diào)這幾個方法的時候,函數(shù)參數(shù)withChildren可以設為false,不觸發(fā)遍歷子節(jié)點。在粒子對象初始化時,預存子節(jié)點,在需要時直接根據(jù)緩存的子節(jié)點列表分別調(diào)用它們的Start。
Camera.main的調(diào)用是存在開銷的,可以把Object.FindObjectWithTag(“MainCamera”)緩存下來來代替。調(diào)用射線檢測函數(shù)時應該使用那些不存在開銷的函數(shù),例如Physics.RaycastNonAlloc。
當Canvas重建時,會引起材質(zhì)的重新創(chuàng)建、排序、Mesh重建,這都會帶來CPU的開銷。當Canvas內(nèi)容非常復雜的時候,每次重建很可能會帶來比較明顯的卡頓。UGUI里面的Mask會使用StencilBuffer,蒙版內(nèi)的元素是沒法和外面的元素做合批,即便在圖集與材質(zhì)都是相同的。這時可以用RectMask2D來實現(xiàn)蒙版,可以稍微降低一些開銷。Canvas上的GraphicRaycaster選項,在不需要有交互時可以不勾選。而Layout組件會涉及到節(jié)點的遍歷操作,都有內(nèi)存與CPU的開銷,如果能不用就不用它,或者自行硬編碼實現(xiàn)簡單的自動布局。
Canvas都建議做動靜分離,頻繁改動的元素和固定不變的元素分開到不同的Canvas。需要注意Canvas數(shù)量,數(shù)量多少根據(jù)UI的復雜程度、動靜分離的Canvas個數(shù)進行測試,評估多少個Canvas是合理的。目前發(fā)現(xiàn)Unity2017.3中,出現(xiàn)過當Canvas數(shù)量達到十幾個或更多時,帶來的開銷反而比不分拆時還大。
UI元素存在半透并很多元素進行疊加,就導致OverDraw消耗比較大??梢酝ㄟ^減少疊加層數(shù)、縮小Sprite的空白區(qū)域等方式來控制。
當Canvas 處于Worldspace或者Screen Space時,Canvas存在Event Camera或者Render Camera屬性,需要掛接Camera。此處若為None,運行時每幀都會有十幾次訪問它,底層默認返回Camera.main。所以預先關(guān)聯(lián)Camera對象。
圖集的分類方式直接影響到UI的合批效率。除了幾個通用圖集外,其它圖集按UI模塊類型區(qū)分,一個或多個UI公用一套圖集。圖集的面積利用率要做到最高,避免圖集存在太多空白區(qū)域。而圖標是分散還是合并到圖集上,要看項目實際情況,并沒有固定的規(guī)則。
UI背景圖不要出現(xiàn)NPOT尺寸,如果要用NPOT,嘗試多個NPOT圖合并為POT尺寸,或者美術(shù)對NPOT圖拉伸為POT,在Unity中還原為原始尺寸。
通常靜態(tài)合批通過給場景上的物體勾上Static實現(xiàn),但是有時會因為導致包體太大,改為運行時調(diào)用staticBatchingUtility.Combine進行物件合并。但是運行時手動靜態(tài)合批會有不小的CPU開銷,同時Mesh可讀寫選項也開啟,在內(nèi)存中邊存在雙份的Mesh數(shù)據(jù),同時合并后模型也是一份新Mesh數(shù)據(jù)。建議可以用第三方插件Mesh Baker來進行靜態(tài)合批。同時,各個模型的材質(zhì)也要針對靜態(tài)合批來制作,畢竟相同材質(zhì)的模型才可以合并。
動態(tài)合批對于大部分有Lightmap的模型是無效的,還存在900左右頂點的合批限制。在Unity 2017.3支持32bit Mesh index buffers,可以合并Mesh時支持更多的頂點,可以在FBX選項內(nèi)Index Format打開或者運行時設置Mesh.indexFormat。
骨骼蒙皮計算一般使用CPU Skinning,雖然引擎也是支持GPU skinning的,但需要注意性能瓶頸在CPU端還是GPU端。如果GPU端是性能瓶頸時,盲目打開GPU skinning,會變成一種負優(yōu)化。當角色模型的骨骼數(shù)超過100根、150根時,某些身體部位的骨骼動畫,可以用BlendShapes代替。當某一部位骨骼動畫不播放時,可以把這個部位的Animator組件關(guān)掉。Animation Instancing也是一個可以優(yōu)化大量角色動畫性能的手段。
物理系統(tǒng)中,MeshCollider的使用在場景比較復雜龐大時,Bake的性能比較差??梢酝ㄟ^配合射線檢測和自定義高度圖數(shù)據(jù)控制角色高度。
以上就是
奇酷為大家分享的“Unity3D培訓:MMORPG游戲”謝謝大家觀看,如果對
unity3D感興趣的話,想學
unity3D培訓的,也可以在線咨詢,我們將竭誠為你解答。