1.對于不能使用事件觸發(fā)的代碼段,并不意味著每一幀都要去處理准脂。
void Update()
{
? ? ExampleExpensiveFunction();
}
可以通過以下代碼將這些邏輯每隔x?幀做一次處理。
void?Update()
{
????if(Time.frameCount % interval == 0)
? ? ? ? ExampleExpensiveFunction();
}
或者可以通過如下代碼將重量級的邏輯拆分到不同的幀去執(zhí)行
void?Update()
{
? ? if(Time.frameCount % interval ==0)
? ? ? ? ExampleExpensiveFunction1();
? ? else if(Time.frameCount % interval == 1)
? ? ? ? ExampleExpensiveFunction2();
}
2.盡量少使用的昂貴API
SendMessage()?BroadcastMessage() 內部使用了反射眼俊,使用事件或代理替代意狠。
Find() 需要Unity?遍歷所有內存中的GameObject粟关,建議不要使用疮胖。
Transform.rotation?Transform.position?設置Transform?的旋轉和世界坐標會觸發(fā)OnTransformChange?通知其所有的子孫Transform,因此相對來說比較昂貴尤其是那些有很多子孫的Transform闷板,應該盡量避免頻繁地賦值澎灸。
獲取位置信息時,優(yōu)先使用Transform.localPosition?而不是Transform.position遮晚,后者每次調用時都會重新計算物體的世界坐標性昭,而localPosition?則是一個緩存在Transform?中的變量,如果需要頻繁使用世界坐標县遣,那么建議緩存下來糜颠。
Update()?LateUpdate() 等生命期函數(shù)都有隱藏的消耗,所以即使是函數(shù)內部什么也不做也有消耗萧求,建議不要保留空的生命期函數(shù)其兴,尤其是Update() 這種每幀都會調用的生命期函數(shù)。
Vector?Vector?系列的magiture?方法和Distance?方法使用了平方根計算夸政,當僅僅需要比較兩個向量的長度的時候元旬,使用sqrMagnitude?效率更高。
Camera.main?不要使用守问,原因是內部調用了Find()?方法匀归。
3.利用是否在視椎體內的信息來優(yōu)化代碼。
private?Renderer myRenderer;
void Start()
{
? ? myRenderer = GetComponent<Renderer>();
}
void Update()
{
? ? UpdateTransformPosition();
? ? if(myRenderer.isVisible)
? ? {
? ? ? ? ExampleExpensiveFunction();
????}
}
4.可以使用LOD?技術提供的信息來優(yōu)化代碼
Unity?提供了CullingGroup?API?用來提供給開發(fā)者Culling?和LOD?相關的信息耗帕,使用這些信息可以實現(xiàn)類似meshrenderer?的基于距離來執(zhí)行不同效果的邏輯穆端。
5.關于垃圾回收相關的優(yōu)化建議
減少代碼產生垃圾的建議:
緩存:經(jīng)常重復調用的方法中如果有堆內存分配和回收邏輯,應該在特定時期將這些引用緩存下來仿便,避免每次調用都要分配對內存
void OnTriggerEnter(Collider other)
{
? ? Renderer[] allRenderers = FindObjectsOfType<Renderer>();
????ExampleFunction(allRenderers);
}
改為
private?Renderer[] allRenderers;
void Start()
{
? ? allRenderers = FindObjectsOfType<Renderer>();
}
void OnTriggerEnter(Collider other)
{
? ? ExampleFunction(allRenderers);
}
不要在頻繁調用的方法(Update)中分配堆內存
使用容器類時体啰,使用Clear()?方法代替生成新容器(或者使用容器池)
創(chuàng)建新的容器類實例時會觸發(fā)堆內存分配,可能會觸發(fā)垃圾回收
List myList = new List();
改為
myList.Clear();
使用對象池技術處理頻繁創(chuàng)建和銷毀的物體
不要頻繁操作(合并探越、截取等)string?類型的數(shù)據(jù)
string?是引用且不可變類型狡赐,每次操作string?類型后,都會重新創(chuàng)建新的string?類型钦幔,可能會觸發(fā)垃圾回收
創(chuàng)建string?時如果需要進行合并操作枕屉,可以使用StringBuilder?類用于輕量級地創(chuàng)建string。
移除所有不需要的Debug.Log?方法調用鲤氢,每個Debug.Log?都至少會創(chuàng)建和銷毀至少一個string搀擂。
需要顯示的string?數(shù)據(jù)西潘,如果需要合并操作,將string?數(shù)據(jù)拆分成不變的部分和變化的部分哨颂,以移除+?操作喷市。
public Text timerText;
void Update()
{
????timerText.text = "Time:" + DateTime.Now.ToString();
}
改為
public?Text headerText, timerText;
void Start()
{
? ? headerText.text = "Time";
}
void Update()
{
? ? timerText = DataTime.Now.ToString();
}
警惕那些返回數(shù)組、容器等的Unity?內置API威恼,因為每次調用他們都會返回一個新的引用品姓,可能引發(fā)垃圾回收,如需頻繁使用箫措,建議獲取一次后暫存下來腹备。
盡量減少裝箱和拆箱操作
創(chuàng)建協(xié)程會造成垃圾,因為Unity?需要為每個協(xié)程創(chuàng)建管理類實例斤蔓。
yield?return 0植酥;
會造成裝箱裝換,改為
yield return null;
可以避免裝箱引起的堆內存分配
while(!isComplete)
{
? ? yield return new WaitForSenconds(1f);
}
改為
WaitForSeconds delay = new WaitForSeconds(1f);
while(!isComplete)
{
? ? yield return delay;
}
可以減少垃圾產生弦牡。
匿名方法是引用類型會產生垃圾友驮,尤其是閉包會顯著增加內存使用和分配。
LINQ?和正則表達式會產生垃圾驾锰,因為其內部操作會昌盛裝箱操作卸留。
避免使用枚舉作為字典的key,因為會產生裝箱操作稻据,必須使用時寇窑,實現(xiàn)IEqualityComparer?接口并將其實例作為字典的比較器渡冻。
public?class MyEnumComparer : IEqualityComparer<MyEnum>
{
? ? public bool Equals(MyEnum x, MyEnum y)
????{
? ? ? ? return x==y;
????}
? ? public int GetHashCode(MyEnum x)
????{
? ? ? ? return (int)x;
????}
}