U3D開發性能優化筆記(待增長版本.x)

http://blog.csdn.net/kaitiren/article/details/45071997
javascript

此總結由本身經驗及網上收集整理優化內容 包括:
.代碼方面;
.函數使用方面;
.ui注意方面;
.數學運算方面;
.內存方面;
.垃圾回收方面 等等...
本着相互交流 共同進步的原則

html


U3D開發性能優化筆記:
java

.NGUI: Atlas優化;ios

.poolmanager使用;程序員

.控制同屏drawcall次數;
算法

.SHADER優化頂點和運算;數組

.合批與動態剔除;緩存

.邏輯部分優化;(如看到不到的物件不要作公告板位置運算,不要播放animation)
性能優化

.物理幀UPDATE下降;服務器

.關閉垂直同步,下降圖片採樣,聲音預加載 方案 等等 。。;

.模型骨骼不要超過32根;

.貼圖不要太大,建議512 *512 如下;

.少用 CUTOFF和 aplha混合;

.3D遊戲效率基本原則就是費內存省CPU 和GPU;

.NGUI ANIMATION ANIMTOR 碰撞檢測 特效 渲染 這些都是性能消耗的大頭;

.Occlusion Culling使用;

​.只用一個mesh renderer, 少用多materials, 最多3個;

.每一個角色儘可能使用一個 Skinned Mesh Renderer;

.面數問題每一個模型不要超過300~1500;

.模型儘可能不要分開若是多個模塊會屢次調用dc;

.通常角色應該沒有 IK 結點.

.儘可能不用像素光(pixels Lights).

.不用 軟陰影(soft shadow), 或者不用陰影(No Shadows)

.燈光能不用就不用陰影能夠用面來代替.

.少用實時燈光儘可能使用lightmap(強大的Unity內置了一個強大的光照圖烘焙工具Beast,這個東東是Autodesk公司的產品);

.transformOnGUI (運算上的優化遠比不上 繪製效率上的優化,少個dc可能就比得上這些了)

.少用transform, 多用 myCachedTransform.

.動態物體的相關優化 
優化主要分爲兩個方向,一個是資源相關優化和引擎相關的優化。資源相關的優化,大概分爲動態物體、靜態物體、紋理數據、音頻數據、程序包數據。對於動態物體好比NPC、怪物等,須要對面片數量的控制,大概在3002000面。1500面就能夠體現人物細節,但若是是人物比較多,可能要下降面數,不要低於300。另外,一方面是控制Skinned Mesh Renderer的數量;另外一方面是控制材質數量在13種。人物最好用小於30根骨骼,若是你用的骨骼越多,耗費的CPU就更多,因此在移動平臺上儘可能少於30根。如今咱們看其餘動態物體,利用Dynamic Batching進行合批。這個下雨特效並非系統作的,是包含不少雨點的網格進行重複拷貝,而後錯亂移動實現的。每個雨點並非一個粒子,這樣能減小不少CPU的消耗,每個總體網格都會有一個頂點的控制,經過控制頂點數量,對系統實現雨點效果來講,這是一個至關省時省力的方法。


.靜態物體的相關優化 
下面咱們來看靜態物體,靜態物體也是要控制面數和頂點數,頂點數少於500個。static是不會進行移動縮放、旋轉的,把它標記爲static,固然他們的材質是同樣的。不要添加animation組建,對於靜態物體來講,這個組件毫無心義,能把他丟掉就丟掉,由於這對CPU的消耗是很是客觀的。 


.音頻程序的優化 
關於音頻時間的播放,好比背景音樂,建議使用MP3壓縮格式,好比音效,要求數據儘快加載,這些數據比較小就能夠,使用WAVAIF未壓縮音頻格式。關於程序包的優化,不少開發者會埋怨說打出來的包太大,如今介紹減小程序包的方法,首先使用壓縮格式的紋理,以顯卡的壓縮格式保存,使用壓縮網格和動畫數據。網格壓縮是先採用量化處理,固然這個壓縮是保證在包裏面的數據小,但運行時佔用的內存沒有減小,由於咱們並無把頂點刪除,可是對動畫數據來講,動畫數據通過壓縮處理後下降,能夠減小遊戲列層。


.代碼儘可能不要使用System.xml,咱們建議使用Mono.xml。啓用Stripping來減小庫的大小,使用剝離方式。


.引擎相關優化和物理相關優化 
下來是引擎相關的優化,例如光照設置、相繼設置、粒子特效、物理特效等。那拿光照設置來講,光源所有的實時光照這是很恐怖的,每一次實施光照表明着每一次使用消耗,怎麼優化?有人使用LightMapping來製做靜態場景,他的好處是不須要用多張實施光照,而給場景很好的光照效果。有人使用Light Probes代替實時光照,好處是徹底不用怎麼消耗,並且運做性能也很是高。在有些時候使用Light Probes代替光照,他能跟場景很好的融合,在一個角落裏,這個任務會被陰影打得暗一些。若是說場景中確實須要一些實時光源,那麼確定是須要作過優化設置的實時光源,控制important的光源個數。若是說光源有些地方產生了交叉光,這個時候你能夠經過設置Pxel Light,控制每個光源都只接受一個動態光照,數目大概是12個。對於關閉光源的實時陰影,並非全部平臺都支持實時陰影,消耗也很是大,不建議你們使用。關於相機方面的設置,平面越近,渲染越少。咱們更建議使用分層,好比遠處的建築,對於建築物的裁減平面遠一些,若是是花草,就可使用平面就近一些。如今看一下粒子特效,粒子也是遊戲中須要優化的東西,建議屏幕中最大的粒子數不要超過200,同時每一個發射器發射的最大粒子數不要超過50。粒子尺寸也要儘量小,最終在屏幕有多少像素。他們中間的像素可能會被渲染不少次,至少四五次,這時發現粒子系統僅像素就填充了更多屏幕,這時候對遊戲來講很是耗費,對遊戲的其餘功能性能也有所影響。另一方面,對於很是小的粒子,儘可能不要開啓粒子碰撞功能。


