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