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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Unity 3D中的内存管理与优化游戏运行性能的经验

發布時間:2023/12/14 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Unity 3D中的内存管理与优化游戏运行性能的经验 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Unity3D在內存占用上一直被人詬病,特別是對于面向移動設備的游戲開發,動輒內存占用飆上一兩百兆,導致內存資源耗盡,從而被系統強退造成極差的體驗。類似這種情況并不少見,但是絕大部分都是可以避免的。雖然理論上Unity的內存管理系統應當為開發者分憂解難,讓大家投身到更有意義的事情中去,但是對于Unity對內存的管理方式,官方文檔中并沒有太多的說明,基本需要依靠自己摸索。最近在接手的項目中存在嚴重的內存問題,在參照文檔和Unity Answer眾多猜測和證實之后,稍微總結了下Unity中的內存的分配和管理的基本方式,在此共享。
雖然Unity標榜自己的內存使用全都是“Managed Memory”,但是事實上你必須正確地使用內存,以保證回收機制正確運行。如果沒有做應當做的事情,那么場景和代碼很有可能造成很多非必要內存的占用,這也是很多Unity開發者抱怨內存占用太大的原因。接下來我會介紹Unity使用內存的種類,以及相應每個種類的優化和使用的技巧。遵循使用原則,可以讓非必要資源盡快得到釋放,從而降低內存占用。


Unity中的內存種類
實際上Unity游戲使用的內存一共有三種:程序代碼、托管堆(Managed Heap)以及本機堆(Native Heap)。
程序代碼包括了所有的Unity引擎,使用的庫,以及你所寫的所有的游戲代碼。在編譯后,得到的運行文件將會被加載到設備中執行,并占用一定內存。這部分內存實際上是沒有辦法去“管理”的,它們將在內存中從一開始到最后一直存在。一個空的Unity默認場景,什么代碼都不放,在iOS設備上占用內存應該在17MB左右,而加上一些自己的代碼很容易就飆到20MB左右。想要減少這部分內存的使用,能做的就是減少使用的庫,稍后再說。
托管堆是被Mono使用的一部分內存。Mono項目一個開源的.net框架的一種實現,對于Unity開發,其實充當了基本類庫的角色。托管堆用來存放類的實例(比如用new生成的列表,實例中的各種聲明的變量等)。“托管”的意思是Mono“應該”自動地改變堆的大小來適應你所需要的內存,并且定時地使用垃圾回收(Garbage Collect)來釋放已經不需要的內存。關鍵在于,有時候你會忘記清除對已經不需要再使用的內存的引用,從而導致Mono認為這塊內存一直有用,而無法回收。
最后,本機堆是Unity引擎進行申請和操作的地方,比如貼圖,音效,關卡數據等。Unity使用了自己的一套內存管理機制來使這塊內存具有和托管堆類似的功能。基本理念是,如果在這個關卡里需要某個資源,那么在需要時就加載,之后在沒有任何引用時進行卸載。聽起來很美好也和托管堆一樣,但是由于Unity有一套自動加載和卸載資源的機制,讓兩者變得差別很大。自動加載資源可以為開發者省不少事兒,但是同時也意味著開發者失去了手動管理所有加載資源的權力,這非常容易導致大量的內存占用(貼圖什么的你懂的),也是Unity給人留下“吃內存”印象的罪魁禍首。


優化程序代碼的內存占用
這部分的優化相對簡單,因為能做的事情并不多:主要就是減少打包時的引用庫,改一改build設置即可。對于一個新項目來說不會有太大問題,但是如果是已經存在的項目,可能改變會導致原來所需要的庫的缺失(雖說一般來說這種可能性不大),因此有可能無法做到最優。