.物理相關優化,物理儘量使用Sphere CoilliderBox Coillider等,儘可能避免使用Meh Colllider等。渲染設置,避免使用Alpha Test,由於很是耗時,性價比很低。關於Sttic Batching,對靜態物體進行Batch,對幾何數據的大小沒有限制。物體被合併後會帶來一些內存消耗,好比說有控制網格的物體,用Batch會合併成大物體。Dynamic Batching目前僅支持小於900頂點的網格物體。如何理解900呢,其實就至關於900個頂點數據大小的物體,若是說使用PositionNormalUV三種屬性,那麼你只能Batch300個頂點。總體縮放的物體不能被Batch,除非他們的縮放值相同。以前有一個客戶作特效,使用Batch機制把面片合併,最終讓全部面片共享一個紋理,這時候發現這些面片沒有被Batch出來,致使運行遊戲時大概放三個技能就10多個招套。對於非總體用戶體,他們的Batch是須要很好利用到。


.紋理合並優化 
如今來看紋理合並,紋理合並就是爲了特到Batch數量,合併物體首先須要合併工具,還要修改使用紋理的網格的UV,使他們使用紋理。合併紋理主要是參照Batch,提升渲染性能。但在合併材質後須要注意的是腳本訪問Renderer被拷貝。/*安擋剔除,建議使用PVS技術。建議你們使用自定義shader,例如高光效果,高光效果可能不須要作一些入射線的檢測,只是簡單把他的值放大也能夠模擬高光效果,從而減小一些消耗。 
另一個是用profiler,經過他給的數據進行鍼對性的優化。以上是跟你們介紹優化的內容,如何做出良好優化,必定要作好良好的規劃,到後期就不會很麻煩,若是規劃沒有作好有可能會給程序帶來很大壓力,結果可能很不樂觀。*/最後,要不斷實驗不斷總結才能達到本身滿意的效果。


.下降Drawcal的話,有以下兩點小建議 
1)不要用Unity自帶UI或者iGUI, NUI 或者EZ GUI 
(2)建立好的GameObject不用了就最好及時 刪除 設置activefalse/移出屏幕 。 這幾種方法均可以去掉該物體致使增長的Drawcall.



最近一段時間一直在作Unity IOS設備上的資源優化,結合Unity的官方文檔以及本身遇到的實際問題,我把本身認爲一些重要的信息羅列在下面,並儘量對將其量化,以方便更多須要作優化的朋友。 
1、 角色 
每一個角色儘可能使用一個 Skinned Mesh Renderer 
這是由於當角色僅有一個 Skinned Mesh Renderer 時, Unity 會 使用可見性裁剪和包圍體更新的方法來優化角色的運動,而這種優化只有在角色僅含有一個 Skinned Mesh Renderer 時纔會啓動。 
角色 Material 數量 
2-3  
骨骼數量 
小於 30  
面片數量 
300-1500 
通常角色應該沒有 IK 結點 
這是由於角色的動做大多數都是事先設定好的,並不須要通過 IK 操做來進行實時計算( Rogdoll 除外),因此在模型導入時,不要將 IK 結點一塊兒導入。 
2、 靜態實體 
不要附加 Animation Component 
在靜態實體上附加 Animation 部件雖然對結果沒有影響,但卻會增長必定的 CPU 開銷來調用這一組件,因此儘可能去掉該組件。 
網格頂點數 
小於 500 
UV 值範圍儘可能不要超過( 0, 1 )區間 
儘可能保證 UV 值不越界,這對於未來的紋理拼合優化頗有幫助。 
3、 地形 
地形的分辨率大小 
長寬均儘可能小於 257 。這是由於地形太大,會形成大量頂點數據,給你的內存帶寬形成必定的影響,在目前的 ios 設備中,內存帶寬是很是有限的,須要儘可能節省。同時,若是用 Unity 自帶的地形,必定也要使用 Occlusion Culling ,由於 Unity 的刷地形工具雖然方便,但倒是 framekiller ,刷過以後,你會發現 drawcall 增長的很是多。 
混合紋理數量 
不要超過 。地形的混合操做是很耗時的,應該儘可能避免。能合併的紋理儘可能合併。 
4、 紋理 
紋理格式 
建議 png 或 tga 。不用轉成 ios 硬件支持的 PVRTC 格式,由於 Unity 在發佈時會幫你自動轉的。 
紋理尺寸 
長寬小於 1024 。同時應該儘量地小,夠用就好,以保證紋理對內存帶寬的影響達到最小。 
支持 Mipmap 
建議生成 Mipmap 。雖然這種作法會增長一些應用程序的大小,但在遊戲運行時,系統會根據需求應用 Mipmap 來渲染,從而減小內存帶寬。 
檢查 Alpha  
若是紋理的 alpha 通道均爲 ,則用 RGB 的 24 位紋理來代替 RGBA 的 32 位紋理。(聽說 Unity 內部會進行自動檢測) 
5、 光源 
光源「 Important 」個數 
建議 個,通常爲方向光。「 Important 」個數應該越小越少。個數越多, drawcall 越多。 
Pixel Light 數目 
1-2 個。 
6、 粒子特效 
屏幕上的最大粒子數 
建議小於 200 個粒子。 
每一個粒子發射器發射的最大粒子數 
建議不超過 50 個。 
粒子大小 
若是能夠的話,粒子的 size 應該儘量地小。由於 Unity 的粒子系統的 shader 不管是 alpha test 仍是 alpha blending 都是一筆不小的開銷。同時,對於很是小的粒子,建議粒子紋理去掉 alpha 通道。 
儘可能不要開啓粒子的碰撞功能。 
很是耗時。 
7、 音頻 
遊戲中播放時間較長的音樂(如背景音樂) 
使用 .ogg 或 .mp3 的壓縮格式。 
較短音樂(如槍聲) 
使用 .wav 和 .aif 的未壓縮音頻格式。 
8、 相機 
裁剪平面 
將遠平面設置成合適的距離。遠平面過大會將一些沒必要要的物體加入渲染,下降效率。 
根據不一樣的物體設置不一樣的遠裁剪平面 
Unity 提供了能夠根據不一樣的 layer 來設置不一樣的 view distance ,因此咱們能夠實現將物體進行分層,大物體層設置的可視距離大些,而小物體層能夠設置地小些,另外,一些開銷比較大的實體(如粒子系統)能夠設置得更小些等等。 
9、 碰撞 
儘可能不用 MeshCollider 
若是能夠的話,儘可能不用 MeshCollider ,以節省沒必要要的開銷。若是不能避免的話,儘可能用減小 Mesh 的面片數,或用較少面片的代理體來代替。 
10、 其餘 
Drawcall 
儘量地減小 Drawcall 的數量。 
iOS 設備上建議不超過 100  
減小的方法主要有以下幾種: Frustum Culling , Occlusion Culling , Texture Packing  
Frustum Culling 是 Unity 內建的,咱們須要作的就是尋求一個合適的遠裁剪平面; Occlusion Culling ,遮擋剔除, Unity 內嵌了 Umbra ,一個很是好 OC 庫。但 Occlusion Culling 也並非放之四海而皆準的,有時候進行 OC 反而比不進行還要慢,建議在OC 以前先肯定本身的場景是否適合利用 OC 來優化; 
Texture Packing ,或者叫 Texture Atlasing ,是將同種 shader 的紋理進行拼合,根據 Unity 的 static batching 的特性來減小draw call 。建議使用,但也有弊端,那就是必定要將場景中距離相近的實體紋理進行拼合,不然,拼合後極可能會增長每幀渲染所需的紋理大小,加大內存帶寬的負擔。 
這也就是爲何會出現「 DrawCall 降了,渲染速度也變慢了」的緣由。 

