unity開發小貼士之十 項目優化

原文連接:python

https://zhuanlan.zhihu.com/p/36930662android

因此這篇博客的主題仍是以此次分享的核心內容爲主線,順便把PPT的內容也提供出來,可能添加少許當時沒聊的私貨(以引用塊或者(補)的方式標註,以方便聽過度享的朋友閱讀)。ios

0. 題目和提綱

此次分享的題目是《Connecting The Dots——基於團隊的持續優化之道》。優化是一件瑣碎而又繁複的事情,它一般是一個又一個的點,而咱們若是要讓一個好比說30人甚至50人的大型團隊,花1到2年的時間去開發一款大型的手遊項目,不但須要了解和掌握這些優化的細節,並且要藉助團隊的力量在整個遊戲開發的生命週期過程當中不斷地進行持續優化,纔可以讓優化效果能夠保持下去。git

在聊具體的分享內容以前,我想先拋出這樣一個問題——在優化這樣的過程當中,程序應該承擔一個什麼樣的角色?程序員

這也是和我剛纔所想要聊的持續優化關聯性很是大的問題。在優化過程當中,程序來可能有兩種角色,一種是像救火員這樣的角色,哪裏冒火了,就去撲滅它。我本身也有過做爲救火員去作優化的經歷。另一種工做方式是像園丁同樣,可能不須要特別緊急地在最後關頭才處理那些性能問題,而是從項目的初期就開始規劃整個項目的性能指標,而後讓整個團隊能夠按照指標的要求合理地產出美術資源、編寫代碼,從而讓整個產品一直處於一個比較健康的狀態。github

救火員的職責頗有挑戰性,同時也比較容易作出成就感,好比我也過用1到2周時間讓遊戲的性能有很是大提高的經歷。而園丁在作的事情可能看上去比較普通,都是些平常且瑣碎的事情。在遊戲開發中,這兩種角色並不衝突,而是同時存在的。可是在個人理解裏,對於性能更友好的狀態,是讓團隊裏的成員都在以一個園丁的角色在工做,從根本上來保持遊戲的性能一直處於一個較好的狀態。由於——編程

每個神級優化的背後,都隱藏着一個2B的bug!

那此次分享我就想從這樣三個方面來聊一下我所理解的以「園丁」的角色來讓團隊進行繼續優化的方式。xcode

首先是美術資源的優化,美術資源是一個會對客戶端運行效率產生很是重要影響元素之一,我想和你們聊一下,怎麼樣讓咱們團隊能夠在美術資源產出方面一直保持一個比較好的狀態。而後我想回歸到程序的本職工做,從一個更加宏觀的角度來討論一下程序代碼的部分要怎麼樣去作一些優化,對於代碼質量和一些底層模塊的設計,如何去作一些提早的思考和設計。第三部分,團隊開發效率的優化,這塊可能看上去跟性能優化的題主沒有直接的關係,可是在我看來優化不只僅是遊戲運行效率優化,並且應該包括整個團隊開發效率的優化,這也是在項目開發過程當中很是重要的部分。性能優化

1. 美術資源優化

首先來看一下美術資源優化的部分。服務器

你們在平常開發中能夠感覺到,對於美術資源部分,美術和程序,尤爲是要進行性能優化的程序,他們的關注點會有不一樣。美術更多的是關注美術效果是否是足夠的好,而程序可能更多的關注美術資源在設備上的運行效率是否能夠達到目標幀率,這其中就有一些目標導向上的衝突。我認爲,解決這一衝突的一個很是重要的手段是儘早來創建合理的美術資源製做規範。

這實際上是一個美術資源製做的過程,首先經過在立項時製做Demo來肯定適合的美術資源規範,而後經過給美術灌輸效率意識、提供輔助工具讓美術在製做資源的過程當中能夠嚴格按照規範進行,最後是要藉助QA的力量對產出的資源進行檢查,確保達標。

1.1 規範制定

爲何制定美術規範這麼重要呢?我想借本身以前的一個項目中的真實經從來聊一下。

《無盡戰區·覺醒》這款手遊是我做爲主程帶領團隊開發的第一款手遊項目。當這個項目進行到中期的時候,收包了一批場景的資源。在使用這些資源的時候發現它們的運行效率明顯偏低,Profile發現面數和Drawcall都超標了。面數很簡單,使用減面工具進行減面便可,Drawcall也使用相似Unity的Static Batching的方式進行優化,可是結果發現一些場景的Drawcall只能從400減小到300多,並不能達到預期。仔細檢查發現不少模型沒法合併的緣由是使用了端用經常使用的一種貼圖製做方法——四方連續的貼圖。這種方式雖然可使用很小的貼圖尺寸經過tiling製做出精細的效果,可是對於Drawcall敏感的手游來講並不合適。最終項目又請了好幾個外派美術進行資源的整合和製做,致使多花費了幾個周時間和幾十萬的美術成本。雖然對於大公司來講幾十萬的美術成本不算什麼,可是我依然以爲這是我做爲一個主程的失職——由於沒有提早和美術溝通好製做方法,致使了這樣的問題發生。因此在出來創業的項目中,在項目最初期Demo製做完畢以後,客戶端團隊就和美術一塊兒製做了很是詳細的美術資源製做規範。

制定規範的步驟大體能夠整理爲以下的幾個部分:

