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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Unity MMORPG游戏优化经验分享

發(fā)布時(shí)間:2024/8/26 编程问答 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Unity MMORPG游戏优化经验分享 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

今天由Unity技術(shù)支持工程師高巖,根據(jù)實(shí)際的技術(shù)支持工作經(jīng)驗(yàn)積累,分享如何對(duì)Unity MMORPG游戲進(jìn)行優(yōu)化。

在優(yōu)化Unity游戲時(shí),我們一般從四個(gè)方面:CPU、GPU、內(nèi)存、工程配置等入手,它們都可能是影響游戲性能瓶頸的關(guān)鍵。

CPU

我們平常游戲的很多性能瓶頸都在CPU。例如:MONO內(nèi)存分配帶來(lái)CPU開(kāi)銷,當(dāng)Mono內(nèi)存從50M、60M、70M,一直增大到100M,這些內(nèi)存分配都相當(dāng)于CPU的開(kāi)銷。當(dāng)在Update函數(shù)中存在比較復(fù)雜的邏輯時(shí),很容易出現(xiàn)每一幀都觸發(fā)內(nèi)存分配,如圖01所示。
?

圖 01


雖然截圖中一幀里的GC Alloc只有0.6KB,但是當(dāng)游戲運(yùn)行很長(zhǎng)時(shí)間后,累計(jì)數(shù)量是相當(dāng)高的,這就讓每一幀都存在GC Alloc帶來(lái)的CPU開(kāi)銷。

處理客戶端與服務(wù)器通信的數(shù)據(jù)包時(shí),會(huì)存在序列化與反序列化,如果實(shí)現(xiàn)方式不合理時(shí),會(huì)帶來(lái)多余的內(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í)容易由于過(guò)大的內(nèi)存分配而撐大MONO堆內(nèi)存。所以要在項(xiàng)目設(shè)計(jì)時(shí)找到最優(yōu)化的方式來(lái)實(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ì)象拷貝。所以建議可以通過(guò)把這類字符串預(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ù)。所以,這些屬性返回值要做緩存。或者通過(guò)其他API來(lái)實(shí)現(xiàn)需求從而規(guī)避掉這個(gè)問(wèn)題。請(qǐng)注意,Getcomponent只會(huì)在編輯器環(huán)境下存在內(nèi)存開(kāi)銷,真機(jī)上不存在,大家在Profiling時(shí)不要被誤導(dǎo)。

通常Debug.Log一類的日志函數(shù)應(yīng)該只存在Debug階段,但是很多時(shí)候這些函數(shù)沒(méi)有屏蔽。如果它們出現(xiàn)在調(diào)用次數(shù)較多的邏輯中,就帶來(lái)額外的CPU開(kāi)銷。同樣Warning和Log存在相同的情況。雖然日常在console或真機(jī)Log里常見(jiàn),但是經(jīng)常沒(méi)有被處理。建議對(duì)待Warning也要找到它的觸發(fā)原因并解決,防止在Release中出現(xiàn)。Log函數(shù)不會(huì)因?yàn)榇虬鼮閞elease版本就會(huì)自動(dòng)屏蔽,需要使用宏定義來(lái)屏蔽。