當使用Unity開發時,默認的Mono包含庫可以說大部分用不上,在Player Setting(Edit->Project Setting->Player或者Shift+Ctrl(Command)+B里的Player Setting按鈕)面板里,將最下方的Optimization欄目中“Api Compatibility Level”選為.NET 2.0 Subset,表示你只會使用到部分的.NET 2.0 Subset,不需要Unity將全部.NET的Api包含進去。接下來的“Stripping Level”表示從build的庫中剝離的力度,每一個剝離選項都將從打包好的庫中去掉一部分內容。你需要保證你的代碼沒有用到這部分被剝離的功能,選為“Use micro mscorlib”的話將使用最小的庫(一般來說也沒啥問題,不行的話可以試試之前的兩個)。庫剝離可以極大地降低打包后的程序的尺寸以及程序代碼的內存占用,唯一的缺點是這個功能只支持Pro版的Unity。
這部分優化的力度需要根據代碼所用到的.NET的功能來進行調整,有可能不能使用Subset或者最大的剝離力度。如果超出了限度,很可能會在需要該功能時因為找不到相應的庫而crash掉(iOS的話很可能在Xcode編譯時就報錯了)。比較好地解決方案是仍然用最強的剝離,并輔以較小的第三方的類庫來完成所需功能。一個最常見問題是最大剝離時Sysytem.Xml是不被Subset和micro支持的,如果只是為了xml,完全可以導入一個輕量級的xml庫來解決依賴(Unity官方推薦這個)。

關于每個設定對應支持的庫的詳細列表,可以在這里找到。關于每個剝離級別到底做了什么,Unity的文檔也有說明。實際上,在游戲開發中絕大多數被剝離的功能使用不上的,因此不管如何,庫剝離的優化方法都值得一試。