在制定美術規範的第一步是要進行遊戲信息的收集,由於不一樣的遊戲類型以及鏡頭視角會對美術資源的製做產生很是大的影響,好比2.5D和3D自由視角的遊戲是有不一樣的製做和優化方式。

在收集了足夠的信息以後,須要和美術敲定一些大的技術方向,好比線性空間、HDR和動態光影等。線性空間是團隊應當提早關注的一個點,我我的的觀點是在寫實的遊戲風格中,能上線性空間仍是儘可能使用線性空間,對於美術效果的提高仍是頗有幫助的。在正確的基礎上,才更容易出正確的效果。Unity目前的版本和OpenGL ES 3.0的普及率,我的觀點是徹底能夠在移動平臺上使用線性空間的。固然這也要考慮具體項目的內容以及開發團隊成員的能力和經驗等因素。HDR的開關在Unity中仍是比較簡單的,但在性能方面對於帶寬的影響比較大,收益也不小,建議追求效果的團隊提早考慮。動態光影是咱們研發團隊的美術特別想追求的一個效果,可是由於咱們項目今年要上線,並且要考慮低配的效果,因此程序一直卡着不讓用場景使用全實時的動態陰影,而是儘可能使用烘焙的方案。

肯定目標機型就很少說了,考慮主流的高中低三檔,建議提早購買一些設備方便後續的性能測試。我對於這三檔的基本分類大體以下:

高檔:大部分iOS設備,主流的安卓旗艦設備;
中檔:少部分但願支持的iOS設備,好比目前的iPhone 5s、6/6s,ipad mini等,1-3年前的旗艦以及1-2年的1500元左右的安卓設備;
低檔:1-3年前的千元機。
這個也能夠參考UWA測試中的機型選擇。

接下來是要針對面數和drawcall這兩個核心點,對總體的標準進行定義,並將這些標準分配到場景、角色、特效、UI等資源分類上。由於這些不一樣類型的資源是由不一樣的美術同窗產出的,他們之間一般不會去溝通各自的性能消耗,所以須要針對每部分進行規則的細化。

場景部分強調一下貼圖「像素密度」的概念。在製做場景的時候須要提早定義貼圖使用的像素密度,不然的會致使貼圖精度太高或者不夠等問題。所謂的「像素密度」是指在正常的遊戲視角下,一個一立方米的Cube應當使用多少尺寸像素的貼圖。這個在場景製做的初期應當定義好,美術纔好去使用正確的貼圖尺寸,不然到後期優化可能發現大量超標的貼圖在使用,若是此時已經進行了貼圖的合併,美術修改的工做量會很是大。

角色部分的規範須要把面數和drawcall的標準細化到每一個角色中,這時候須要和策劃溝通指望的同屏顯示人數,根據不一樣的遊戲類型會有很大的差別。好比《崩壞3》不會同時有不少戰鬥單元,所以能夠給每一個角色很是高的精度,而像《御龍在天》這樣的國戰遊戲,追求百人同屏的效果,對於每一個角色的消耗限制就會比較大。

骨骼數量的部分最好在前期和美術溝通好,好比手指骨骼的減小和合並,腳部骨骼的簡化,來減小CS骨骼的數量,給飄帶等Bone骨骼留出足夠的空間。所以美術規範制定的過程並非單純的程序給美術添加製做限制,而是一個和美術一塊兒來討論如何在有限的資源下製做出更好效果的過程。

UI製做規範是咱們初期沒有特別關注的部分,也所以踩了一些坑:

1. UI組件數量較多致使加載頓卡。咱們有些複雜界面有上千個GameObject,幾百個UI組件,這是很是恐怖的。咱們測試發現UI的Prefab加載的時間消耗在Unity 5.5.6上和UI組件的數量幾乎成線性關係。這裏補充一張咱們測試的GameObject數量和加載時長的測試結果圖(橫軸是GameObject個數,縱軸是單純的Prefab加載的時間消耗,單位秒,測試環境:小米Max2):

所以建議其餘團隊提早考慮UI部分的異步加載和分塊加載。(或者升級引擎到5.6.5+或者2017.3+,對於Prefab的加載速度有了很大的提高)

UI中的粒子特效建議提早考慮異步加載,咱們最初直接放在了UI的Prefab中,不但由於Unity不支持Prefab的嵌套致使維護麻煩,並且由於每個ParticleSystem的初始化都佔用一個幾乎固定的時長(咱們本身測試在PC上也大約有3ms左右),致使UI初始化的時候很是卡。使用一個間接的引用,就能夠比較容易地作到既方便更新又能夠異步加載。

最後針對標準,要進行真機的壓力測試,而且在組內推廣,造成你們都承認的美術資源製做規範。

1.2 美術資源製做

在肯定好美術資源的製做規範以後,就是須要在美術鋪量製做的階段讓美術能夠按照規範進行資源的製做,在這個階段程序須要注意去作的事情我以爲有兩點:

  1. 幫助美術創建效率意識;
  2. 爲美術提供便利的製做和檢查工具。

在咱們這樣的一個初創團隊裏,沒有經驗豐富的TA存在,並且有部分美術同窗以前來自外包團隊,對於遊戲運行時的效率意識比較薄弱,所以須要客戶端程序同窗不斷和美術溝通來灌輸對於遊戲運行效率的關注。一般的作法包括平常的溝通、規範和技術點的分享等。

