使用unity引擎時有哪些禁忌?
美術和程序兩方面描述
補充,感謝評論區留言 @周源@張章(為什麼找不到……),之前做動畫的時候沒有發現這麼好用的介面,試了一下只要建立AnimatorOverrideController,引用模板的AnimatorController就可以直接創建成功了。
看到這個想到的並不是什麼禁忌,Unity引擎的很多問題樓上各位都已經講解差不多了,我就補上幾個和遊戲開發相關的優化方案,應該是任何引擎都適用的。
尤其針對初創團隊,沒有核心的技術骨幹時,經常會走一些彎路,有時候遊戲做出來Demo了效果很棒,但是要開始上線運營了,要修改資源和擴展功能做適配的時候,開始頭疼花太多的時間去處理資源,往往一優化就是一兩個月。
我講一些簡單卻有必要的點吧,概念本身沒有任何的技術難度
1. 資源命名
資源命名其實很多朋友忽略掉了,覺得資源命名沒有必要。但是其實這個很有必要,因為我們在項目後期會涉及到批量操作資源,比如我們要將某系統的圖片格式資源批量替換格式呀,我們要將動畫批量創建動畫控制器呀。還有一些程序自動化處理一些工作,比如我們對某種格式命名的資源設置不同的參數。例:圖片根據後綴名字不同設置不同的圖片格式。這是非常節省時間和避免人為出錯的方法。
(輸入eff就出來和特效有關的所有貼圖資源,後面為該圖片是否帶有透明通道等後綴)
(有了規範命名直接可以在編輯腳本裡面批量設置好格式了)
(遊戲中的角色動畫總是有很多狀態和連線,如果每個都讓美術人員來連線設置可是要累死人的)
(而只要我們做好一個正確的模板,美術的動畫Clips命名和第一個模板的命名一樣,我們就可以腳本批量控制直接拷貝複製參數就可以了,後期如果要修改成批的動畫控制器,只用修改模板的屬性批量轉換一次就搞定)
(同時載入資源的時候也非常方便,只要命名的結構合理,我們就可以直接傳入類型,讓功能函數幫我們同時載入好資源並掛載相應的腳本)
2. 文件夾規範
很多團隊的文件夾其實很多時候都是亂的,尤其是美術資源。
如果項目需要做熱更新功能,都會無法避免的面臨如何分包資源的問題,這個時候文件夾規範就尤為重要了。常見的方法是根據單獨文件夾作為一個AssetBundle包,如果文件夾混亂,資源位置混亂,打的包將會互相依賴嚴重,導致載入資源緩慢,資源冗餘,更新數據量大。
即時我們的遊戲不做熱更新功能,後期也會刪減掉很多無用冗餘的美術資源。而文件夾混亂的話你將會很難去把舊的資源剔除出項目。如果各位有太多冗餘資源在項目里,這可以介紹一個簡單的剔除方法。將你不用的資源從Resources目錄下剔除,右鍵Resources依賴打包unitypackage,然後刪除所有美術資源重新導入unitypackage(當然得做好備份,避免刪除重要文件)。
(如果要剔除某個不用的特效只用刪除該個文件夾就好了,當然特效的貼圖如果是公用的是放公用文件夾下)
3. 不要直接用Unity的介面或者資源。因為Unity底層並不開源,我們要優化某個公用資源或介面,如果之前沒有做一個中轉介面就會變成全部人工替換。這裡主要舉例Shader,Shader應該是我們自定義的。就算我們用的和Unity的功能是一模一樣的,也要複製一份出來改成我們自定義的Shader。這樣後期做優化,就不用新創建好Shader讓美術一個一個給成千上百個材質球重新替換Shader。
(實現效果和Unity是一樣的,只不過我們重寫了一次)
(後期如果要做擴展和優化,就可以在我們自定義的Shader中修改了)
最後……
遊戲開發不易,且行且珍惜。
其實程序方面的禁忌還是挺多的。
先說一個比較常見而又冷門的:unity編輯器卡死。
早期開發由於很多的不規範,所以導致unity卡死情況是很常見的,最多時一天能卡死十幾次。所以後來就總結了些經驗了。
1.線程導致。如果在遊戲關閉了,子線程沒有關閉,那麼在下次遊戲啟動時,很大情況下會卡死。
而往往有些線程,遊戲不關閉是不會關閉的,例如socket的線程。解決方案是,讓一個MonoBehaviour伴隨遊戲的生命周期,然後在移除的時候關閉線程。
2.死循環導致。這個不用解釋了。
3.VS調試導致。偶爾出現,任務管理器顯示VS內存在增加,等一段時間有幾率正常。初步認為是unity在等待VS響應,所以卡住了。
解決方案是停止調試。
極端情況是VS也未響應,這時候應該要關掉VS,因為unity很有可能有未保存的修改。
4.大量log導致,偽卡死。log輸出是挺耗時的,如果過多,會導致你整個界面卡住。
這種情況可以看任務管理器進行排除,如果是內存一直在增長,那麼就不是卡死了。
解決方案,減少不必要的log,對update方法的邏輯排除,盡量避免出現大量的log。
5.開啟遊戲時卡死。有些時候場景初始化時間過長會讓人以為是卡死。
建議適當的把初始化的邏輯分幾幀執行,最好加進度條或者標識。
手機碼字,先總結到這裡了,有時間再填坑。
6.運行時狀態,修改代碼,然後觸發Unity重新編譯,此過程可能會涉及大量代碼序列化和反序列化,等待響應的時間有可能會比較長(偽卡死)。也有可能有些對象不能序列化,然後再編譯完成後,運行的代碼會出現大量訪問空對象錯誤,大量的Error輸出,導致類似4的情況。
個人目前只在做2d遊戲,而且是初學,回答很具有局限性,甚至有可能有錯誤,見諒。
程序上
1 2d遊戲不分層靠改z坐標分前後,剛開始可能可以用,之後多半會出問題,比如粒子特效之類的改z坐標不現實。
2 在繼承了MB(MonoBehaviour)的類里,進行一次巨大的內存申請(new等方式),在MB里申請內存好像是不會「自動」釋放,也不會自動還給系統。一次巨大的new之後就會一直佔用著了。
感謝評論區提醒,注意是不會自動釋放的,因此要手動做GC(Garbage Collection),個人用的方式是在切換場景的時候調用System.GC.Collect ();注意這一定不是最好的方法,個人水平有限,只能暫時避免使用過多new。
3 在Update里進行複雜循環,計算,new大量東西每一幀申請大量內存等。
4 一個物體上掛了幾十個通道幾十個腳本。在風馬牛不相及的物體上掛腳本。
5 完全不懂優化,例如圖片等美術資源不壓縮就丟進遊戲,放進去沒做圖集等等。或者壓過頭造成破壞性壓縮,比如產生嚴重鋸齒,圖片顆粒等等。
6 字元串直接「加」字元串。
7 完全不懂design pattern和設計準則,一個腳本幾千行。或者每個腳本就幾行,一個小遊戲弄了幾千個腳本。(如果代碼都是有效的話,相比於前者來說無傷大雅。)
8 不會命名法,什麼中文拼音,英語甚至日語羅馬音全混在一起,變數就叫a b c d aa1什麼的也不加註釋過一天自己就看不懂了。或者同樣的物體叫完全不同的名字,同一個名字的物體在同一個場景里有一大堆。
9 程序耦合度過高,隨便需要改變或者刪除一部分功能就會導致大崩盤。
10 無腦繼承 MonoBehaviour(某些極端簡單的2d遊戲可以全部繼承MonoBehaviour) 和無腦黑MonoBehaviour而完全不繼承MonoBehaviour。
11 無腦濫用插件。
12 Hierarchy里東西亂放,沒有考慮好層級,所有東西都堆在最上層啊,沒有空的遊戲物體做上級啊等等。
補充:有幾十層級的遊戲物體,把腳本藏在幾十層的物體上,
13 雖然MonoDevelop一類是有自動對齊的,但是還是有人三五句程序堆在一行啊,不明所以的換行啊等等排版出現問題的情況。雖然不會直接影響程序運行,但是可讀性大幅度下降。
14 完全不進行版本管理。
15 沒有備份的情況下做一些危險或者不可撤回操作,例如delete。
16 完全不做繼承,相同的功能或者稍微有改動的功能重複寫了數遍。
17 稍微複雜的功能沒法實現的時候,用一些邪門方法解決,可能當時可以,之後必定會出現嚴重問題。(比如預想是某個計數器到10出現新一波敵人,不小心在三個不同的地方重複計算了這個計數,然後沒發現具體原因,只看到結果變成了12,就把「等於10」的條件改成「大於10」甚至「等於12」)
18 完全不關心優化,例如一個小遊戲做成幾G容量+運行時佔用將近1G內存(我真的沒黑槍某遊戲)。
補充:
19 使用數組之前不new。
20 無視自己看不懂的warning信息甚至無視自己看不懂的error信息。
21 不會排版,例如對齊,空格,換行,雖然沒python要求那麼高,最基本的排版要有。
22 無腦用輪子和無腦排斥輪子,比如完全不懂輪子怎麼用的,湊合當前測試通過就用了。還有一種是覺得別人輪子都不如自己,不進行任何測試就重造輪子,結果往往不如原來的輪子(得到廣泛認可的輪子多數情況下是有理由的),浪費時間浪費精力。
23 完全不會注釋,或者希望通過注釋改善原本質量很差的代碼。
24 代碼一行無比長,不換行不優化命名。
25 無腦用別人shader,自己連為啥shader里常用的向量點乘具體意義都不懂。
26 我自己碰的一個禁忌,有一次往git上commit,有一段時間沒傳+自己大量改動+另一個人大量改動,導致merge時間以天計數了,不得已只能新建一個分枝來用。應該是每次工作有了階段性進展,或者每天下班時,最好commit一次,不要攢一周兩周再傳。
27 壓縮圖片紋理的時候,往ios包里用僅android支持的格式(例如ETC1),反之亦然。而且好像這個錯誤某些情況下會被隱藏?
28 認為程序做好自己代碼就行,完全不進行溝通。
29 完全不會數學,比如多數簡單手游移動都是只會直線,好一點的有非完美曲線(例如人工添加無數個中間點)。貝塞爾曲線這樣基本的數學要會一點。
30 程序中很多不聲明的常數,例如Attack=Attack*1.3,這個1.3是個buff量,實際上最起碼要AttackBuff=1.3;
Attack*=AttackBuff;
否則很容易過幾天就忘了這個1.3是幹嘛的了。
美術上
0 盜圖盜素材等一系列非原創行為。
美術上其實不是很容易到「禁忌」的程度,以下幾乎都不一定能算禁忌
1 做的圖含透明部分後大小不一,還要程序員重新修正到透明最小(萬一有不含透明部分都大小不一的那真的算禁忌了)或者修成大小一致。尤其按鈕和血條進度條等,附了一大片透明就會有不大不小的麻煩。
2 比如類似相框一樣的只有邊框,中間全透明的,個人認為最好做成4個框拼接,中間透明的部分不要。
3 比如按鈕等等只需要給原來顏色版本和如果需要的特殊變化版本等,不要只給一個按下去變灰色之類的特殊版本,單純只整體變顏色unity程序員就可以做到,只給個灰色的還要開ps去擦。
4 如果不是只使用一次的文字,最好做成字體,否則程序就變成從控制字元串變成控制圖片,而且之後如果又要追加文字又要重新做。
5 跟程序一樣的問題,瞎命名,叫a1 a2 未命名png 什麼的,雖然比變數名直觀一點看一下能看出來是什麼,但是管理起來還是很麻煩。
6 完全不懂程序也很麻煩,比如一個按鈕含按鈕和裝飾的部分,然後美術把這兩個做成一張圖了,就可能要返工。或者比如無腦加mipmap,雖然程序點一下就能取消掉一個,甚至可以寫腳本取消,但是確實增加了工作量。
7 配色不統一,甚至配色水平不如程序員的也是有見過的...風格不統一,分了好幾批人做等等。
8 完全不懂渲染和優化,粒子數巨多,還開碰撞什麼的。
9 沒考慮是在什麼平台上用的ui,做的解析度過大或者過小,又沒做成九宮格。
10 剪的素材不合理,比如把粒子特效和背景做在一張圖裡的,要粒子特效素材結果給了整體中間狀態圖。又比如說「攻擊力提升」「防禦力提升」的藝術字素材,並且「提升」兩個字一模一樣,個人認為應該是剪成「攻擊力」,「防禦力」,「提升」三個素材,但是往往收到的是「攻擊力提升」和「防禦力提升」,力和提中間有的時候又隔著很長的透明部分,就要程序重新修,等等。不過出現這種情況大多是因為中間溝通不暢導致的。
11 不算是禁忌,純色背景做素材了,萬一碰到不懂的程序還以為這有什麼玄機加到素材里又浪費空間。
12 不算是禁忌,完全一樣的或者高度相似的素材重複給了非常多個,比如有上述問題程序自己修正好了,換一個scene發現又一堆要修正的一模一樣的素材,就有點煩了,也是增加工作量的情況。
13 不算是禁忌,在光照極強或者背景很亮的情況下用粒子特效,可能會導致效果變差。這種情況下注意選擇粒子的shader,例如自帶的alpha blended。(僅限2d遊戲,3d遊戲做法沒研究過...)
補充:
14 注意一張圖裡各個部件分圖層,例如光照,對應陰影,背景,衣服等等,需要改需求或者動態光照等等情況下會很少很多麻煩。
15 一定要和策劃或者程序溝通好是在什麼設備上用的,定好畫板是 多少*多少,否則很可能所有ui做出來比例都不一樣,導致要重做。
程序:
1、不少後期優化的東西其實可以在前期通過制定規範來讓整租人規避掉。其他答案中提出的一些禁忌,在官方的一篇博文中已經提到過:Unity - Optimizing scripts in Unity games 垃圾回收的可以看看這篇: Unity - Optimizing garbage collection in Unity games
2、繼承自MonoBehaviour的類不要使用構造函數。
3、避免使用單例。評論里有人問到為什麼要避免使用單例的問題。我簡單說一下好了。首先單例都是靜態對象,在單例中載入的資源和new的對象都要注意在適當的時候回收,不是所有人都能注意到的,也有管理成本。這點就容易造成內存泄露等問題。其次單例比較難做到完全解耦。比較典型的是時序耦合。假如你考慮到了前面說的回收問題,在適當的時候做了回收,比如調用的單例的Clear介面。但單例提供的就是全局對象,假如你有其他單例或對象還需引用該Clear單例的全局對象,或者存在A這個單例必須在B這個單例Clear之前Clear都是比較難管理,也很難一眼看出問題的。更多的單例的利弊,可以看看《Game Programming Patterns》關於單例模式的描述:Singleton · Design Patterns Revisited · Game Programming Patterns
4、做好Component的緩存,不要每次用到都調用GetComponent。
5、shader盡量程序搞定,浮點數盡量使用低精度。其他有關渲染的可以看看官方的這篇文章Unity - Optimizing graphics rendering in Unity games
6、如果使用git做版本管理,注意lineending字元的坑,這裡提供一個討論:GitHub 第一坑:換行符自動轉換 · Issue #22 · cssmagic/blog
美術:
1、如果目標平台是移動終端,美術效果不要太華麗並做好分檔,高中端機型使用不同的美術效果。
2、模型注意減面,否則容易出現gpu瓶頸。不少移動端cpu和內存都夠用,但是gpu比較渣。
先想到這麼多,手機碼字,連接稍後再給。
-------------------2017年6月29日12點16分更新了為啥避免使用單例的淺見和一些外部鏈接
既然是講禁忌,我就講一個:千萬別把工程放在機械硬碟上。慢得那是一批吊糟。
我手上這個項目,資源弄得很亂,場景載入的內容很多。之前每次用Unity開遊戲,都會在場景切換時卡好久。一直百思不得其解,後來把工程移到SSD上,如絲般順滑。先說美術:使用過多particle system
然後程序:過度依賴mono behaviour
在Update裡面寫GameObject.Find(「xxx」).GetComponent&<&>();
禁忌多了去了。
不能開線程,aji,一個21世紀的引擎了。居然還有main thread的報錯,不上線程安全的靠用戶保證不就行了。(哪怕你在一個非「main thread」的裡面訪問紋理的寬度和高度都不允許)
當然在手機上把CPU跑滿,一會就過熱性能BOOM了。
而這一點在PC上極為討厭,四核八線程用Unity編輯器打包也好,導入也好,Player也好,其他七個線程永遠都在看戲。可謂是垃圾中的垃圾了,他能打包流程很多地方多少能並行處理的,這也導致了一系列的注意事項。
實現/設計垃圾,SteamingAssetsPath在安卓下強行打入apk中,在安卓平台的時候,WWW讀取偷懶走Java的讀取文件實現,這樣就導致一個問題:
為了避免垃圾Java實現導致的Java堆內存增長,在Resource下和StreamingAssets下放置的東西越少越好。(我們遊戲是採取了再次壓Pak,分析天龍八部的Apk包,看起來也是做了類似處理,不知道是不是這個原因)
其他因為MONO導致的不能用foreach這種就不提了。
諸如Vector3/4的構造函數更是呵呵噠一樣,
隨便說幾個吧,不一定對:
1. 濫用物理。判斷個距離的事,用碰撞檢測,控制人物運動用addForce,射個子彈也用addForce ...
2. update 里頻繁調用GetComponent
3. 各種靜態,各種單例(主要是說他們引起的高度耦合)
4. 500行的函數,而且還是一堆if else 連起來的
5. 動態光
把可口可樂放在滑鼠線上
大部分時候寫遊戲腳本基本不需要考慮多線程的問題。
但是,UGUI是多線程的。
所以寫擴展的時候一定要記住是多線程的,是多線程的,是多....
唉,目前正在填坑,說多了都是淚啊。
禁忌?
那就是,別用unity做次時代遊戲!
我是次時代PBR模型師(聽起來感覺很高端一樣)
現在的工作流程就需要用unity
我在Substance painter里2048做個東西
扔unity里,視覺美術效果直接被閹割
難受的一批
沒有先做好命名和位置規範就開始展開製作。這個是多大的禁忌。無論你是個多隨性的人,在命名規範上強迫自己如同禁欲主義者一樣
shader 里不能用 grabpass
謝邀, 哈哈 我就分享一個我看到的神奇的代碼吧
Update(){
While(true)
{}
}
推薦閱讀:
※如何用C#對論壇進行爬蟲?
※C# 里非同步方法該如何理解?
※一個簡單的C#控制台小程序如下,可是不輸出,為什麼?
※如何判斷 string 是否為合法的 C# 變數名?
※怎麼看待 「C#已經沒落」 這種說法?