托管堆優化
Unity有一篇不錯的關于托管堆代碼如何寫比較好的說明,在此基礎上我個人有一些補充。
首先需要明確,托管堆中存儲的是你在你的代碼中申請的內存(不論是用js,C#還是Boo寫的)。一般來說,無非是new或者Instantiate兩種生成object的方法(事實上Instantiate中也是調用了new)。在接收到alloc請求后,托管堆在其上為要新生成的對象實例以及其實例變量分配內存,如果可用空間不足,則向系統申請更多空間。
當你使用完一個實例對象之后,通常來說在腳本中就不會再有對該對象的引用了(這包括將變量設置為null或其他引用,超出了變量的作用域,或者對Unity對象發送Destory())。在每隔一段時間,Mono的垃圾回收機制將檢測內存,將沒有再被引用的內存釋放回收。總的來說,你要做的就是在盡可能早的時間將不需要的引用去除掉,這樣回收機制才能正確地把不需要的內存清理出來。但是需要注意在內存清理時有可能造成游戲的短時間卡頓,這將會很影響游戲體驗,因此如果有大量的內存回收工作要進行的話,需要盡量選擇合適的時間。
如果在你的游戲里,有特別多的類似實例,并需要對它們經常發送Destroy()的話,游戲性能上會相當難看。比如小熊推金幣中的金幣實例,按理說每枚金幣落下臺子后都需要對其Destory(),然后新的金幣進入臺子時又需要Instantiate,這對性能是極大的浪費。一種通常的做法是在不需要時,不摧毀這個GameObject,而只是隱藏它,并將其放入一個重用數組中。之后需要時,再從重用數組中找到可用的實例并顯示。這將極大地改善游戲的性能,相應的代價是消耗部分內存,一般來說這是可以接受的。關于對象重用,可以參考Unity關于內存方面的文檔中Reusable Object Pools部分,或者Prime31有一個是用Linq來建立重用池的視頻教程(Youtube,需要翻墻,上,下)。

如果不是必要,應該在游戲進行的過程中盡量減少對GameObject的Instantiate()和Destroy()調用,因為對計算資源會有很大消耗。在便攜設備上短時間大量生成和摧毀物體的話,很容易造成瞬時卡頓。如果內存沒有問題的話,盡量選擇先將他們收集起來,然后在合適的時候(比如按暫停鍵或者是關卡切換),將它們批量地銷毀并且回收內存。Mono的內存回收會在后臺自動進行,系統會選擇合適的時間進行垃圾回收。在合適的時候,也可以手動地調用System.GC.Collect()來建議系統進行一次垃圾回收。要注意的是這里的調用真的僅僅只是建議,可能系統會在一段時間后在進行回收,也可能完全不理會這條請求,不過在大部分時間里,這個調用還是靠譜的。


本機堆的優化
當你加載完成一個Unity的scene的時候,scene中的所有用到的asset(包括Hierarchy中所有GameObject上以及腳本中賦值了的的材質,貼圖,動畫,聲音等素材),都會被自動加載(這正是Unity的智能之處)。也就是說,當關卡呈現在用戶面前的時候,所有Unity編輯器能認識的本關卡的資源都已經被預先加入內存了,這樣在本關卡中,用戶將有良好的體驗,不論是更換貼圖,聲音,還是播放動畫時,都不會有額外的加載,這樣的代價是內存占用將變多。Unity最初的設計目的還是面向臺式機,幾乎無限的內存和虛擬內存使得這樣的占用似乎不是問題,但是這樣的內存策略在之后移動平臺的興起和大量移動設備游戲的制作中出現了弊端,因為移動設備能使用的資源始終非常有限。因此在面向移動設備游戲的制作時,盡量減少在Hierarchy對資源的直接引用,而是使用Resource.Load的方法,在需要的時候從硬盤中讀取資源,在使用后用Resource.UnloadAsset()和Resources.UnloadUnusedAssets()盡快將其卸載掉。總之,這里是一個處理時間和占用內存空間的trade off,如何達到最好的效果沒有標準答案,需要自己權衡。
在關卡結束的時候,這個關卡中所使用的所有資源將會被卸載掉(除非被標記了DontDestroyOnLoad)的資源。注意不僅是DontDestroyOnLoad的資源本身,其相關的所有資源在關卡切換時都不會被卸載。DontDestroyOnLoad一般被用來在關卡之間保存一些玩家的狀態,比如分數,級別等偏向文本的信息。如果DontDestroyOnLoad了一個包含很多資源(比如大量貼圖或者聲音等大內存占用的東西)的話,這部分資源在場景切換時無法卸載,將一直占用內存,這種情況應該盡量避免。
另外一種需要注意的情況是腳本中對資源的引用。大部分腳本將在場景轉換時隨之失效并被回收,但是,在場景之間被保持的腳本不在此列(通常情況是被附著在DontDestroyOnLoad的GameObject上了)。而這些腳本很可能含有對其他物體的Component或者資源的引用,這樣相關的資源就都得不到釋放,這絕對是不想要的情況。另外,static的單例(singleton)在場景切換時也不會被摧毀,同樣地,如果這種單例含有大量的對資源的引用,也會成為大問題。因此,盡量減少代碼的耦合和對其他腳本的依賴是十分有必要的。如果確實無法避免這種情況,那應當手動地對這些不再使用的引用對象調用Destroy()或者將其設置為null。這樣在垃圾回收的時候,這些內存將被認為已經無用而被回收。

需要注意的是,Unity在一個場景開始時,根據場景構成和引用關系所自動讀取的資源,只有在讀取一個新的場景或者reset當前場景時,才會得到清理。因此這部分內存占用是不可避免的。在小內存環境中,這部分初始內存的占用十分重要,因為它決定了你的關卡是否能夠被正常加載。因此在計算資源充足或是關卡開始之后還有機會進行加載時,盡量減少Hierarchy中的引用,變為手動用Resource.Load,將大大減少內存占用。在Resource.UnloadAsset()和Resources.UnloadUnusedAssets()時,只有那些真正沒有任何引用指向的資源會被回收,因此請確保在資源不再使用時,將所有對該資源的引用設置為null或者Destroy。同樣需要注意,這兩個Unload方法僅僅對Resource.Load拿到的資源有效,而不能回收任何場景開始時自動加載的資源。與此類似的還有AssetBundle的Load和Unload方法,靈活使用這些手動自愿加載和卸載的方法,是優化Unity內存占用的不二法則~




http://gamerboom.com/archives/76214

總結使用Unity 3D優化游戲運行性能的經驗

作者:Amir Fasshihi

流暢的游戲玩法來自流暢的幀率,而我們即將推出的動作平臺游戲《Shadow Blade》已經將在標準iPhone和iPad設備上實現每秒60幀視為一個重要目標。

以下是我們在緊湊的優化過程中提升游戲運行性能,并實現目標幀率時需要考慮的事項。

當基本游戲功能到位時,就要確保游戲運行表現能夠達標。我們衡量游戲運行表現的一個基本工具是Unity內置分析器以及Xcode分析工具。使用Unity分析器來分析設備上的運行代碼真是一項寶貴的功能。

我們總結了這種為將目標設備的幀率控制在60fps而進行衡量、調整、再衡量過程的中相關經驗。

shadow blade(from deadmage.com)

一、遇到麻煩時要調用“垃圾回收器”(Garbage Collector,無用單元收集程序,以下簡稱GC)

由于具有C/C++游戲編程背景,我們并不習慣無用單元收集程序的特定行為。確保自動清理你不用的內存,這種做法在剛開始時很好,但很快你就公發現自己的分析器經常顯示CPU負荷過大,原因是垃圾回收器正在收集垃圾內存。這對移動設備來說尤其是個大問題。要跟進內存分配,并盡量避免它們成為優先數,以下是我們應該采取的主要操作:

1.移除代碼中的任何字符串連接,因為這會給GC留下大量垃圾。

2.用簡單的“for”循環代替“foreach”循環。由于某些原因,每個“foreach”循環的每次迭代會生成24字節的垃圾內存。一個簡單的循環迭代10次就可以留下240字節的垃圾內存。

3.更改我們檢查游戲對象標簽的方法。用“if (go.CompareTag (“Enemy”)”來代替“if (go.tag == “Enemy”)” 。在一個內部循環調用對象分配的標簽屬性以及拷貝額外內存,這是一個非常糟糕的做法。

4.對象庫很棒,我們為所有動態游戲對象制作和使用庫,這樣在游戲運行時間內不會動態分配任何東西,不需要的時候所有東西反向循環到庫中。

5.不使用LINQ命令,因為它們一般會分配中間緩器,而這很容易生成垃圾內存。

二、謹慎處理高級腳本和本地引擎C++代碼之間的通信開銷。

所有使用Unity3D編寫的游戲玩法代碼都是腳本代碼,在我們的項目中是使用Mono執行時間處理的C#代碼。任何與引擎數據的通信需求都要有一個進入高級腳本語言的本地引擎代碼的調用。這當然會產生它自己的開銷,而盡量減少游戲代碼中的這些調用則要排在第二位。

1.在這一情景中四處移動對象要求來自腳本代碼的調用進入引擎代碼,這樣我們就會在游戲玩法代碼的一個幀中緩存某一對象的轉換需求,并一次僅向引擎發送一個請求,以便減少調用開銷。這種模式也適用于其他相似的地方,而不僅局限于移動和旋轉對象。

2.將引用本地緩存到元件中會減少每次在一個游戲對象中使用 “GetComponent” 獲取一個元件引用的需求,這是調用本地引擎代碼的另一個例子。

三、物理效果

1.將物理模擬時間步設置到最小化狀態。在我們的項目中就不可以將讓它低于16毫秒。

2.減少角色控制器移動命令的調用。移動角色控制器會同步發生,每次調用都會耗損極大的性能。我們的做法是緩存每幀的移動請求,并且僅運用一次。

3.修改代碼以免依賴“ControllerColliderHit” 回調函數。這證明這些回調函數處理得并不十分迅速。

4.面對性能更弱的設備,要用skinned mesh代替physics cloth。cloth參數在運行表現中發揮重要作用,如果你肯花些時間找到美學與運行表現之間的平衡點,就可以獲得理想的結果。

5.在物理模擬過程中不要使用ragdolls,只有在必要時才讓它生效。

6.要謹慎評估觸發器的“onInside”回調函數,在我們的項目中,我們盡量在不依賴它們的情況下模擬邏輯。

7.使用層次而不是標簽。我們可以輕松為對象分配層次和標簽,并查詢特定對象,但是涉及碰撞邏輯時,層次至少在運行表現上會更有明顯優勢。更快的物理計算和更少的無用分配內存是使用層次的基本原因。

8.千萬不要使用Mesh對撞機。

9.最小化碰撞檢測請求(例如ray casts和sphere checks),盡量從每次檢查中獲得更多信息。

四、讓AI代碼更迅速

我們使用AI敵人來阻攔忍者英雄,并同其過招。以下是與AI性能問題有關的一些建議:

1.AI邏輯(例如能見度檢查等)會生成大量物理查詢。可以讓AI更新循環設置低于圖像更新循環,以減少CPU負荷。

五、最佳性能表現根本就不是來自代碼!

沒有發生什么情況的時候,就說明性能良好。這是我們關閉一切不必要之物的基本原則。我們的項目是一個側邊橫向卷軸動作游戲,所以如果不具有可視性時,就可以關閉許多動態關卡物體。

1.使用細節層次的定制關卡將遠處的敵人AI關閉。

2.移動平臺和障礙,當它們遠去時其物理碰撞機也會關閉。

3.Unity內置的“動畫挑選”系統可以用來關閉未被渲染對象的動畫。

4.所有關卡內的粒子系統也可以使用同樣的禁用機制。

六、回調函數!那么空白的回調函數呢?

要盡量減少Unity回調函數。即使敵人回調函數存在性能損失。沒有必要將空白的回調函數留在代碼庫中(有時候介于大量代碼重寫和重構之間)。

七、讓美術人員來救場

在程序員抓耳撓腮,絞盡腦汁去想該如何讓每秒運行更多幀時,美術人員總能神奇地派上大用場。

1.共享游戲對象材料,令其在Unity中處于靜止狀態,可以讓它們綁定在一起,由此產生的簡化繪圖調用是呈現良好移動運行性能的重要元素。

2.紋理地圖集對UI元素來說尤其有用。

3.方形紋理以及兩者功率的合理壓縮是必不可少的步驟。

4.我們的美術人員移除了所有遠處背景的網格,并將其轉化為簡單的2D位面。

5.光照圖非常有價值。

6.我們的美術人員在一些關口移除了額外頂點。

7.使用合理的紋理mip標準是一個好主意(游戲邦注:要讓不同分辨率的設備呈現良好的幀率時尤其如此)。

8.結合網格是美術人員可以發揮作用的另一個操作。

9.我們的動畫師盡力讓不同角色共享動畫。

10.要找到美學/性能之間的平衡,就免不了許多粒子效果的迭代。減少發射器數量并盡量減少透明度需求也是一大挑戰。

八、要減少內存使用

使用大內存當然會對性能產生負面影響,但在我們的項目中,我們的iPod由于超過內存上限而遭遇了多次崩潰事件。我們的游戲中最耗內存的是紋理。

1.不同設備要使用不同的紋理大小,尤其是UI和大型背景中的紋理。《Shadow Blade》使用的是通用型模板,但如果在啟動時檢測到設備大小和分辨率,就會載入不同資產。

2.我們要確保未使用的資產不會載入內存。我們必須遲一點在項目中找到僅被一個預制件實例引用,并且從未完全載入內存中實例化的資產。

3.去除網格中的額外多邊形也能實現這一點。

4.我們應該重建一些資產的生周期管理。例如,調整主菜單資產的加載/卸載時間,或者關卡資產、游戲音樂的有效期限。

5.每個關卡都要有根據其動態對象需求而量身定制的特定對象庫,并根據最小內存需求來優化。對象庫可以靈活一點,在開發過程中包含大量對象,但知道游戲對象需求后就要具體一點。

6.保持聲音文件在內存的壓縮狀態也是必要之舉。

加強游戲運行性能是一個漫長而具有挑戰性的過程,游戲開發社區所分享的大量知識,以及Unity提供的出色分析工具為《Shadow Blade》實現目標運行性能提供了極大幫助。(本文為游戲邦/gamerboom.com編譯,拒絕任何不保留版權的轉載,如需轉載請聯系:游戲邦

“0 – 60 fps in 14 days!” What we learned trying to optimize our game using Unity3D.

by Amir Fassihi

The following blog post, unless otherwise noted, was written by a member of Gamasutra’s community.

The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.

A smooth gameplay is built upon the foundations of a smooth frame rate and hitting the 60 frames per second target on the standard iPhone and iPad devices was a significant goal during the development of our upcoming action platformer game, Shadow Blade. (http://shadowblade.deadmage.com)

The following is a summary from the things we had to consider and change in the game in order to increase the performance and reach the targeted frame rate during the intense optimization sessions.

Once the basic game functionalities were in place, it was time to make sure the game performance would meet its target. Our main tool for measuring the performance was the built-in Unity profiler and the Xcode profiling tools. Being able to profile the running code on the device using the Unity profiler proved to be an invaluable feature.

So here goes our summary and what we learned about the results of this intense measuring, tweaking and re-measuring journey which paid out well at the end and resulted in a fixed 60fps for our target devices.

1 – Head to head with a ferocious monster called the Garbage Collector.

Coming from a C/C++ game programming background, we were not used to the specific behaviors of the garbage collector. Making sure your unused memory is cleaned up automatically for you is nice at first but soon the reality kicks in and you witness regular spikes in your profiler showing the CPU load caused by the garbage collector doing what it is supposed to do, collecting the garbage memory. This proved to be a huge issue specifically for the mobile devices. Chasing down memory allocations and trying to eliminate them became priority number one and here are some of the main actions we took:

Remove any string concatenation in code since this leaves a lot of garbage for the GC to collect.

Replace the “foreach” loops with simple “for” loops. For some reason, every iteration of every “foreach” loop generated 24 Bytes of garbage memory. A simple loop iterating 10 times left 240 Bytes of memory ready to be collected which was just unacceptable

Replace the way we checked for game object tags. Instead of “if (go.tag == “Enemy”)” we used “if (go.CompareTag (“Enemy”)”. Calling the tag property on an object allocates and copies additional memory and this is really bad if such a check resides in an inner loop.

Object pools are great, we made and used pools for all dynamic game objects so that nothing is ever allocated dynamically during the game runtime in the middle of the levels and everything is recycled back to the pool when not needed.

Not using LINQ commands since they tended to allocate intermediate buffers, food for the GC.

2 – Careful with the communication overhead between high level scripts and native engine C++ code.

All gameplay code written for a game using Unity3D is script code which in our case was C# that was handled using the Mono runtime. Any requirements to communicate with the engine data would require a call into the native engine code from the high level scripting language. This of course has its own overhead and trying to reduce such calls in game code was the second priority.

Moving objects around in the scene requires calls from the script code to the engine code and we ended up caching the transformation requirements for an object during a frame in the gameplay code and sending the request to the engine only once to reduce the call overhead. This pattern was used in other similar places other than the needs to move and rotate an object.

Caching references to components locally would eliminate the need to fetch a component reference using the “GetComponent” method on a game object every time which is another example for a call into the native engine code.

3 – Physics, Physics and more Physics.

Setting the physics simulation timestep to the minimum possible. For our case we could not set it lower than 16 milliseconds.

Reducing calls to character controller move commands. Moving the character controller happens synchronously and every call can have a significant performance cost. What we did was to cache the movement requests per frame and apply them only once.

Modifying code to not rely on the “ControllerColliderHit” callbacks. It proved that these callbacks are not handled very quickly.

Replacing the physics cloth with a skinned mesh for the weaker devices. The cloth parameters can play important roles in performance also and it pays off to spend some time to find the appropriate balance between aesthetics and performance.

Ragdolls were disabled so that they were not part of the physics simulation loop and only enabled when necessary.

“OnInside” callbacks of the triggers need to be assessed carefully and in our case we tried to model the logic without relying on them if possible.

Layers instead of tags! Layers and tags can be assigned to objects easily and used for querying specific objects, however, layers have a definite advantage at least performance wise when it comes to working with collision logic. Quicker physics calculations and less unwanted newly allocated memory are the basic reasons.

Mesh colliders are definitely a no-no.

Minimize collision detection requests like ray casts and sphere checks in general and try to get as much information from each check.

4 – Let’s make the AI code faster!

We use artificial intelligence for the enemies that try to block our main ninja hero and fight with him. The following topics needed to be covered regarding AI performance issues:

A lot of physical queries are generated from AI logic like visibility checks. The AI update loop could be set to something much lower than the graphics update loop to reduce CPU load.

5 – Best performance is achieved from no code at ALL!

When nothing happens, performance is good. This was the base philosophy for us to try and turn anything not necessary at the moment off. Our game is a side scroller action game and so a lot of the dynamic level objects can be turned off when they are not visible in the scene.

Enemy AI was turned off when far away using a custom level of detail scheme.

Moving platforms and hazards and their physics colliders were turned off when far away.

Built in Unity “animation culling” system was used to turn off animations on objects not being rendered.

Same disabling mechanism used for all in level particle systems.

6 – Callback! How about empty callbacks?

The Unity callbacks needed to be reduced as much as possible. Even the empty callbacks had performance penalties. There is no reason for having empty callbacks but they just get left in the code base sometimes in between a lot of code rewrite and refactoring.

7 – The mighty Artists to the rescue.

Artists can always magically help out the hair-pulling programmer trying to go for a few more frames per second.

Sharing materials for game objects and making them static in Unity causes them to be batched together and the resulting reduced draw calls are critical for good mobile performance.
Texture atlases helped a lot especially for the UI elements.

Square textures and power of two with proper compression was a must.

Being a side-scroller enabled our artists to remove all far background meshes and convert them to simple 2D planes instead.

Light maps were highly valuable.

Our artists removed extra vertices during a few passes.

Proper texture mip levels were a good decision especially for having a good frame rate on devices with different resolutions.

Combining meshes was another performance friendly action by the artists.

Our animator tried to share animations between different characters if it was possible.

A lot of iterations on the particles were necessary to find the aesthetic/performance balance. Reducing number of emitters and trying to reduce transparency requirements were among the major challenges.

8 – The memory usage needs to be reduced, now!

Using a lot of memory of course has negative performance related effects but in our case we experienced a lot of crashes on iPods due to exceeding memory limits which was a much more critical problem. The biggest memory consumers in our game were the textures.

Different texture sizes were used for different devices, especially textures used in UI and large backgrounds. Shadow Blade uses a universal build but different assets get loaded when the device size and resolution is detected upon startup.

We needed to make sure un-used assets were not loaded in memory. We had to find out a little late in the project that any asset that was only referenced by an instance of a prefab and never instantiated was fully loaded in memory.

Stripping out extra polygons from meshes helped.

We needed to re-architect the lifecycle management of some assets a few times. For example tweaking the load/unload time for the main menu assets or end of level assets or game music.

Each level needed to have its specific object pool tailored to its dynamic object requirements and optimized for the least memory needs. Object pools can be flexible and contain a lot of objects during development, however, they need to be specific once the game object requirements are known.

Keeping the sound files compressed in memory was necessary.

Game performance enhancement is a long and challenging journey and we had a fun time experiencing a small part of this voyage. The vast amount of knowledge shared by the game development community and very good profiling tools provided by Unity were what made us reach our performance targets for Shadow Blade.(source:gamasutra)


總結

以上是生活随笔為你收集整理的Unity 3D中的内存管理与优化游戏运行性能的经验的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。