而在提供便利的工具方面,除了教會美術使用Unity已經提供的那些性能檢查工具(Batches和麪數查看、Overdraw、Mipmaps、Wireframe等渲染模式)以外,咱們也爲美術提供了很是多的開發和檢查工具。

從截圖中能夠看到咱們爲美術提供了大量的工具,選擇幾個來着重介紹一下:

1.2.1 場景鏡頭同步功能

在場景資源製做的時候,須要美術去關注的除了常規的Drawcall以外,還有Overdraw、Mipmap以及面數。Unity已經在Scene View下提供了這三種渲染方式的檢查工具,可是在咱們的使用中發現,因爲遊戲運行時鏡頭規則的複雜和多變,致使美術在Scene View下沒法準確判斷是否存在資源不合理的狀況。所以美術提出但願能夠在Game View下查看這些狀態。

最初的時候咱們也是想在Game View下實現這些不一樣的渲染方式,而且已經集成了OverDraw的檢測方式,基於的也是錢康來建議的Unite2017/OverdrawMonitor。可是後來以爲這種方式會影響美術對於遊戲的操做,程序實現起來也要多花一些時間,好比Mipmap的實現效果就不是很滿意。後來就想到另一個思路——在Scene View下來作鏡頭和Game View下的鏡頭同步。結果就很是簡單,只須要爲Game View下的Camera身上添加一個Component就好:

namespace ThorProfile { public class SyncSceneView : MonoBehaviour { #if UNITY_EDITOR private SceneView view = null; // Use this for initialization void Awake() { view = SceneView.lastActiveSceneView; } private void LateUpdate() { if (view != null) { view.LookAt(transform.position, transform.rotation, 0f); } } private void OnDestroy() { if (view != null) { view.LookAt(transform.position, transform.rotation, 5f); } } #endif } } 

核心的代碼只有LateUpdate中的那一句:"view.LookAt(transform.position, transform.rotation, 0f);",實現的效果就是正常操做遊戲,Scene View下的Camera能夠一直跟隨移動,視角和Game View下很是接近。(知乎對於gif上傳支持不太好,放一張靜幀圖感覺下,有須要本身使用上述代碼進行試驗便可。)

1.2.2 批量烘焙功能(補)

Unity的烘焙在Unity 5.X的版本速度仍是比較低,咱們雖然爲美術專門購買了CPU強勁的烘焙機,可是好比在製做大世界的時候,由於場景拆分得比較細因此須要一次性連續烘焙多個場景,因而爲美術提供了批量烘焙的功能。以前在知乎上也有朋友問過相似問題,代碼很是簡單,直接貼一下,須要的自取好了:

 [MenuItem("美術工具/烘焙選中場景(同步)")] public static void BakeSelectedScenes() { Object[] selectedAsset = Selection.GetFiltered(typeof(SceneAsset), SelectionMode.DeepAssets); foreach (Object obj in selectedAsset) { string scenePath = AssetDatabase.GetAssetPath(obj); Debug.Log("開始烘焙場景: " + scenePath); EditorSceneManager.OpenScene(scenePath); Lightmapping.Bake(); EditorSceneManager.SaveOpenScenes(); //若是有更新Prefab的需求,能夠放這裏。 EditorSceneManager.SaveOpenScenes(); Debug.Log(scenePath + " 場景烘焙完成!"); } } 

使用同步的方式烘焙,會卡住Unity,可是烘焙速度應該會有些提高(沒有對比過……)。

1.2.3 場景合法性檢查(補)

咱們爲美術添加了場景的合法性檢查工具,由於咱們對於場景中的相機設置等有些特別的要求。這塊跟具體項目相關,只羅列一下咱們在檢查的內容以及要檢查它們的緣由。

  1. 頂點格式,對於帶有color等邏輯上不須要的數據的部分給出警告。由於咱們的場景是採用靜態合批的,若是有資源不當心導入了color等不須要的頂點數據,會致使整個合併以後的mesh變大不少。
  2. 地形數據,咱們使用了T2M的工做流程,美術使用Terrain進行地表的製做,而後經過T2M插件導出成mesh,爲了保留原始的編輯數據便於之後修改和調整,Terrain會保留在原始場景裏,可是在發佈場景裏不容許帶有這塊數據,即便Disable也不行,會對場景加載帶來額外負擔。
  3. 光源參數,不容許設置光源的ShadowType的Resolution屬性,必須爲Use Quality Settings。在作UWA的深度優化的時候,咱們發現有些狀況下ShadowMap的尺寸從8M增長到了32M,檢查後發現是美術爲了更好的效果本身調整了這個參數致使的。脫離程序高中低效果控制的配置是不被容許的。
  4. 物理碰撞體,在目前的手游上,咱們的但願是儘可能少地使用物理,所以咱們不管是在尋路仍是在動態阻擋方面都儘可能少地使用物理,一樣在場景裏禁止擺放MeshCollider組件,對於其餘類型的Collider組件也進行檢查,對於咱們項目一般都是不須要的。
  5. 攝像機設置,咱們遊戲中鏡頭是徹底由程序邏輯控制的,所以不容許在場景中遺留用於預覽的Camera組件。

就像以前說的那樣,這部分很是零散,一般是在項目開發中不斷髮現的各類問題經過統一的檢查工具來讓美術在上傳以前進行自查,確保資源提交到svn上的時候就是正確的。

1.3 美術資源檢查

在美術大量產出資源的過程當中,除了美術自查以外,還須要其餘職位的同事同時對美術資源進行檢查。

在咱們項目中,對於美術資源的檢查主要有三個部分:

  1. 程序檢查。程序會根據發現的性能問題進行鍼對性的檢查,好比會使用Profile、FrameDebug工具等進行問題的排查。
  2. 咱們也是UWA產品的深度用戶,購買了專業會員,幾乎每月都會提交一份安卓版本的包讓UWA團隊幫忙進行在線的性能診斷與優化,頻繁的優化週期內可能會每週提交一份包。去年的年末也邀請UWA團隊來公司針對咱們項目進行了深度優化,發現和解決了很多美術資源的問題。
  3. QA每週的性能測試。每週QA團隊會在周版本以後,生成《性能測試報告》,以郵件的方式發送到全公司全部人的郵箱裏。

這裏以咱們的《性能測試報告》爲例來講一下QA團隊進行性能監測的內容。

1.3.1 包體大小監控

咱們經歷過在測試上線以前要進行包體大小優化的狀況,所以將包體大小的監控放到了每週的性能測試報告裏。

咱們會統計總體包體大小、資源佔用大小、按照場景、UI、角色、特效等分類以後統計各自的大小,以及若是包體大小有變更,主要緣由是什麼。這部分使用的工具主要是基於打包時候產生的中間文件來統計分析。以下圖所示,左側是按照文件夾分類的列表,右側是選中的節點下的全部文件,能夠進行排序、關鍵字過濾,以及多選統計等操做。

在進行資源大小統計和分析方面,咱們也開發了一套在Unity內的根據資源的引用關係進行分析的工具,統計對象爲全部要打包出去的資源,能夠查看資源之間的引用關係、被引用關係、資源消耗統計(粒子系統數量、GameObject數量等)。藉助這個工具能夠方便地進行一些再也不須要的資源篩查等分析工做。

1.3.2 遊戲幀率統計

另一塊須要持續關注的是遊戲在設備上的運行幀率。

咱們會分別在高配和低配機器上對戰鬥外、不一樣的戰鬥類型進行幀率的統計,並和以前的記錄進行對比。

1.3.3 具體資源的檢查

在具體資源的檢查方面,由程序提供儘可能簡便的測試和統計工具,由QA進行自動化的檢測,主要包括場景資源檢查、場景合法性檢查、技能特效檢查、UWA GOT性能數據等部分。

場景部分主要是統計Batch和麪數。咱們製做了一個工具,按照填寫的檢查點在默認鏡頭下統計四個方向的數據,同時截圖記錄。這個工具也提供了跳轉到指定座標直接查看檢查的功能。

前文已經介紹了場景的合法性的部分,這裏是由QA每週進行一次合法性檢查,對於不合法的場景反饋給美術進行修改。

特效檢查主要集中在Drawcall、描述以及粒子數量這幾個比較常規的方面,一樣會列出不合格的特效讓美術修改。

對於特效的Overdraw的統計,咱們是針對技能進行的,由於技能釋放過程當中會有鏡頭的軌跡,所以這種方式更加合理。工具的功能是逐個釋放技能,而後記錄技能釋放過程當中最大的那幀Overdraw的數據和截圖,方便美術排查。

咱們也讓QA團隊在時間充裕的狀況下使用UWA的GOT工具進行性能數據的記錄,方便程序進行對比和檢查。對於內存的統計數據也會從Overview測試中獲取。

PPT截圖的右下角是工具的製做者在咱們組內的外號。標註在這裏也說想說明這些工具的開發工做是整個團隊一塊兒來分工協做完成的,而不只僅是分享者的功勞。

1.4 小結

第一部分的最後,咱們作一下小結。通過規範制定、規範執行以及資源檢查這些步驟,美術資源的優化就造成了一個閉環,這個閉環中的各個環節是由不一樣職位的同事協做來完成的,程序在其中起到了穿針引線的做用。在規範制定階段,是由程序和美術主導,同時注意從策劃和運營等處收集遊戲設計的信息;在美術製做階段的主角是美術,程序則爲美術提供更好用的檢查工具輔助美術自查;資源檢查階段由QA主導,程序負責提供便利的自動化檢查工具,美術則須要對發現的問題進行修正,同時這個階段的檢查結果也可能反饋到規範制定的內容上,對一些規範細節可能會進行修正和調整。

2. 程序代碼優化

在聊完美術資源的優化以後,咱們迴歸到程序的部分,從一個比較宏觀的角度來看一下程序代碼的優化部分。

Donald Knuth在他的一篇文章裏說:

We should forget about small efficiencies, say about 97% of the time: premature
optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.

這段話是那句頗有名的——「過早優化是萬惡之源」的出處。首先我很贊同這句話,在沒有必要的狀況下進行盲目的優化浪費時間並且有可能有負面做用,可是正如這句話後半句所說,在少許的狀況下,咱們也不該該放過那些會產生嚴重影響的機會,而這些地方,每每是踩了坑以後才知道的,好比前文所說的美術大量使用四方連續的貼圖這樣的例子。因此這部分我想聊的主題內容着眼在「過早優化是萬惡之源」的另外一面,在程序中越早關注我以爲收益越高的部分。

  1. 底層模塊。這點比較容易理解,即使是在作好層次劃分的狀況下,越底層的模塊對於上層的影響越大,所以早期花費較多的時間和精力在重要的底層模塊上,對於後期的優化可能會有意想不到的收益。
  2. 代碼質量。我以爲保證團隊的代碼質量,對於優化和開發效率有着潛移默化的重要影響。
  3. 全員參與(補)。在一些團隊裏,優化主要由那麼1-2個資深的同窗主導進行,而在咱們的團隊裏,優化的工做是一件全員參與的事情。

2.1 底層模塊

首先來看下底層模塊,這一部分的重要性不言而喻,我想舉兩個例子來講明一下它們設計得好壞對於咱們項目的影響。

其中一個是我以爲咱們作得比較好的地方——Lua與C#的職責劃分,另一個是咱們踩了不少坑的資源管理模塊。

2.1.1 Lua與C#的職責劃分

Lua與C#的職責劃分可使用這樣一張圖來描述:

這裏涉及到的語言有C#、Lua和C三種,在項目最初期,咱們就決定將大部分的業務邏輯放在Lua層來作。這樣作的緣由和大部分使用Lua的項目同樣——爲了保證大部分的業務邏輯能夠被Hotfix以及Patch更新。同時咱們客戶端團隊有大量的Python腳本使用經驗,所以對於Lua來講上手也是沒有特別大的問題。

當決定了核心的業務邏輯存放位置的時候,數據的存放也就比較明瞭了。咱們遵循的一個設計理念是——

讓數據儘可能靠近它的使用者。

數據越靠近最終的使用者,中間須要進行轉換的CPU和內存消耗就越小。所以咱們使用Lua這種原生就支持數據存儲的方式,即Lua Table的方式來存儲客戶端使用的數據。

接下來是網絡部分。在項目最初的時候我也考慮過是否應當將網絡放置在C#層,由於對於Unity引擎來講,C#是更加原生的語言,對於一些庫的支持也特別方便。可是通過一些思考和討論以後,咱們決定將網絡放在C層,這樣作的緣由和數據放置在Lua層是同樣的。由於對於客戶端來講,網絡是數據的來源和發送者,至關於一個數據源的角色,所以也應該更加靠近它的使用者。不放在Lua層的緣由是網絡傳輸中仍是有很多計算量的,加密、內存拷貝等,所以放在C層效率更高。同時咱們在C層集成了一些Lua中缺乏的擴展庫。

選擇放在C#層的部分有這麼幾種:

  • 引擎功能,這個毋庸置疑,Unity自己就是經過C#來提供引擎接口的;
  • 計算密集型邏輯,對於一些可能涉及到CPU消耗的計算密集的邏輯,封裝成C#的接口供Lua調用;
  • Tick邏輯,即一些須要持續Update的邏輯,放在C#層經過Component的Update函數來實現;
  • 交互頻繁的邏輯,一些須要Lua和C#頻繁交互的邏輯放置在了C#層來實現,一樣封裝成接口供Lua調用。

這樣的設計依據另一個設計理念——

將Unity做爲引擎層來使用,讓C#層儘可能少地關注具體的業務邏輯。

在這樣的設計下,C#和Lua之間的交互就很是清晰:

  1. C#經過tolua# warp出來的接口讓Lua調用,Lua是邏輯的驅動者;
  2. C#提供每幀一次的Update/LateUpdate調用,具體內部須要的分發由Lua本身來作,大量的間隔邏輯經過Timer模塊來實現,減小每幀的tick邏輯;
  3. C#對於Lua的感知僅僅是一些異步操做的回調,好比按鍵點擊事件、異步加載完成的回調邏輯。

在這樣的設計之下,UWA對咱們項目進行深度測試的時候給出的測試報告結論以下:

UWA團隊告訴咱們在他們測試的重度項目裏,這個數據已是很是好的了,咱們項目在這塊也只使用本身開發的調用次數統計工具進行常規的優化,在項目後期並無花費太多的時間。

同事增練開發的調用次數統計工具

這裏提一下跨語言編程的時候關於對象/資源生命週期的設計理念:

誰建立誰銷燬。

簡單明瞭,若是一個對象是由Lua建立的,那它必定要由Lua來顯示地負責銷燬;而若是一個對象是由C#邏輯來建立的,那必定由C#來進行銷燬。只有這樣纔可以避免生命週期錯亂致使的泄露或者提早銷燬的錯誤,對於泄露的檢查也更加明瞭。

2.1.2 資源管理模塊

咱們項目中的資源管理模塊是我以爲因爲最初設計不夠致使後期踩了不少坑的一個部分,其中一個表現就是每次遊戲要進行測試上線以前都要花時間解決加載模塊的各類奇怪問題。咱們熬夜修復的問題有這麼一些:

最初的版本由於一些迭代致使資源的引用計數存在問題,出現了Asset被卸載了可是又被使用的狀況,再次嘗試加載資源就報錯了。後來又發現很是嚴重的內存泄露,表現是幾乎全部的資源都殘留在了內存=_=……爲了修復泄露問題,咱們將底層AssetBundle的管理從原來的Unload(false)修改成了Unload(true),雖然解決了泄露問題,可是須要上層邏輯有一些迭代工做。後續咱們還嘗試處理同一個資源在異步加載過程當中有同步加載請求的問題。

如今回頭來看,對於資源管理模塊能夠進行反思的內容有以下幾點:

最初的時候因爲團隊內對於Unity的經驗不是很足,面對資源管理模塊這個很是重要的部分,想法是藉助比較成熟的開源框架來彌補經驗上的缺失。因此在大體瞭解了基本原理以後,選擇了KSFramework這套開源框架。它對於資源管理模塊有一套基於Loader設計的封裝,咱們又根據本身的需求和發現的問題進行了一些迭代工做。在初期編輯器模式下,這套東西幫助咱們快速創建了Demo和推動前期功能的開發,可是也隱藏了不少設備上的問題。這應當說是很是標準的技術債務,只是沒想到須要付出這麼高昂的利息。。。

在後期的維護中,由於技術團隊的擴張和一些「不可抗力」的緣由,這個模塊前後經手三個負責人的維護,在交接以及討論中由於理念不一樣也產生過一些設計上的誤解,埋下了一些問題。

最後,由於編輯器模式下沒有使用異步加載的方式,所以運行邏輯和設備上是不一樣的,致使不少異步的問題在真正進行大範圍的真機測試的時候才暴露出來,須要在比較高壓的條件下進行修復,帶來了不少挑戰。

最後,想說的一個點是——

即時面臨很大的壓力,對於一些奇怪的問題,不要嘗試用一些臨時手段進行掩蓋和容錯的方式進行處理,而是儘可能地去找到問題產生的根源,從根本上進行解決。

有時候在沒搞清楚根本緣由的狀況下貿然經過「補洞」的方式來進行問題的修復,可能會把坑埋得更加深,讓問題更難復現和排查。我在項目中就經歷過這樣的狀況,也都是血與淚的教訓。

2.2 代碼質量

代碼質量的重要性我依然想講一個我在《無盡戰區·覺醒》這樣一款手遊開發項目中的例子來進行說明。

在項目中後期,咱們進行Python層性能優化的時候發現:__dict__這樣的屬性訪問佔用很是高。用過Python的同窗可能都知道這是Python中進行屬性訪問的方式。排查調用源發現不少優化的點都是相似上面這樣的局部變量的優化。(圖中的代碼只是示例,並不是真實代碼。)

而對於每個入職的同事,在進入公司的Python課程裏,都會學習到在腳本語言中儘可能使用局部變量來進行性能優化的方法和原理。然而在真正的項目開發中,仍是會有不少人忽略這種優化,這是代碼質量偏低的一種表現。

在接下來的三四天時間裏,咱們不斷地profile、修改,對__dict__性能消耗比較大的地方使用局部變量的方式進行性能優化以後,整個腳本的性能有了大約10%-20%的性能提高。這是很是大的一個優化了,並且徹底是無損的優化。若是咱們的開發人員能夠在平常的開發中就注意維護代碼質量,對於這些優化時間的消耗就能夠節省掉很多。

在我看來,在項目開發中能夠提高程序團隊的代碼質量的方式包括以下幾個方面:

  1. 針對性的培訓和按期的技術分享。技術分享可能會花費挺多的時間,可是在時間相對寬鬆的研發期堅持進行技術分享仍是會給團隊帶來有多正向的收益。咱們一年多的創業時間內,技術分享大約作了十幾場,雖然和大廠的分享相比不算不少,可是在促進團隊技術進步、提高代碼和設計質量等方面仍是起到了很好的做用。
  2. 代碼Review。也有很多人和我討論過在團隊內進行大範圍的Code Review的可行性。首先Code Review對於提高代碼質量確定是有很大幫助的,可是從我我的的項目經驗來講,要在手遊這樣一個須要快速開發迭代的團隊裏推行嚴格的Code Review代價仍是很是大的。好比工做壓力比較大的狀況下,咱們一個同事可能會在一天產出上千行的Lua代碼,若是想要另一個一樣有這樣大工做壓力的同事抽出時間來進行完整的Review,幾乎是一件不可能的事情。所以咱們選擇只在關鍵節點進行Review,包括核心代碼和線上Bug修復代碼,以及新同事入職的第一個月提交的代碼。咱們有過一次集體Review和迭代的過程,對於項目中會由多人共同維護的一段邏輯,你們都花時間進行迭代,而後分享本身迭代的思路。這種方式雖然會花費團隊挺多時間,可是偶爾針對特定代碼進行仍是比較有效果的,能夠統一你們對於關鍵部分代碼的設計理念和使用方式。
  3. 靜態分析工具。這塊咱們在使用的有LuaChecker和UnityEngineAnalyzer,針對代碼進行檢查,能夠發現一些優化的點。

2.3 全員參與的優化(補)

咱們客戶端程序團隊在進行優化的時候和一些團隊不一樣的作法是你們都針對本身負責的部分進行優化。這樣作和團隊自身的特色有關,咱們客戶端團隊對於一個創業團隊來講算是經驗和技術能力都不錯的一個團隊,每一個成員都有多年的遊戲開發經驗。所以每個同事都負責一些比較底層的模塊,也會負責各個玩法系統的開發,是一個縱向的結構。總結起來,讓全部同事都參與優化的好處主要有:

  • 讓團隊中的每一個人創建優化意識;
  • 每一個人做爲本身負責模塊的優化負責人;
  • 組織專門的優化週期,橫向對比,互相學習。
組織專門進行優化週期的Evernote記錄

3. 團隊開發效率優化

終於來到第三部分,也就是以前說的看上去和性能優化並無直接關係的團隊開發效率優化。

在聊這部分以前,我想讓讀者思考一個問題——咱們爲何要作優化?

是爲了讓遊戲的運行更加流程?讓遊戲更加省流量?更省電?讓遊戲包體更小?這些都是咱們進行優化的目標,但歸根節點,咱們作這些優化的目標都是——爲玩家提供更好的遊戲體驗。

因此在我看來,若是一個優化,不管使用多麼高超的技巧,若是它的優化結果沒法直接或者間接地被玩家感覺到,那這個優化可能就只是一個程序員的「自嗨」,沒法爲遊戲提供真正的價值。反過來講,若是咱們能夠優化團隊的開發效率,讓團隊有更多的時間來開發新的功能、製做更多的遊戲細節,那對於遊戲來講也是一種優化。

所以在我看來,進行團隊效率的優化是一件很是重要的事情,也是程序的職責之一。我主要想從這樣三個方面來聊一下如何進行團隊開發效率的優化:工做流的構建、程序團隊、策劃團隊。

3.1 工做流的構建

我以爲在項目中構建更好、更順暢的工做流能夠很大地提高總體團隊的工做效率。我以咱們團隊如今一個功能的完成流程爲例來分享一下咱們團隊使用的工做流。

  1. 策劃提早和程序、美術溝通需求的可行性,在可行性肯定以後,經過Redmine這樣的管理軟件提單,將需求詳細地描述在任務單裏;
  2. 咱們在Redmine中集成了Webhook的功能,當有任務提出的時候,Redmine會經過釘釘的接口通知到對應的程序;
  3. 程序根據本身手頭的工做安排進行排期和功能實現,當任務單完成並進行自測以後,會將代碼提交到svn上,同時將Redmine上的單子修改成「已完成」的狀態,狀態的變動會一樣通知到相應的策劃和QA;
  4. SVN經過SVN hook的方式,自動觸發Jenkins的Lua代碼編譯指令,Jenkins調用咱們部署在公司內網的一套分佈式打包服務,進行腳本編譯。咱們團隊中只有程序有Lua代碼的svn訪問權限,其餘職位統一使用編譯好的Lua bytes code。
  5. 當打包完成以後,分佈式的打包服務會調用釘釘接口將完成消息通知到特定的羣裏。
  6. 策劃須要進行導表、更新服務器,或者QA同事須要進行安卓/iOS打包的時候,都是經過Jenkins進行請求,Jenkins繼續調用分佈式打包服務進行打包,並將結果通知到羣裏。
Jenkins上的部分服務
對於Jenkins部分,提醒一下要作好權限控制,對於其餘職位可能須要的,儘可能避免參數式的執行方式,而是以多個任務的方式提供。而程序部分則能夠儘可能靈活地使用參數進行構建。對於發佈版本打包、分支建立等功能,經過權限控制不要讓策劃/美術/QA誤操做點擊到。

咱們的分佈式打包服務是基於Python構建的,經過簡單的RPC服務進行內網跨機器的互聯,經過argparse模塊進行參數化的提供,方便擴展:

def ParseArgs(args): parser = argparse.ArgumentParser(description = 'Build App') parser.add_argument('-p', '--platform', choices=('android', 'ios',), required = True) parser.add_argument('-c', '--channel') #all, xiaomi parser.add_argument('-b', '--build-type', choices=('dev', 'pub',), default="dev") parser.add_argument('-sp', '--spmark') parser.add_argument('-hm', '--headmark') parser.add_argument('--non-sdo-server', action = 'store_true') parser.add_argument('--nopatch', action = 'store_true') parser.add_argument('--onlyab', action = 'store_true') parser.add_argument('--uwashipping', action = 'store_true') #單獨爲uwa測試準備的發佈參數,臨時添加 parser.add_argument('--make-base', action = 'store_true') parser.add_argument('--il2cpp', action = 'store_true') parser.add_argument('-xp', '--xcode-profile-type', choices=('development', 'addhoc', 'appstore',), default="development") buildArgs = parser.parse_args(args) 

我以爲這樣的工做流的好處主要有:

  • 程序將更多的事情推出去,交給工具,本身能夠更加專一在程序開發的工做上;
  • 其餘職位擁有更多的自主權,在不須要程序參與的狀況下能夠完成本身的不少工做;
  • 經過釘釘這樣的IM的通知功能,將輪詢的消息變成通知,再也不須要等待和關注Jenkins任務的完成進度,完成以後天然就會收到通知。

3.2 提升程序開發效率

這塊基本都在PPT裏了,不贅述了,其中調試工具部分再次推薦一下:Hdg Remote Debug這樣的設備調試工具,關於Lua的部分在3月份的博客中已經說得很是詳細了,也再也不重複。

3.3 策劃工做效率優化

策劃工做效率的優化部分想講兩個切身經歷的事情。一個是很是小的一個優化,幫助策劃實現NPC座標從Unity中拷貝到Excel中。

咱們由於開發週期比較緊,並且服務器須要一些NPC的位置數據作驗證,所以沒有在Unity內部爲策劃實現NPC編輯器,而是須要策劃手動去Excel表裏填寫。這裏就有一個填寫座標的過程,最初的時候策劃手動填寫很是費時間,並且容易出錯,後來幫助他們實現了一個點擊GameObject節點拷貝座標到粘貼板的功能,策劃使用後表示極大地提高了填寫NPC表格的工做效率。

有時候程序只須要經過很簡單的代碼就能夠幫助其餘職位的同事解決一些工做中的痛點,提升工做效率。

第二件事情是以前在大公司工做的時候的一個親身經歷。當時在帶新人作mini項目,一個新人策劃就在公司的KM知識分享平臺上提了一個問題——他表示如今的策劃填表的工做效率很低,須要經歷這樣幾個複雜的步驟:

在Excel中編輯數據,而後提交到SVN上,經過導表將數據轉換成程序代碼讀取的資源,而後更新服務器,更新客戶端,啓動客戶端鏈接服務器才能查看結果,這些步驟要花費大約10-20分鐘的時間。他問可否編寫完數據以後就能夠直接在遊戲內看到結果?

當時的我做爲自覺得在遊戲行業已經有幾年工做經驗的「過來人」,看到新人策劃有這樣的疑問,內心實際上是有一些嘲諷的。因此去「耐心」地回覆他:對於客戶端來講,能夠作到本地導表而後不重啓客戶端就能夠直接Reload數據查看結果,可是若是你不把數據上傳到svn上,服務器如何知道你本地修改的結果?這就像那樣一個笑話:

「是這樣的,張總, 您在家裏的電腦上按了ctrl+c,而後在公司的電腦上再按ctrl+v是確定不行的。即便同一篇文章也不行。不不,多貴的電腦都不行。」

這個笑話後來的結果是本身成了一個笑話,由於雖然時代的發展,網絡硬盤等雲服務的普及,也有了跨電腦進行粘貼拷貝的功能……張總再也不須要很貴的電腦就能夠實現本身的操做。

這個故事的發展和這個笑話有些類似,在大約半年以後,我和工做室的另一個同事將rpyc這樣一套中間件引入公司並基於它實現了跨進程的外掛式編輯框架。基於這套框架就實現了策劃在編輯器內編輯完數據,只須要點擊重載數據的按鈕,就能夠自動更新本地的客戶端和指定ip的一臺服務器中的數據,再也不須要提交到svn,甚至不須要重啓客戶端就能夠看到修改以後的結果。

通過樣的改進以後,以前須要10-20分鐘左右時間的操做,如今只須要2-5秒就能夠實現,極大地提高了策劃的工做效率。我和那位同事也所以拿了當年公司內部的技術分享獎。

這個故事對於個人觸動還蠻大的,由於最初我所嘲諷的一個新人的想法,最終由我和另外的一個同事一塊兒進行了實現,這對於我來講也是一種諷刺。所以在以後我再聽到策劃或者其餘職位的一些看上去「異想天開」的想法的時候,不會急於反駁或者指出其中的漏洞,而是先想一想是否本身的思路被本身瞭解的技術所禁錮,是否有別的方式能夠真的實現這些想法。

經過這兩個故事我想表達一個觀點,對於程序在團隊效率優化方面應當承擔什麼樣的角色?借用《蜘蛛俠1》裏很是有名的那句話來講——能力越大,責任越大。

由於程序是整個團隊中最瞭解技術和開發的人,也最有能力開發一些工具或者引入一些方法讓整個團隊的工做效率獲得提高,所以也應該肩負起相應的責任。

 

4. 總結

最後,咱們聊了這麼多,進行一些總結。

我在遊戲行業裏也作了五六年,特別是本身在創業的這一年多的時間,讓我更加深切地感覺到遊戲開發很是符合這樣的冰山理論。

浮在冰面上的這一部分是玩家能夠感覺到的遊戲內容,好比精緻的美術資源、有趣的玩法,而在這之下,有更多沒法被玩家直接感覺到的內容,好比被迭代掉的玩法。而今天咱們所聊的這些優化的內容,好比美術規範、代碼質量、團隊的工做流構建,它們大都是水面如下,沒法被玩家切身感覺到的部分。但它們又是如此地重要,是整座冰山不可或缺的一部分。

就像我以前所說,經過剛纔的分享你們應該也能夠感覺到,這些優化的內容很是的瑣碎繁雜,就像散佈在各個地方的一個又一個點,是團隊的協做讓這些點能夠鏈接成線,造成相似於美術資源的規範制定、規範執行和規範檢查這樣的閉環,而在整個遊戲的開發週期過程當中經過團隊鍥而不捨地去作這些事情,讓這些線鏈接成一張大網,將水聚攏在周圍凝結成冰,托起了整座冰山,使得海面上能夠被玩家感覺到的內容愈來愈多,這就是我眼中基於團隊的持續優化之道。

最後的最後,我仍是想把我在上次分享中也說過的一句話送給你們。

這一年多的創業經歷讓我更加深入地體會到遊戲開發是一件艱難並且辛苦的事情,有一些朋友或者同事也找我聊做爲一個程序進行遊戲開發的迷茫,我本身心裏也曾有過彷徨和糾結。由於不少事情太過瑣碎,帶給咱們的成就感可能也會偏低。可是我也發現,如今作過幾年遊戲行業以後,依然留在遊戲行業中的人心中都有着對於遊戲發自肺腑的熱愛和激情。它們或從小就喜歡遊戲,或曾經被遊戲感動過心中最爲柔軟的那個部分,在遊戲行業內堅持作這些着看似平凡的工做。

因此,我想把這句話送給全部依然在堅持的遊戲開發者們——不忘初心,不愧平凡,相信經過團隊的協做和堅持不懈的努力,能夠給這份平凡以不凡!

謝謝!

 

2018年5月17中午 於杭州海創園 心光流美公司

相關文章
相關標籤/搜索