非運動物體儘可能打上 Static 標籤 
Unity 在運行時會對 static 物體進行自動優化處理,因此應該儘量將非運行實體勾上 static 標籤。 

場景中儘量地使用 prefab 
儘量地使用 prefab 的實例化物體,以下降內存帶寬的負擔。檢查實體的 PrefabType ,儘可能將其變成 PrefabInstance ,而不是ModelPrefabInstance 


來自其餘開發者的筆記:

總的來講,Unity沒有啥天坑。只要肯研究,後期都能改進,也都不會影響到上線。


小坑太多,說不完。Unity上手容易坑太多,基本事件機制,生存週期,場景和資源管理,mono虛擬機的gc機制都是坑。


要說的話,真正影響到架構的是(排序)

1. 是否要用lua

2. (對於需操做的遊戲)客戶端遊戲如何作戰鬥驗證



公司的話,推薦:

參加Unity年會

購買Unity的官方支持問答平臺,人有源代碼,還能找總部



下面列舉小坑吧。不建議都繞開,畢竟沒有那麼多時間作前期調研的。

對應版本Unity4.x


1. 客戶端程序層面


總的來講C#超級給力的,不過別玩脫了


1) mono虛擬機gc


Unity的mono虛擬機使用不分代的gc算法,臨時對象積攢起來,致使重量級GC遊戲頻繁卡頓。


Unity官方:認真review每幀20B以上,以及一次2K以上的GC Alloc的行爲。傳聞:Unity5會改進。


評價:請像C++同樣精確瞭解各類行爲的gc,foreach 都不要隨便用。嚴重,但遊戲是能夠卡巴卡巴上線的。後期一位核心開發人員修2~3周。


2) 蘋果aot編譯問題:模板問題


mono在蘋果上採用aot將C#編譯爲靜態代碼。首先,依賴於動態代碼生成的複雜模板容易運行時崩潰;其次,mono會將客戶端生成一個庫。模板代碼實例化容易膨脹致使該庫超過40M而沒法連接。


實戰:碰到了改寫法吧。不過我本人是靜態類型檢查派的。


3) 少用coroutine


yield只支持try--finally,與異常體系兼容性極差;難以提供返回值;異步自己是非線性的,很難保證邏輯完備。


實戰:複雜異步邏輯用狀態機。不致命,多修bug也能抗過。


4) 自行處理配置數據序列化


嚴重影響配置讀取速度。C#自帶的xml序列化很慢,自帶的二進制序列化也不夠快。


實戰:打包配置考慮protobuf或者代碼生成器。中後期一週左右。


5) 反射


手機上jit狀況下,第一次反射一個類很慢。亂用足夠影響啓動速度。


6) 本地化


若是公司習慣於作海外市場,一開始就能夠考慮全套本地化方案。後期改須要一我的1~2個月工做量。


2 資源優化


Unity資源優化,一個靠譜的TA很重要。


1) 資源內存佔用


512內存機器能用的資源大概只有50~60M。需透徹研究貼圖。考慮換皮怪資源複用、UI的圖集合理化。沒有UI優化經驗的話,強烈建議一個核心開發死跟,像摳代碼優化同樣優化圖集總結經驗。這個後期很難收場。每一個粒子發射器佔用10K內存;有些項目在動畫上會有內存問題。


2) 關注資源包大小


最大的是貼圖和骨骼動畫。貼圖關注內存便可。骨骼動畫能夠佔到模型的一半大小,重作的話有各類優化方案。但超標後期也很難收場。


3) 依賴打包