閉包與匿名函數(shù)盡可能不要使用。閉包中調(diào)用外部變量,需要?jiǎng)?chuàng)建一個(gè)臨時(shí)class對(duì)象來(lái)包含外部變量并且傳給閉包函數(shù),從而帶來(lái)內(nèi)存開(kā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,這帶來(lái)了一定的CPU開(kāi)銷。如果需要調(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)用是存在開(kāi)銷的,可以把Object.FindObjectWithTag(“MainCamera”)緩存下來(lái)來(lái)代替。調(diào)用射線檢測(cè)函數(shù)時(shí)應(yīng)該使用那些不存在開(kāi)銷的函數(shù),例如Physics.RaycastNonAlloc。

當(dāng)Canvas重建時(shí),會(huì)引起材質(zhì)的重新創(chuàng)建、排序、Mesh重建,這都會(huì)帶來(lái)CPU的開(kāi)銷。當(dāng)Canvas內(nèi)容非常復(fù)雜的時(shí)候,每次重建很可能會(huì)帶來(lái)比較明顯的卡頓。UGUI里面的Mask會(huì)使用StencilBuffer,蒙版內(nèi)的元素是沒(méi)法和外面的元素做合批,即便在圖集與材質(zhì)都是相同的。這時(shí)可以用RectMask2D來(lái)實(shí)現(xiàn)蒙版,可以稍微降低一些開(kāi)銷。Canvas上的GraphicRaycaster選項(xiàng),在不需要有交互時(shí)可以不勾選。而Layout組件會(huì)涉及到節(jié)點(diǎn)的遍歷操作,都有內(nèi)存與CPU的開(kāi)銷,如果能不用就不用它,或者自行硬編碼實(shí)現(xiàn)簡(jiǎn)單的自動(dòng)布局。

Canvas都建議做動(dòng)靜分離,頻繁改動(dòng)的元素和固定不變的元素分開(kāi)到不同的Canvas。需要注意Canvas數(shù)量,數(shù)量多少根據(jù)UI的復(fù)雜程度、動(dòng)靜分離的Canvas個(gè)數(shù)進(jìn)行測(cè)試,評(píng)估多少個(gè)Canvas是合理的。目前發(fā)現(xiàn)Unity2017.3中,出現(xiàn)過(guò)當(dāng)Canvas數(shù)量達(dá)到十幾個(gè)或更多時(shí),帶來(lái)的開(kāi)銷反而比不分拆時(shí)還大。

UI元素存在半透并很多元素進(jìn)行疊加,就導(dǎo)致OverDraw消耗比較大。可以通過(guò)減少疊加層數(shù)、縮小Sprite的空白區(qū)域等方式來(lái)控制。

當(dāng)Canvas 處于Worldspace或者Screen Space時(shí),Canvas存在Event Camera或者Render Camera屬性,需要掛接Camera。此處若為None,運(yùn)行時(shí)每幀都會(huì)有十幾次訪問(wèn)它,底層默認(rèn)返回Camera.main。所以預(yù)先關(guān)聯(lián)Camera對(duì)象。

圖集的分類方式直接影響到UI的合批效率。二手手游轉(zhuǎn)讓除了幾個(gè)通用圖集外,其它圖集按UI模塊類型區(qū)分,一個(gè)或多個(gè)UI公用一套圖集。圖集的面積利用率要做到最高,避免圖集存在太多空白區(qū)域。而圖標(biāo)是分散還是合并到圖集上,要看項(xiàng)目實(shí)際情況,并沒(méi)有固定的規(guī)則。

UI背景圖不要出現(xiàn)NPOT尺寸,如果要用NPOT,嘗試多個(gè)NPOT圖合并為POT尺寸,或者美術(shù)對(duì)NPOT圖拉伸為POT,在Unity中還原為原始尺寸。

通常靜態(tài)合批通過(guò)給場(chǎng)景上的物體勾上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開(kāi)銷,同時(shí)Mesh可讀寫選項(xiàng)也開(kāi)啟,在內(nèi)存中邊存在雙份的Mesh數(shù)據(jù),同時(shí)合并后模型也是一份新Mesh數(shù)據(jù)。建議可以用第三方插件Mesh Baker來(lái)進(jìn)行靜態(tài)合批。同時(shí),各個(gè)模型的材質(zhì)也要針對(duì)靜態(tài)合批來(lái)制作,畢竟相同材質(zhì)的模型才可以合并。

圖 02


動(dòng)態(tài)合批對(duì)于大部分有Lightmap的模型是無(wú)效的,還存在900左右頂點(diǎn)的合批限制。在Unity 2017.3支持32bit Mesh index buffers,可以合并Mesh時(shí)支持更多的頂點(diǎn),可以在FBX選項(xiàng)內(nèi)Index Format打開(kāi)或者運(yùn)行時(shí)設(shè)置Mesh.indexFormat。

骨骼蒙皮計(jì)算一般使用CPU Skinning,雖然引擎也是支持GPU skinning的,但需要注意性能瓶頸在CPU端還是GPU端。如果GPU端是性能瓶頸時(shí),盲目打開(kāi)GPU skinning,會(huì)變成一種負(fù)優(yōu)化。當(dāng)角色模型的骨骼數(shù)超過(guò)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的使用在場(chǎng)景比較復(fù)雜龐大時(shí),Bake的性能比較差。可以通過(guò)配合射線檢測(cè)和自定義高度圖數(shù)據(jù)控制角色高度。

GPU

頂點(diǎn)數(shù)量的控制,首先要從美術(shù)方面,控制模型的合理面數(shù)。有的建筑物被遮擋了一部分,被遮擋部分可以減面甚至把這一塊摳掉留空。避免場(chǎng)景中出現(xiàn)大量小物體組合出一個(gè)更大的物件,設(shè)計(jì)之初就對(duì)零散物體合并材質(zhì)、貼圖、Mesh。場(chǎng)景地圖也可以分區(qū)塊制作、加載管理,同時(shí)配合LODGroup使用。還可以通過(guò)第三方插件Mesh Baker LOD輔助進(jìn)行。
?

圖 03


紋理的尺寸會(huì)影響上傳紋理時(shí)帶寬的使用,也就是上傳耗時(shí)比較高。通常3D模型的紋理,都會(huì)把打開(kāi)Mipmap,可以提高紋理采樣的質(zhì)量,降低命中耗時(shí),提升IO速度。同時(shí)紋理過(guò)濾模式的選擇,對(duì)于UI紋理使用Bilinear足矣,Trilinear配合打開(kāi)Mipmap后的插值計(jì)算,效果更好。

當(dāng)一個(gè)角色帶有一對(duì)翅膀,設(shè)置Mesh.alpha進(jìn)行隱藏或顯示,翅膀在Alpha=0時(shí),依然被渲染。而顯示全屏UI時(shí),它擋住了后面的主場(chǎng)景,但由于場(chǎng)景Camera未關(guān)閉使得場(chǎng)景依然被渲染,如果此時(shí)UI里還顯示角色模型,積累的渲染壓力就比較大,這些都會(huì)體現(xiàn)在Overdraw消耗上。

根據(jù)對(duì)Shader的功能需求,對(duì)復(fù)雜度要進(jìn)行控制。運(yùn)算符要合理使用,變量的浮點(diǎn)精度要同時(shí)考慮計(jì)算需求和真機(jī)的實(shí)際支持的精度范圍。對(duì)Tex2D、紋理采樣的使用方式要合理,畢竟這類指令過(guò)多時(shí)會(huì)增加開(kāi)銷。

Unity引擎自帶的Terrian系統(tǒng),可以通過(guò)分區(qū)塊或者轉(zhuǎn)為Mesh解決此部分性能瓶頸。我們可以通過(guò)插件Terrain Slicing & Dynamic Loading Kit來(lái)分割地形,并調(diào)整地形的尺寸和精度等配置參數(shù)。
?

圖 04


一個(gè)特效包含粒子發(fā)射器的數(shù)量不能隨意創(chuàng)建,對(duì)渲染和內(nèi)存都有不小的負(fù)載。當(dāng)粒子存在發(fā)射Mesh的需要時(shí),要控制Max Particles的數(shù)量。同時(shí)有些特效不一定要通過(guò)粒子系統(tǒng)實(shí)現(xiàn),可以通過(guò)各種變通方式或低負(fù)載的方式制作。

內(nèi)存

每一個(gè)Mesh的壓縮選項(xiàng)、Read/Write選項(xiàng)都要根據(jù)Mesh使用方式進(jìn)行單獨(dú)設(shè)置,同時(shí)要做好當(dāng)Mesh存在雙份數(shù)據(jù)時(shí),CPU端數(shù)據(jù)的及時(shí)釋放。合理的減面也是必不可少的。

壓縮紋理的使用是毋庸置疑,而壓縮格式要根據(jù)項(xiàng)目的機(jī)型適配靈活選擇,保證質(zhì)量和體積都能滿足需要。當(dāng)編輯器中刷地形紋理時(shí),需要紋理開(kāi)啟Read/Write,而在打包時(shí)要關(guān)閉這個(gè)選項(xiàng)。

每個(gè)紋理的尺寸要根據(jù)它的用途、實(shí)際測(cè)試時(shí)內(nèi)存占用的情況,進(jìn)行合理的限制,不能隨意設(shè)定它。對(duì)于圖集需要最大限度利用面積,避免浪費(fèi)寶貴的內(nèi)存。另外當(dāng)紋理使用ETC2、ASTC格式時(shí),在不支持這些格式的設(shè)備上,壓縮紋理會(huì)被fallback為無(wú)壓縮的RGBA格式,不但增大了內(nèi)存占用,同時(shí)增加了fallback的CPU開(kāi)銷。

AnimationClip可以通過(guò)壓縮浮點(diǎn)數(shù)精度,剔除無(wú)用的scale曲線降低內(nèi)存占用。同時(shí)AnimationClip加載策略也對(duì)內(nèi)存占用有很大影響,全部預(yù)加載還是按需異步加載,需要根據(jù)項(xiàng)目實(shí)際情況決定。

Mono進(jìn)行內(nèi)存分配時(shí),在不同類型的數(shù)據(jù)對(duì)象在內(nèi)存中是相鄰的存在內(nèi)存塊里,如果說(shuō)釋放了一個(gè)數(shù)組,它所占的內(nèi)存被釋放了。但是這個(gè)區(qū)域是不會(huì)還給系統(tǒng)內(nèi)存,依然保留著。接著又創(chuàng)建了新的對(duì)象,新對(duì)象的內(nèi)存大小比剛才被釋放的空間大,就無(wú)法直接放入這個(gè)空間,只能由Mono申請(qǐng)一份新的內(nèi)存來(lái)存放。當(dāng)Mono申請(qǐng)新內(nèi)存時(shí),Mono堆內(nèi)存一般會(huì)擴(kuò)大很大一部分,如見(jiàn)下圖05所示。
?

圖 05


在使用數(shù)組類型的對(duì)象時(shí),如果初始化時(shí)時(shí)非定長(zhǎng)數(shù)組,數(shù)組實(shí)際容量會(huì)根據(jù)Add操作以0、4、8、16、32倍逐步擴(kuò)大,其中大量空間為Null,浪費(fèi)了內(nèi)存。這種情況常出現(xiàn)在客戶端初始化數(shù)據(jù)表保存到List、Dictionary時(shí)。

當(dāng)我們需要手動(dòng)釋放一些對(duì)象的內(nèi)存時(shí),會(huì)有很多種方式,Unity提供了很多卸載各種資源的函數(shù)。主動(dòng)調(diào)GC.collect是不必要的,如果一個(gè)對(duì)象的引用不是Null時(shí),是不可能釋放它的。GC只需要做好對(duì)象引用的清理就可以,剩下的還是由GC機(jī)制自動(dòng)管理更好。我們可以通過(guò)自定義內(nèi)存池和資源管理器,來(lái)很精細(xì)的控制每一種資源的生命周期。

AssetBundle壓縮格式一般使用LZ4,但要注意AssetBundle的合理Unload時(shí)機(jī)。而LZMA格式,由于存在加載時(shí)解壓后重壓縮為L(zhǎng)Z4的開(kāi)銷,一般情況下不建議使用。主Bundle卸載時(shí),與它關(guān)聯(lián)的依賴Bundle一定要根據(jù)引用計(jì)數(shù)來(lái)控制是否可以卸載,否則依賴Bundle的Asset容易引發(fā)內(nèi)存泄露。

IL2CPP在安卓系統(tǒng)使用時(shí),要注意libil2coo.so的文件大小。在安卓系統(tǒng)中,so會(huì)在游戲啟動(dòng)后直接加載在內(nèi)存中,它的內(nèi)存占用大小基本上和文件大小差不多。所以so的尺寸要有所控制,否則會(huì)影響整個(gè)游戲的內(nèi)存數(shù)值。所以,使用il2cpp時(shí)要注意值類型的泛型、重復(fù)代碼等容易增大il2cpp的cpp代碼體積的情況。

其它

在PhysicsManagerSetting的LayerCollisionMatrix去掉不參加碰撞檢測(cè)的layer。Time Manager中的fixed time step要根據(jù)物理系統(tǒng)的使用情況設(shè)置間隔時(shí)長(zhǎng)。游戲分辨率要通過(guò)高中低配置來(lái)動(dòng)態(tài)調(diào)整。

Graphics Stettings和內(nèi)置Shader有關(guān)的開(kāi)關(guān)根據(jù)項(xiàng)目使用情況來(lái)有選擇的打開(kāi)或關(guān)閉。同時(shí)建議所有Shader都要打包為Bundle來(lái)加載初始化。

項(xiàng)目的性能優(yōu)化工作應(yīng)該每隔一階段就進(jìn)行一次性能分析評(píng)估,及時(shí)解決掉性能瓶頸。同時(shí)應(yīng)該有專人負(fù)責(zé)這一項(xiàng)工作,提高執(zhí)行力。

雖然Unity Asset Store資源商店提供的各種插件功能強(qiáng)大,但是插件內(nèi)部的一些邏輯沒(méi)有考慮到移動(dòng)平臺(tái)的應(yīng)用環(huán)境,存在很多不良代碼,需要開(kāi)發(fā)者仔細(xì)檢查插件源代碼,根據(jù)情況進(jìn)行改進(jìn)。并在性能測(cè)試時(shí)觀察是否存在插件帶來(lái)的性能瓶頸。

通常在對(duì)項(xiàng)目進(jìn)行性能分析時(shí),會(huì)有很多工具輔助我們進(jìn)行分析工作。下面是我們推薦的工具:
?

  • Xcode & Instrunments
  • RenderDoc
  • Snapdragon Profiler



小結(jié)

Unity MMORPG游戲優(yōu)化經(jīng)驗(yàn)分享就為大家介紹到這里,更多Unity優(yōu)化經(jīng)驗(yàn)分享請(qǐng)?jiān)L問(wèn)Unity官方

中文論壇(Unitychina.cn)!

總結(jié)

以上是生活随笔為你收集整理的Unity MMORPG游戏优化经验分享的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。