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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

unity3d优化总结篇

發(fā)布時間:2025/3/14 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 unity3d优化总结篇 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉自http://www.unitymanual.com/thread-21597-1-1.html

?

此總結由自己經(jīng)驗及網(wǎng)上收集整理優(yōu)化內容 包括:
1.代碼方面;
2.函數(shù)使用方面;
3.ngui注意方面;
4.數(shù)學運算方面;
5.內存方面;
6.垃圾回收方面 等等...
本著相互交流 共同進步的原則



好了,總結如下:

1.?盡量避免每幀處理,可以每隔幾幀處理一次 比如: [C#]?純文本查看?復制代碼 function Update() { DoSomeThing(); } 可改為每5幀處理一次: [C#]?純文本查看?復制代碼 function Update() { if(Time.frameCount % 5 == 0) { DoSomeThing(); } } 2.?定時重復處理用InvokeRepeating?函數(shù)實現(xiàn)

?

比如,啟動0.5秒后每隔1秒執(zhí)行一次?DoSomeThing?函數(shù): [C#]?純文本查看?復制代碼 function Start() { InvokeRepeating("DoSomeThing", 0.5, 1.0); }
CancelInvoke("你調用的方法"); 停止InvokeRepeating 3.?優(yōu)化?Update,FixedUpdate, LateUpdate?等每幀處理的函數(shù),函數(shù)里面的變量盡量在頭部聲明。 比如: [C#]?純文本查看?復制代碼 function Update() { var pos: Vector3 = transform.position; } 可改為 [C#]?純文本查看?復制代碼 private var pos: Vector3; function Update(){ pos = transform.position; }

?

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

?

5.?運行時盡量減少?Tris??Draw Calls

?

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

?

一般來說,要做到:

?

Tris?保持在?7.5k?以下

?

Draw Calls?保持在?35?以下

?

6.?壓縮?Mesh

?

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

?

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

?

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

?

Unity?內建的?Mesh,多邊形的數(shù)量比較大,如果物體不要求特別圓滑,可導入其他的簡單3D模型代替。

?

8.?優(yōu)化數(shù)學計算 盡量避免使用float,而使用int,特別是在手機游戲中,盡量少用復雜的數(shù)學函數(shù),比如sin,cos等函數(shù)。改除法/為乘法,例如:使用x*0.5f而不是 x/2.0f 。

?

9.如果你做了一個圖集是1024X1024的。此時你的界面上只用了圖集中的一張很小的圖,那么很抱歉1024X1024這張大圖都需要載入你的內存里面,1024就是4M的內存,如果你做了101024的圖集,你的界面上剛好都只用了每個圖集里面的一張小圖,那么再次抱歉你的內存直接飆40M。意思是任何一個4096的圖片,不管是圖集還是texture,他都占用4*4=16M ? ====================================================================分割線===================================================== ? 1、在使用數(shù)組或ArrayList對象時應當注意

本帖隱藏的內容

[C#]?純文本查看?復制代碼 length=myArray.Length;??
for(int i=0;i<length;i++)??
{??

} 避免 [C#]?純文本查看?復制代碼 for(int i=0;i<myArray.Length;i++)??
{??

}??

2、少使用臨時變量,特別是在Update OnGUI等實時調用的函數(shù)中。 [C#]?純文本查看?復制代碼 void Update()??
{??
Vector3 pos;??
pos=transform.position;??
}??
可以改為: [C#]?純文本查看?復制代碼 private Vector3 pos;??
void Update()??
{??
pos=transform.position;??
} ??
3、如果可能,將GameObject上不必要的腳本disable掉。
如果你有一個大的場景在你的游戲中,并且敵方的位置在數(shù)千米意外,
這時你可以disable你的敵方AI腳本直到它們接近攝像機為止。
一個好的途徑來開啟或關閉GameObject是使用SetActiveRecursively(false),并且球形或盒型碰撞器設為trigger。

4、刪除空的Update方法。
當通過Assets目錄創(chuàng)建新的腳本時,腳本里會包括一個Update方法,當你不使用時刪除它。

5、引用一個游戲對象的最合乎邏輯的組件。
有人可能會這樣寫someGameObject.transform,gameObject.rigidbody.transform.gameObject.rigidbody.transform,但是這樣做了一些不必要的工作,
你可以在最開始的地方引用它,像這樣:
[C#]?純文本查看?復制代碼 privateTransform myTrans;
void Start()
{
myTrans=transform;
}
6、協(xié)同是一個好方法。
可以使用協(xié)同程序來代替不必每幀都執(zhí)行的方法。(還有InvokeRepeating方法也是一個好的取代Update的方法)。
7、盡可能不要再Update或FixedUpdate中使用搜索方法(例如GameObject.Find()),你可以像前面那樣在Start方法里獲得它。
8、不要使用SendMessage之類的方法,他比直接調用方法慢了100倍,你可以直接調用或通過C#的委托來實現(xiàn)。
9、使用javascript或Boo語言時,你最好確定變量的類型,不要使用動態(tài)類型,這樣會降低效率,
你可以在腳本開頭使用#pragmastrict 來檢查,這樣當你編譯你的游戲時就不會出現(xiàn)莫名其妙的錯誤了。
====================================================================分割線=====================================================
1、頂點性能? ???
一般來說,如果您想在iPhone 3GS或更新的設備上每幀渲染不超過40,000可見點,
那么對于一些配備 MBX GPU的舊設備(比如,原始的 iPhone,如 iPhone 3g和 iPod Touch第1和第2代)來說,你應該保證每幀的渲染頂點在10000以下。
2、光照性能? ???
像素的動態(tài)光照將對每個受影響的像素增加顯著的計算開銷,并可能導致物體會被渲染多次。
為了避免這種情況的發(fā)生,您應該避免對于任何單個物體都使用多個像素光照,并盡可能地使用方向光。
需要注意的是像素光源是一個渲染模式(Render Mode)設置為重要(Important)的光源。
像素的動態(tài)光照將對頂點變換增加顯著的開銷。所以,應該盡量避免任何給定的物體被多個光源同時照亮的情況。
對于靜態(tài)物體,采用烘焙光照方法則是更為有效的方法。
3、角色? ???
每個角色盡量使用一個Skinned Mesh Renderer,這是因為當角色僅有一個 Skinned Mesh Renderer 時,
Unity 會使用可見性裁剪和包圍體更新的方法來優(yōu)化角色的運動,而這種優(yōu)化只有在角色僅含有一個 Skinned Mesh Renderer時才會啟動。
角色的面數(shù)一般不要超過1500,骨骼數(shù)量少于30就好,角色Material數(shù)量一般1~2個為最佳。
4、靜態(tài)物體? ???
對于靜態(tài)物體定點數(shù)要求少于500,UV的取值范圍不要超過(0,1)區(qū)間,這對于紋理的拼合優(yōu)化很有幫助。
不要在靜態(tài)物體上附加Animation組件,雖然加了對結果沒什么影響,但是會增加CPU開銷。
5、攝像機? ???
將遠平面設置成合適的距離,遠平面過大會將一些不必要的物體加入渲染,降低效率。
另外我們可以根據(jù)不同的物體來設置攝像機的遠裁剪平面。Unity 提供了可以根據(jù)不同的 layer 來設置不同的 view distance ,
所以我們可以實現(xiàn)將物體進行分層,大物體層設置的可視距離大些,而小物體層可以設置地小些,
另外,一些開銷比較大的實體(如粒子系統(tǒng))可以設置得更小些等等。
6、DrawCall? ?? ?
盡可能地減少 Drawcall 的數(shù)量。 IOS 設備上建議不超過 100 。
減少的方法主要有如下幾種: Frustum Culling ,Occlusion Culling , Texture Packing 。 Frustum Culling 是 Unity 內建的,我們需要做的就是尋求一個合適的遠裁剪平面;
Occlusion Culling ,遮擋剔除, Unity 內嵌了 Umbra ,一個非常好 OC 庫。
但 Occlusion Culling 也并不是放之四海而皆準的,有時候進行 OC 反而比不進行還要慢,
建議在 OC 之前先確定自己的場景是否適合利用 OC 來優(yōu)化; Texture Packing ,或者叫 Texture Atlasing ,
是將同種 shader 的紋理進行拼合,根據(jù) Unity 的 static batching 的特性來減少 draw call 。
建議使用,但也有弊端,那就是一定要將場景中距離相近的實體紋理進行拼合,否則,拼合后很可能會增加每幀渲染所需的紋理大小,
加大內存帶寬的負擔。這也就是為什么會出現(xiàn)“ DrawCall 降了,渲染速度也變慢了”的原因。
===========================================分割線==========================
1.粒子系統(tǒng)運行在iPhone上時很慢,怎么辦?
答:iPhone擁有相對較低的fillrate 。
如果您的粒子效果覆蓋大部分的屏幕,而且是multiple layers的,這樣即使最簡單的shader,也能讓iPhone傻眼。
我們建議把您的粒子效果baking成紋理序列圖。
然后在運行時可以使用1-2個粒子,通過動畫紋理來顯示它們。這種方式可以取得很好的效果,以最小的代價。??

===========================================分割線==============================
1.操作transform.localPosition的時候請小心
移動GameObject是非常平常的一件事情,以下代碼看起來很簡單:
[C#]?純文本查看?復制代碼 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#]?純文本查看?復制代碼 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函數(shù),
所以正確的做法應該是:?
[C#]?純文本查看?復制代碼 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#]?純文本查看?復制代碼 public float foobar = 1.0f;
這樣,這個變量不僅會在Inspector中出現(xiàn),還可以在animation view中進行操作,生成AnimationClip供我們通過AnimationComponent調用。
范例:
[C#]?純文本查看?復制代碼 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#]?純文本查看?復制代碼 public class foo : MonoBehaviour { ...} public class bar : foo { ...}

假設我們現(xiàn)在有A,B兩個GameObject, A包含foo, B包含bar, 當我們寫

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

可以看到comp1, comp2都得到了應得的Component。那如果我們對B的操作改成:

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

答案是comp2還是會返回bar Component并且轉換為foo類型。你同樣可以用向下轉換得到有效變量:

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

3.Invoke, yield 等函數(shù)會受 Time.timeScale 影響
Unity3D提供了一個十分方便的調節(jié)時間的函數(shù)Time.timeScale。對于初次使用Unity3D的使用者,
會誤導性的認為Time.timeScale同樣可以適用于游戲中的暫停(Pause)和開始(Resume)。
所以很多人有習慣寫:
[C#]?純文本查看?復制代碼 Time.timeScale = 0.0f

對于游戲的暫停/開始,是游戲系統(tǒng)設計的一部分,而Time.timeScale不不是用于這個部分的操作。
正確的做法應該是搜集需要暫停的腳本或 GameObject,
通過設置他們的enabled = false 來停止他們的腳本活動或者通過特定函數(shù)來設置這些物件暫停時需要關閉那些操作。

Time.timeScale 更多的是用于游戲中慢鏡頭的播放等操作,在服務器端主導的游戲中更應該避免此類操作。
值得一提的是,Unity3D的許多時間相關的函數(shù)都和 timeScale掛鉤,而timeScale = 0.0f將使這些函數(shù)或動畫處于完全停止的狀態(tài),這也是為什么它不適合做暫停操作的主要原因。

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

4.Coroutine 和 IEnumerator的關系
初寫Unity3D C#腳本的時候,我們經(jīng)常會犯的錯誤是調用Coroutine函數(shù)忘記使用StartCoroutine的方式。如:

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

當我們用以下代碼去調用上述函數(shù):
[C#]?純文本查看?復制代碼 TestCoroutine testCo = GetComponent<TestCoroutine>();testCo.CoLog ();testCo.StartCoroutine ( "CoLog" );

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

5.StartCoroutine, InvokeRepeating 和其調用者關聯(lián)
通常我們只在一份GameObject中去調用StartCoroutine或者InvokeRepeating,
我們寫:
[C#]?純文本查看?復制代碼 StartCoroutine ( Foobar() );InvokeRepeating ( "Foobar", 0.0f, 0.1f );

所以如果這個GameObject被disable或者destroy了,這些coroutine和invokes將會被取消。就好比我們手動調用:
[C#]?純文本查看?復制代碼 StopAllCoroutines ();CancelInvoke ();

這看上去很美妙,對于AI來說,這就像告訴一個NPC你已經(jīng)死了,你自己的那些小動作就都聽下來吧。

但是注意了,假如這樣的代碼用在了一個Manager類型的控制AI上,他有可能去控制其他的AI, 也有可能通過Invoke, Coroutine去做一些微線程的操作,這個時候就要明確StartCoroutine或者InvokeRepeating的調用者的設計。討論之前我 們先要理解,StartCoroutine或InvokeRepeating的調用會在該MonoBehavior中開啟一份Thread State, 并將需要操作的函數(shù),變量以及計時器放入這份Stack中通過并在引擎每幀Update的最后,Renderer渲染之前統(tǒng)一做處理。所以如果這個 MonoBehavior被Destroy了,那么這份Thread State也就隨之消失,那么所有他存儲的調用也就失效了。

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

[C#]?純文本查看?復制代碼
// 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函數(shù),請去掉他們
當你的腳本里沒有任何Start, Update, LateUpdate函數(shù)的時候,Unity3D將不會將它們加入到他的Update List中,有利于腳本整體效率的提升。

我們可以從這兩個腳本中看到區(qū)別:

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

Update_02.cs
[C#]?純文本查看?復制代碼 public class Update_02 : MonoBehaviour {
}


===========================================分割線==============
1.減少固定增量時間
將固定增量時間值設定在0.04-0.067區(qū)間(即,每秒15-25幀)。您可以通過Edit->Project Settings->Time來改變這個值。這樣做降低了FixedUpdate函數(shù)被調用的頻率以及物理引擎執(zhí)行碰撞檢測與剛體更新的頻率。如果您使用了較低的固定增量時間,并且在主角身上使用了剛體部件,那么您可以啟用插值辦法來平滑剛體組件。


2.減少GetComponent的調用使用 GetComponent或內置組件訪問器會產(chǎn)生明顯的開銷。您可以通過一次獲取組件的引用來避免開銷,并將該引用分配給一個變量(有時稱為"緩存"的引用)。
例如,如果您使用如下的代碼:
[JavaScript]?純文本查看?復制代碼 function Update ()
{
transform.Translate(0, 1, 0);
}

通過下面的更改您將獲得更好的性能:
[JavaScript]?純文本查看?復制代碼 var myTransform : Transform;

function Awake () {

myTransform = transform;

}

function Update () {

myTransform.Translate(0, 1, 0);

}

3.避免分配內存
您應該避免分配新對象,除非你真的需要,因為他們不再在使用時,會增加垃圾回收系統(tǒng)的開銷。
您可以經(jīng)常重復使用數(shù)組和其他對象,而不是分配新的數(shù)組或對象。這樣做好處則是盡量減少垃圾的回收工作。
同時,在某些可能的情況下,您也可以使用結構(struct)來代替類(class)。
這是因為,結構變量主要存放在棧區(qū)而非堆區(qū)。因為棧的分配較快,并且不調用垃圾回收操作,所以當結構變量比較小時可以提升程序的運行性能。
但是當結構體較大時,雖然它仍可避免分配/回收的開銷,而它由于"傳值"操作也會導致單獨的開銷,實際上它可能比等效對象類的效率還要低。

4.最小化GUI
使用GUILayout 函數(shù)可以很方便地將GUI元素進行自動布局。然而,這種自動化自然也附帶著一定的處理開銷。
您可以通過手動的GUI功能布局來避免這種開銷。
此外,您也可以設置一個腳本的useGUILayout變量為 false來完全禁用GUI布局:
[JavaScript]?純文本查看?復制代碼 function Awake () {
useGUILayout = false;
}

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

6.優(yōu)化垃圾回收
如上文所述,您應該盡量避免分配操作。
但是,考慮到它們是不能完全杜絕的,所以我們提供兩種方法來讓您盡量減少它們在游戲運行時的使用:
如果堆比較小,則進行快速而頻繁的垃圾回收

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

但是,您應該小心地使用這種技術,并且通過檢查Profiler來確保這種操作確實可以降低您游戲的垃圾回收時間
如果堆比較大,則進行緩慢且不頻繁的垃圾回收

這一策略適合于那些內存分配 (和回收)相對不頻繁,并且可以在游戲停頓期間進行處理的游戲。
如果堆足夠大,但還沒有大到被系統(tǒng)關掉的話,這種方法是比較適用的。
但是,Mono運行時會盡可能地避免堆的自動擴大。
因此,您需要通過在啟動過程中預分配一些空間來手動擴展堆(ie,你實例化一個純粹影響內存管理器分配的"無用"對象):
[JavaScript]?純文本查看?復制代碼 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;
}
游戲中的暫停是用來對堆內存進行回收,而一個足夠大的堆應該不會在游戲的暫停與暫停之間被完全占滿。所以,當這種游戲暫停發(fā)生時,您可以顯式請求一次垃圾回收:
System.GC.Collect();
另外,您應該謹慎地使用這一策略并時刻關注Profiler的統(tǒng)計結果,而不是假定它已經(jīng)達到了您想要的效果。

轉載于:https://www.cnblogs.com/mrblue/p/4520636.html

總結

以上是生活随笔為你收集整理的unity3d优化总结篇的全部內容,希望文章能夠幫你解決所遇到的問題。

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