Unity4.x和Unity5徹底不一樣。其中Unity4.x機制龐大繁雜容易錯,要有心理準備。扯一些要點:


* 必定要搞清其內存佔用和生存週期。要實測,特別容易跌眼鏡。

* 每一個API都有坑。我我的目前推薦壓縮模式、LoadFromCache,此時不能拆太碎。戰鬥前預加載。

* shader加載慢,應當放入依賴包

* bundle不能重名


4) 場景、drawcall、camera


場景面多了考慮動態batching。不一樣材質透明物體(例如粒子)穿插可能引發drawcall暴增。camera是重型對象,越少越好。


5) svn


資源選Text模式、顯式保存.meta,便於版本管理。資源分人或者鎖了改,規避衝突。


3 Unity


和Flash同樣容易學的3D編輯器


1 ) 事件機制


Unity事件機制很很差用。單個對象,Awake,Start,Enable調用時機至關複雜。Unity徹底不保證多個對象的事件執行順序,致使不少人繞開Start。不恰當的使用事件,很容易致使父子對象不在同一幀出現,畫面不乾淨。


Destroy操做是延遲的,對象會活到幀的結尾,而後一定銷燬。庫級設計時,必須考慮到這一點(例如對象池/動畫庫)。


2) 資源管理


只說Unity4.x。合理作法是依賴很卡的UnloadUnusedAssets、LoadScene清理無引用資源(另注意前者是異步的),或者Bundle.Unload(true),這些方案各有限制。試圖更細粒度手工清理的困難在於,並不存在系統性文檔解釋Unity資源的分類和生存週期,且Destroy操做很保守。例如,銷燬mesh時,並不會銷燬material、texture,更不會清理腳本資源。


此外,特定的普通操做會形成資源克隆。例如訪問Renderer.meterial,Animation.AddClip。


4 NGUI


久經驗證的掉鏈子王。新項目也能夠嚐嚐uGUI


1) panel重繪


widget改變後,所在panel須要生成多邊形,很慢,坑新人沒商量,注意合理分panel。panel中多邊形過多會爆(貌似是65535個頂點?)。

uGUI原理相同,就是c代碼比C#快很多。


2) panel渲染順序


搞清楚ui上放置3D物體咋辦,ui如何和特效混合排序。


3) 策劃/美術ui規範


潛規則不少。Anchor、動畫不可做用於同一個物體。widget必須是panel的子節點,否則他就會本身造panel,常常搞出亂子。再加上上面的panel規則等,要策劃美術折騰ui可費神了。


項目組自制UI編輯器天然是極好的,不過不必定必要。


4) 建立速度慢


因爲序列化字段多,NGUI對象建立可致使卡頓。多狀態對象不要靠隱藏-顯示,而要動態建立。尤爲是狀態中包含粒子發生器/Animation,這倆還有內存問題(10K一個)。


5) 與Unity事件機制強耦合


與Unity的事件機制強耦合,不完備,容易有bug。例如,panel繪製依賴於LateUpdate。coroutine中同時關閉舊界面,建立新界面,此時當前幀 LateUpdate 已過,表現爲有一幀畫面爲空白。


代碼部分的優化方案:

1.  儘可能避免每幀處理, 能夠每隔幾幀處理一次
好比:
[C#]  純文本查看  複製代碼
?
1
function Update() { DoSomeThing(); }
可改成每5 幀處理一次:
[C#]  純文本查看  複製代碼
?
1
function Update() { if (Time.frameCount % 5 == 0) { DoSomeThing(); } }
2.  定時重複處理用InvokeRepeating  函數實現

好比,啓動0.5 秒後每隔1 秒執行一次 DoSomeThing  函數:
[C#]  純文本查看  複製代碼
?
1
2
function Start() { InvokeRepeating( "DoSomeThing" , 0.5, 1.0); }
  CancelInvoke( "你調用的方法" ); 中止InvokeRepeating
3.  優化 Update,FixedUpdate, LateUpdate  等每幀處理的函數, 函數裏面的變量儘可能在頭部聲明。
好比:
[C#]  純文本查看  複製代碼
?
1
function Update() { var pos: Vector3 = transform.position; }
可改成
[C#]  純文本查看  複製代碼
?
1
private var pos: Vector3; function Update(){ pos = transform.position; }


4.  主動回收垃圾
給某個 GameObject  綁上如下的代碼:
[C#]  純文本查看  複製代碼
?
1
function Update() { if (Time.frameCount % 50 == 0) { System.GC.Collect(); } }


5.  運行時儘可能減小 Tris   Draw Calls

預覽的時候,可點開 Stats ,查看圖形渲染的開銷狀況。特別注意 Tris   Draw Calls  這兩個參數。

通常來講,要作到:

Tris  保持在 7.5k  如下

Draw Calls  保持在 35  如下

6.  壓縮 Mesh

導入 3D  模型以後,在不影響顯示效果的前提下,最好打開 Mesh Compression

Off, Low, Medium, High  這幾個選項,可酌情選取。 對於單個Mesh最好使用一個材質。

7.  避免大量使用 unity  自帶的 Sphere  等內建 Mesh

Unity  內建的 Mesh ,多邊形的數量比較大,若是物體不要求特別圓滑,可導入其餘的簡單3D 模型代替。

8.  優化數學計算
儘可能避免使用float,而使用int,特別是在 手機遊戲中,儘可能少用複雜的數學函數,好比sin,cos等函數。改除法/爲乘法,例如:使用x*0.5f而不是 x/2.0f 。

9.若是你作了一個圖集是1024X1024 的。此時你的界面上只用了圖集中的一張很小的圖,那麼很抱歉1024X1024 這張大圖都須要載入你的內存裏面,1024 就是4M 的內存,若是你作了10 1024 的圖集,你的界面上恰好都只用了每一個圖集裏面的一張小圖,那麼再次抱歉你的內存直接飆40M 。意思是任何一個4096 的圖片,無論是圖集仍是texture ,他都佔用4*4=16M

====================================================================分割線=====================================================

一、在使用數組或ArrayList對象時應當注意

本帖隱藏的內容

[C#]  純文本查看  複製代碼
?
1
2
3
4
5
length=myArray.Length; 
for ( int i=0;i<length;i++) 
   
}
避免
[C#]  純文本查看  複製代碼
?
1
2
3
4
for ( int i=0;i<myArray.Length;i++) 
   
}


二、少使用臨時變量,特別是在Update OnGUI等實時調用的函數中。
[C#]  純文本查看  複製代碼
?
1
2
3
4
5
void Update() 
    Vector3 pos; 
    pos=transform.position; 
}

能夠改成:
[C#]  純文本查看  複製代碼
?
1
2
3
4
5
private Vector3 pos; 
void Update() 
    pos=transform.position; 
}
  
三、若是可能,將GameObject上沒必要要的腳本disable掉。
若是你有一個大的場景在你的遊戲中,而且敵方的位置在數公里意外,
這時你能夠disable你的敵方AI腳本直到它們接近攝像機爲止。
一個好的途徑來開啓或關閉GameObject是使用SetActiveRecursively(false),而且球形或盒型碰撞器設爲trigger。

四、刪除空的Update方法。
當經過Assets目錄建立新的腳本時,腳本里會包括一個Update方法,當你不使用時刪除它。

五、引用一個遊戲對象的最合乎邏輯的組件。
有人可能會這樣寫someGameObject.transform,gameObject.rigidbody.transform.gameObject.rigidbody.transform,可是這樣作了一些沒必要要的工做,
你能夠在最開始的地方引用它,像這樣:
[C#]  純文本查看  複製代碼
?
1
2
3
4
5
privateTransform myTrans;
void Start()
{
     myTrans=transform;
}

六、協同是一個好方法。
可使用協同程序來代替沒必要每幀都執行的方法。(還有InvokeRepeating方法也是一個好的取代Update的方法)。
七、儘量不要再Update或FixedUpdate中使用搜索方法(例如GameObject.Find()),你能夠像前面那樣在Start方法裏得到它。
八、不要使用SendMessage之類的方法,他比直接調用方法慢了100倍,你能夠直接調用或經過C#的委託來實現。
九、使用javascript或Boo語言時,你最好肯定變量的類型,不要使用動態類型,這樣會下降效率,
你能夠在腳本開頭使用#pragmastrict 來檢查,這樣當你編譯你的遊戲時就不會出現莫名其妙的錯誤了。
====================================================================分割線=====================================================
一、頂點性能     
通常來講,若是您想在iPhone 3GS或更新的設備上每幀渲染不超過40,000可見點,
那麼對於一些配備 MBX GPU的舊設備(好比,原始的 iPhone,如 iPhone 3g和 iPod Touch第1和第2代)來講,你應該保證每幀的渲染頂點在10000如下。
二、光照性能     
像素的動態光照將對每一個受影響的像素增長顯著的計算開銷,並可能致使物體會被渲染屢次。
爲了不這種狀況的發生,您應該避免對於任何單個物體都使用多個像素光照,並儘量地使用方向光。
須要注意的是像素光源是一個渲染模式(Render Mode)設置爲重要(Important)的光源。
像素的動態光照將對頂點變換增長顯著的開銷。因此,應該儘可能避免任何給定的物體被多個光源同時照亮的狀況。
對於靜態物體,採用烘焙光照方法則是更爲有效的方法。
三、角色     
每一個角色儘可能使用一個Skinned Mesh Renderer,這是由於當角色僅有一個 Skinned Mesh Renderer 時,
Unity 會使用可見性裁剪和包圍體更新的方法來優化角色的運動,而這種優化只有在角色僅含有一個 Skinned Mesh Renderer時纔會啓動。
角色的面數通常不要超過1500,骨骼數量少於30就好,角色Material數量通常1~2個爲最佳。
四、靜態物體     
對於靜態物體定點數要求少於500,UV的取值範圍不要超過(0,1)區間,這對於紋理的拼合優化頗有幫助。
不要在靜態物體上附加Animation組件,雖然加了對結果沒什麼影響,可是會增長CPU開銷。
五、攝像機     
將遠平面設置成合適的距離,遠平面過大會將一些沒必要要的物體加入渲染,下降效率。
另外咱們能夠根據不一樣的物體來設置攝像機的遠裁剪平面。Unity 提供了能夠根據不一樣的 layer 來設置不一樣的 view distance ,
因此咱們能夠實現將物體進行分層,大物體層設置的可視距離大些,而小物體層能夠設置地小些,
另外,一些開銷比較大的實體(如粒子系統)能夠設置得更小些等等。
六、DrawCall      
儘量地減小 Drawcall 的數量。 IOS 設備上建議不超過 100 。
減小的方法主要有以下幾種: Frustum Culling ,Occlusion Culling , Texture Packing 。 Frustum Culling 是 Unity 內建的,咱們須要作的就是尋求一個合適的遠裁剪平面;
Occlusion Culling ,遮擋剔除, Unity 內嵌了 Umbra ,一個很是好 OC 庫。
但 Occlusion Culling 也並非放之四海而皆準的,有時候進行 OC 反而比不進行還要慢,
建議在 OC 以前先肯定本身的場景是否適合利用 OC 來優化; Texture Packing ,或者叫 Texture Atlasing ,
是將同種 shader 的紋理進行拼合,根據 Unity 的 static batching 的特性來減小 draw call 。
建議使用,但也有弊端,那就是必定要將場景中距離相近的實體紋理進行拼合,不然,拼合後極可能會增長每幀渲染所需的紋理大小,
加大內存帶寬的負擔。這也就是爲何會出現「 DrawCall 降了,渲染速度也變慢了」的緣由。
===========================================分割線==========================
1.粒子系統運行在iPhone上時很慢,怎麼辦?
答:iPhone擁有相對較低的fillrate 。
若是您的粒子效果覆蓋大部分的屏幕,並且是multiple layers的,這樣即便最簡單的shader,也能讓iPhone傻眼。
咱們建議把您的粒子效果baking成紋理序列圖。
而後在運行時可使用1-2個粒子,經過動畫紋理來顯示它們。這種方式能夠取得很好的效果,以最小的代價。  

===========================================分割線==============================
1.操做transform.localPosition的時候請當心
移動GameObject是很是日常的一件事情,如下代碼看起來很簡單:
[C#]  純文本查看  複製代碼
?
1
transform.localPosition += new Vector3 ( 10.0f * Time.deltaTime, 0.0f, 0.0f );


可是當心了,假設上面這個GameObject有一個parent, 而且這個parent GameObject的localScale是(2.0f,2.0f,2.0f)。你的GameObject將會移動20.0個單位/秒。
由於該 GameObject的world position等於:
[C#]  純文本查看  複製代碼
?
1
Vector3 offset = new Vector3( my.localPosition.x * parent.lossyScale.x, my.localPosition.y * parent.lossyScale.y, my.localPosition.z * parent.lossyScale.z );Vector3 worldPosition = parent.position + parent.rotation * offset;


換句話說,上面這種直接操做localPosition的方式是在沒有考慮scale計算的時候進行的,爲了解決這個 問題unity3d提供了Translate函數,
因此正確的作法應該是: 
[C#]  純文本查看  複製代碼
?
1
transform.Translate ( 10.0f * Time.deltaTime, 0.0f, 0.0f );


曝出在Inspector的變量一樣的也能被Animation View Editor所使用
有時候咱們會想用Unity3D自帶的Animation View Editor來作一些簡單的動畫操做。而Animation Editor不只能夠操做Unity3D自身的component,
還能夠操做咱們自定義的MonoBehavior中的各個Property。因此加入 你有個float值須要用曲線操做,你能夠簡單的將它曝出到成能夠serialize的類型,如:
[C#]  純文本查看  複製代碼
?
1
public float foobar = 1.0f;

這樣,這個變量不只會在Inspector中出現,還能夠在animation view中進行操做,生成AnimationClip供咱們經過AnimationComponent調用。
範例:
[C#]  純文本查看  複製代碼
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
public class TestCurve : MonoBehaviour
{
public float foobar = 0.0f;
IEnumerator Start ()
{
yield return new WaitForSeconds (2.0f);
animation.Play( "foobar_op" );
InvokeRepeating ( "LogFoobar" , 0.0f, 0.2f );
yield return new WaitForSeconds (animation[ "foobar_op" ].length);
CancelInvoke ( "LogFoobar" );
}
void LogFoobar ()
{
Debug.Log( "foobar = " + foobar); }}



2.GetComopnent<T> 能夠取父類類型
Unity3D 容許咱們對MonoBehavior作派生,因此你可能有如下代碼:

[C#]  純文本查看  複製代碼
?
1
public class foo : MonoBehaviour { ...} public class bar : foo { ...}


假設咱們如今有A,B兩個GameObject, A包含foo, B包含bar, 當咱們寫

[C#]  純文本查看  複製代碼
?
1
foo comp1 = A.GetComponent<foo>();bar comp2 = B.GetComponent<bar>();


能夠看到comp1, comp2都獲得了應得的Component。那若是咱們對B的操做改爲:

[C#]  純文本查看  複製代碼
?
1
foo comp2 = B.GetComponent<foo>();


答案是comp2仍是會返回bar Component而且轉換爲foo類型。你一樣能夠用向下轉換獲得有效變量:

bar comp2_bar = comp2 as bar;
合理利用GetComponent<base_type>()可讓咱們設計Component的時候耦合性更低。

3.Invoke, yield 等函數會受 Time.timeScale 影響
Unity3D提供了一個十分方便的調節時間的函數Time.timeScale。對於初次使用Unity3D的使用者,
會誤導性的認爲Time.timeScale一樣能夠適用於遊戲中的暫停(Pause)和開始(Resume)。
因此不少人有習慣寫:
[C#]  純文本查看  複製代碼
?
1
Time.timeScale = 0.0f


對於遊戲的暫停/開始,是遊戲系統設計的一部分,而Time.timeScale不不是用於這個部分的操做。
正確的作法應該是蒐集須要暫停的腳本或 GameObject,
經過設置他們的enabled = false 來中止他們的腳本活動或者經過特定函數來設置這些物件暫停時須要關閉那些操做。

Time.timeScale 更多的是用於遊戲中慢鏡頭的播放等操做,在服務器端主導的遊戲中更應該避免此類操做。
值得一提的是,Unity3D的許多時間相關的函數都和 timeScale掛鉤,而timeScale = 0.0f將使這些函數或動畫處於徹底中止的狀態,這也是爲何它不適合作暫停操做的主要緣由。

這裏列出受timeScale影響的一些主要函數和Component:
MonoBehaviour.Invoke(…)
MonoBehaviour.InvokeRepeating(…)
yield WaitForSeconds(…)
GameObject.Destroy(…)
Animation Component
Time.time, Time.deltaTime

4.Coroutine 和 IEnumerator的關係
初寫Unity3D C#腳本的時候,咱們常常會犯的錯誤是調用Coroutine函數忘記使用StartCoroutine的方式。如:

TestCoroutine.cs
[C#]  純文本查看  複製代碼
?
1
IEnumerator CoLog () { yield return new WaitForSeconds (2.0f); Debug.Log( "hello foobar" );}


當咱們用如下代碼去調用上述函數:
[C#]  純文本查看  複製代碼
?
1
TestCoroutine testCo = GetComponent<TestCoroutine>();testCo.CoLog ();testCo.StartCoroutine ( "CoLog" );


那麼testCo.CoLog()的調用將不會起任何做用。

5.StartCoroutine, InvokeRepeating 和其調用者關聯
一般咱們只在一份GameObject中去調用StartCoroutine或者InvokeRepeating,
咱們寫:
[C#]  純文本查看  複製代碼
?
1
StartCoroutine ( Foobar() );InvokeRepeating ( "Foobar" , 0.0f, 0.1f );


因此若是這個GameObject被disable或者destroy了,這些coroutine和invokes將會被取消。就比如咱們手動調用:
[C#]  純文本查看  複製代碼
?
1
StopAllCoroutines ();CancelInvoke ();


這看上去很美妙,對於AI來講,這就像告訴一個NPC你已經死了,你本身的那些小動做就都聽下來吧。

可是注意了,假如這樣的代碼用在了一個Manager類型的控制AI上,他有可能去控制其餘的AI, 也有可能經過Invoke, Coroutine去作一些微線程的操做,這個時候就要明確StartCoroutine或者InvokeRepeating的調用者的設計。討論以前我 們先要理解,StartCoroutine或InvokeRepeating的調用會在該MonoBehavior中開啓一份Thread State, 並將須要操做的函數,變量以及計時器放入這份Stack中經過並在引擎每幀Update的最後,Renderer渲染以前統一作處理。因此若是這個 MonoBehavior被Destroy了,那麼這份Thread State也就隨之消失,那麼全部他存儲的調用也就失效了。

若是有兩份GameObject A和B, 他們互相知道對方,假如A中經過StartCoroutine或InvokeRepeating去調用B的函數從而控制B,這個時候Thread State是存放在A裏,當A被disable或者destroy了,這些可能須要一段時間的控制函數也就失效了,這個時候B明明還沒死,也不會動了。更 好的作法是讓在A的函數中經過B.StartCoroutine ( … ) 讓這份Thread State存放於B中。

[C#]  純文本查看  複製代碼
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
// class TestCortouine
public class TestCoroutine : MonoBehaviour
{
public IEnumerator CoLog ( string _name )
{
Debug.Log(_name + " hello foobar 01" );
yield return new WaitForSeconds (2.0f);
Debug.Log(_name + " hello foobar 02" ); }}
// component attached on GameObject A
public class A: MonoBehaviour
{
public GameObject B;  void Start ()
{ TestCoroutine compB = B.GetComponent<TestCoroutine>(); 
// GOOD, thread state in B
// same as: comp
B.StartCoroutine ( "CoLog" , "B" );
compB.StartCoroutine ( compB.CoLog( "B" ) );
// BAD, thread state in A
StartCoroutine ( compB.CoLog( "A" ) );
Debug.Log( "Bye bye A, we'll miss you" );
Destroy(gameObject);
// T_T I don't want to die... }}


以上代碼,獲得的結果將會是:
B hello foobar 01A hello foobar 01Bye bye A, we'll miss youB hello foobar 02
如不須要Start, Update, LateUpdate函數,請去掉他們
當你的腳本里沒有任何Start, Update, LateUpdate函數的時候,Unity3D將不會將它們加入到他的Update List中,有利於腳本總體效率的提高。

咱們能夠從這兩個腳本中看到區別:

Update_01.cs
[C#]  純文本查看  複製代碼
?
1
public class Update_01 : MonoBehaviour { void Start () {} void Update () {}}


Update_02.cs
[C#]  純文本查看  複製代碼
?
1
2
public class Update_02 : MonoBehaviour {
}



===========================================分割線==============
1.減小固定增量時間
將固定增量時間值設定在0.04-0.067區間(即,每秒15-25幀)。您能夠經過Edit->Project Settings->Time來改變這個值。這樣作下降了FixedUpdate函數被調用的頻率以及物理引擎執行碰撞檢測與剛體更新的頻率。若是您使用了較低的固定增量時間,而且在主角身上使用了剛體部件,那麼您能夠啓用插值辦法來平滑剛體組件。


2.減小GetComponent的調用使用 GetComponent或內置組件訪問器會產生明顯的開銷。您能夠經過一次獲取組件的引用來避免開銷,並將該引用分配給一個變量(有時稱爲"緩存"的引用)。
例如,若是您使用以下的代碼:
[JavaScript]  純文本查看  複製代碼
?
1
2
3
4
function Update ()
{
transform.Translate(0, 1, 0);
}


經過下面的更改您將得到更好的性能:
[JavaScript]  純文本查看  複製代碼
?
01
02
03
04
05
06
07
08
09
10
11
12
13
var myTransform : Transform;
 
function Awake () {
 
myTransform = transform;
 
}
 
function Update () {
 
myTransform.Translate(0, 1, 0);
 
}


3.避免分配內存
您應該避免分配新對象,除非你真的須要,由於他們再也不在使用時,會增長垃圾回收系統的開銷。
您能夠常常重複使用數組和其餘對象,而不是分配新的數組或對象。這樣作好處則是儘可能減小垃圾的回收工做。
同時,在某些可能的狀況下,您也可使用結構(struct)來代替類(class)。
這是由於,結構變量主要存放在棧區而非堆區。由於棧的分配較快,而且不調用垃圾回收操做,因此當結構變量比較小時能夠提高程序的運行性能。
可是當結構體較大時,雖然它仍可避免分配/回收的開銷,而它因爲"傳值"操做也會致使單獨的開銷,實際上它可能比等效對象類的效率還要低。

4.最小化GUI
使用GUILayout 函數能夠很方便地將GUI元素進行自動佈局。然而,這種自動化天然也附帶着必定的處理開銷。
您能夠經過手動的GUI功能佈局來避免這種開銷。
此外,您也能夠設置一個腳本的useGUILayout變量爲 false來徹底禁用GUI佈局:
[JavaScript]  純文本查看  複製代碼
?
1
2
3
function Awake () {
useGUILayout = false ;
}


5.使用iOS腳本調用優化功能
UnityEngine 命名空間中的函數的大多數是在 C/c + +中實現的。
從Mono的腳本調用 C/C++函數也存在着必定的性能開銷。
您可使用iOS腳本調用優化功能(菜單:Edit->Project Settings->Player)讓每幀節省1-4毫秒。
此設置的選項有:
Slow and Safe – Mono內部默認的處理異常的調用
Fast and Exceptions Unsupported –一個快速執行的Mono內部調用。
不過,它並不支持異常,所以應謹慎使用。
它對於不須要顯式地處理異常(也不須要對異常進行處理)的應用程序來講,是一個理想的候選項。

6.優化垃圾回收
如上文所述,您應該儘可能避免分配操做。
可是,考慮到它們是不能徹底杜絕的,因此咱們提供兩種方法來讓您儘可能減小它們在遊戲運行時的使用:
若是堆比較小,則進行快速而頻繁的垃圾回收

這一策略比較適合運行時間較長的遊戲,其中幀率是否平滑過渡是主要的考慮因素。
像這樣的遊戲一般會頻繁地分配小塊內存,但這些小塊內存只是暫時地被使用。
若是在iOS系統上使用該策略,那麼一個典型的堆大小是大約 200 KB,這樣在iPhone 3G設備上,
垃圾回收操做將耗時大約 5毫秒。若是堆大小增長到1 MB時,該回收操做將耗時大約 7ms。
所以,在普通幀的間隔期進行垃圾回收有時候是一個不錯的選擇。
一般,這種作法會讓回收操做執行的更加頻繁(有些回收操做並非嚴格必須進行的),
但它們能夠快速處理而且對遊戲的影響很小:
[C#]  純文本查看  複製代碼
?
1
2
3
4
if (Time.frameCount % 30 == 0)
{
System.GC.Collect();
}


可是,您應該當心地使用這種技術,而且經過檢查Profiler來確保這種操做確實能夠下降您遊戲的垃圾回收時間
若是堆比較大,則進行緩慢且不頻繁的垃圾回收

這一策略適合於那些內存分配 (和回收)相對不頻繁,而且能夠在遊戲停頓期間進行處理的遊戲。
若是堆足夠大,但尚未大到被系統關掉的話,這種方法是比較適用的。
可是,Mono運行時會盡量地避免堆的自動擴大。
所以,您須要經過在啓動過程當中預分配一些空間來手動擴展堆(ie,你實例化一個純粹影響內存管理器分配的"無用"對象):
[JavaScript]  純文本查看  複製代碼
?
1
2
3
4
5
6
7
8
9
function Start()
{
var tmp = new System.Object[1024];
// make allocations in smaller blocks to avoid them to be treated in a special way, which is designed for large blocks
for ( var i : int = 0; i < 1024; i++)
tmp = new byte[1024];
// release reference
tmp = null ;
}

遊戲中的暫停是用來對堆內存進行回收,而一個足夠大的堆應該不會在遊戲的暫停與暫停之間被徹底佔滿。因此,當這種遊戲暫停發生時,您能夠顯式請求一次垃圾回收:
System.GC.Collect();
另外,您應該謹慎地使用這一策略並時刻關注Profiler的統計結果,而不是假定它已經達到了您想要的效果。



U3D開發中的一些多餘組件問題:

在美術製做場景中,都會帶上 MeshCollider, Animation, Animator 等組件,包括材質中的shader部分也會使用mobile以外的diffuse,因此有一句話:「若是相信美術,母豬都會上樹」, 程序員們仍是本身動手來幹吧:

[ MenuItem ( "Tools/刪除場景沒用的組件" ) ]
static public void Remove ( )
{
   //獲取當前場景裏的全部遊戲對象
GameObject [ ] rootObjects = ( GameObject [ ] ) UnityEngine . Object . FindObjectsOfType ( typeof ( GameObject ) ) ;
//遍歷遊戲對象
foreach ( GameObject go in rootObjects )
{
   //若是發現Render的shader是Diffuse而且顏色是白色,那麼將它的shader修改爲Mobile/Diffuse
if ( go != null && go . transform . parent != null )
{
Renderer render = go . GetComponent < Renderer > ( ) ;
   if ( render != null && render . sharedMaterial != null && render . sharedMaterial . shader . name == "Diffuse" && render . sharedMaterial . color == Color . white )
{
   render . sharedMaterial . shader = Shader . Find ( "Mobile/Diffuse" ) ;
}
}
 
       //刪除全部的MeshCollider
foreach ( MeshCollider collider in UnityEngine . Object . FindObjectsOfType ( typeof ( MeshCollider ) ) )
{
DestroyImmediate ( collider ) ;
}
 
//刪除沒有用的動畫組件
foreach ( Animation animation in UnityEngine . Object . FindObjectsOfType ( typeof ( Animation ) ) )
{
if ( animation . clip == null )
DestroyImmediate ( animation ) ;
}
 
       //應該沒有人用Animator吧? 避免美術弄錯我都所有刪除了。
foreach ( Animator animator in UnityEngine . Object . FindObjectsOfType ( typeof ( Animator ) ) )
{
DestroyImmediate ( animator ) ;
}
}
//保存
AssetDatabase . SaveAssets ( ) ;
}





有提出改進需求的童鞋請留言;

相關文章
相關標籤/搜索