遊戲開發中常見細節優化實踐

不二發表於2020-05-21

文章分兩部分
一是自己最近使用Perfdog也發現了一些常用的技巧,現在安利給大家一下
二是一些在遊戲開發中常見到的優化技巧

一PerfDog使用技巧篇

1.雙擊批註
基本使用不提,在整個測試過程我們經常會遇到很多場景,每個場景的效能資料一般都會各有不同,所以為了在報告中看的更明顯,我們可以增加批註,比如標記關鍵節點等。
==滑鼠左鍵雙加新增批註==
批註及標定(滑鼠左鍵雙擊,則批註。左鍵雙擊已生成的批註,則取消。滑鼠左鍵單擊,則標定):
在這裡插入圖片描述
2.場景新增標籤

為了更加明顯區分我們的 測試場景,我們可以對階段時間增加標籤,
==通過標籤按鈕給效能資料打標籤,滑鼠左鍵雙擊顏色區域可修改對應區域標籤名==
在這裡插入圖片描述
比如我的標記完了就是這樣
在這裡插入圖片描述
現在我們來看一下報告的樣式
在這裡插入圖片描述
這樣對於場景區分是不是明顯多了。
3.儲存具體資料資訊
有時我們需要具體的記錄下每一幀執行的具體資料,我們有兩種辦法:
1.滑鼠左鍵框選後右鍵儲存
在這裡插入圖片描述
2.是測試完後上傳資料到雲端時選擇同時儲存到本地
這樣就可以把資料儲存到具體的Xlsx裡,預設在效能狗的data/測試的應用包名/測試時間資料夾。
在這裡插入圖片描述
4.多程式測試
iOS平臺,APP多程式分為APP Extension和系統XPC Server。
比如:某電競直播軟體用到APP Extension擴充套件程式(擴充套件程式名LABroadcastUpload)。當然也可能用到系統XPC Server服務程式,如一般web瀏覽器會用到webkit。
Android平臺,一般大型APP,比如遊戲有時候是多程式協作執行(微信小遊戲,微視等APP及王者榮耀等遊戲多子程式),可選擇目標子程式進行鍼對性測試。預設是主程式;
==子程式程式名高亮顯示,表示當前子程式處於頂層==
在這裡插入圖片描述
5.資料對比
首先在web後臺上選擇所在比對的資料
在這裡插入圖片描述選擇完畢後開啟對比介面就可以對比歷史測試用例的資料啦,FPS,cpu,記憶體,GPU,網路,耗電量啦都可以對比,十分便捷。
在這裡插入圖片描述
更詳細的的使用說明可以在這裡檢視使用說明

下面簡單分享一下在遊戲開發中一些細節優化

二遊戲開發中一些細節優化

1.能整不浮,能乘不除

看一下程式碼

float f_a = 66666888f;
float f_b = f_a + 0.01f;
Debug.Log(f_a);//1.677722E+07
Debug.Log(f_b);//1.677722E+07
Debug.Log("_______________****_______________");
double d_a = 9007199254740992f;
double d_b = d_a + 0.01f;
Debug.Log(d_a);//9.00719925474099E+15
Debug.Log(d_b);//9.00719925474099E+15

輸出結果;
在這裡插入圖片描述
實際上
一是二者儲存結構不同;
二就是就是浮點數精度的問題;

float把 32位分成了3部分,1位(符號位)8位(指數位)23位(有效數字)那麼 1+ 8 + 23 等於32吧,所以float的32位是這麼來的。23位有效數字就表示float真正能存的精度,23位小數部分是反被儲存的部分,所以它是有24位儲存的,224(2的24次方)=16777216
如果程式中有一個float的數值運算後的小數部分,如果超過16777216.xxx後運算的結果就會不準確;
double則是1位符號位+11位指數+52位有效數字 = 64位
有效數253(2的53次方)=9007199254740992,超過9007199254740992.xxx後運算的結果就會不準確;

浮點數計算結果不同的CPU計算出來可能是不一致的,像幀同步等就基本告別浮點數了,儘量用整型代替浮點型,實在需要可以用定點數,但也要注意是否存在定點數轉回浮點數的現象;
其實還可以巧用位操作符,
位操作符比傳統乘除效率要高,適合大量計算時使用
例如:
int a = 100 >> 1 相當於除2取整 結果為 50
int a = 100 << 2 相當於乘4取整 結果為 400
不過不必要過分追求位運算,在許多比較老的微處理器上, 位運算比加減運算略快, 通常位運算比乘除法運算要快很多,但現代架構中, 情況並非如此:位運算的運算速度通常與加法運算相同(仍然快於乘法運算)。在現代處理機架構中編譯器一般會自動優化為移位運算的。還有很多晶片已經內建了硬體乘法器(乘法器的模型就是基於“移位和相加”的演算法);

2.MonoBehaviour

1.MonoBehaviour中,如果沒有相應的事件要處理,要刪除預設的空函式;
Update、FixedUpdate、LateUpdate中,如果沒必要每幀的邏輯,可以降低頻率

Void Update(){
ifTime.frameCount%6==0{
//要執行的功能函式
}
}

2.如果間隔更長,沒必要每幀的邏輯,使用週期性的協程更妥當,例如使用InvokeRepeating函式:

InvokeRepeating();

3.Gameobject不可見時,設定 enabled = false 時,update 就會停止呼叫。

  1. FindGameObjectWithTag或者GetComponent<>等查詢操作放在Star()先引用,不要放在Update或者FixedUpdate裡;
  2. 協程使用 yield return new WaitForSeconds() 將會每幀導致 大概21Byte GC,而yield return null 會產生 大概9 Byte GC;
    我們可以簡單地通過複用一個全域性的 WaitForEndOfFrame 物件來優化掉這個開銷:

    static WaitForEndOfFrame EndOfTest = new WaitForEndOfFrame();
    // Start is called before the first frame update
    void Start()
    {
    StartCoroutine(Test());
    }
    IEnumerator Test()
    {
    yield return null;

    while (true)
    {
    //原本是yield return new WaitForEndOfFrame();
    yield return EndOfTest;

    }
    }

    實際上實際上,所有繼承自YieldInstruction 的用於掛起協程的指令型別,都可以使用全域性快取來避免不必要的 GC 負擔。常見的有:
    WaitForSeconds
    WaitForFixedUpdate
    WaitForEndOfFrame
    可以自己新建檔案xxx.cs這個檔案裡,集中地建立了上面這些型別的靜態物件,使用時可以直接這樣:
    yield return xxx.GetWaitForSeconds(1.0f);

    6.使用內建資料代替新建資料
    例如: 使用 Vector3.up 而不是 new Vector(0, 0, 0);
    在這裡插入圖片描述
    7.快取元件,快取Gameobject,呼叫 GetComponent 函式會有查詢開銷,用變數掛載到指令碼使用,降低開銷(GetComponent時如果獲取到空的元件也會產生GC)。在指令碼掛載物件引用,可減少查詢。
    8.查詢物件標籤用if (go.CompareTag (“xxx”)來代替if (go.tag == “xxx”)。GameObject.tag會在內部迴圈呼叫物件分配的標籤屬性,並分配額外的記憶體,並且效率也更低。
    9.使用委託機制代替SendMessage,BroadcastMessage,SendMessageUpwards這三個函式。
    因為它們的實現是一種偽監聽者模式,利用的是反射機制,效能非常低。具體的效能大概是委託的十分之一。

3.Instantiate例項化操作技巧

需要頻繁例項化的gameobject比如子彈,需要放入物件池,可以大量減少例項化,銷燬的開銷。將部分耗時資源進行預載。

4.複雜的UI介面和帶有動畫元件的GameObject不要頻繁切換Active/Deactive

設定Active/Deactive複雜物件所用的耗時會比較大,這裡可以使用一個小技巧,可以將需要Deactive的操作變成將GameObject移動到比較遠的,攝像機之外。然後將GameObject上面的全部Component的enabled屬性設定成false。Active時再重新設定回來

5.使用for或者while代替foreach

foreach每次呼叫會產生有40Byte左右的GC資料,產生 GC 的根本原因是使用了 using 語句。(GetEnumerator()返回值型別,在
using 中裝箱了)

6.合理使用陣列,ArrayList,List

陣列:記憶體中是連續儲存的,索引速度非常快,賦值與修改元素也很簡單。但不利於動態擴充套件以及移動。因為陣列的缺點,就產生了 ArrayList。
ArrayList:使用該類時必須進行引用,同時繼承了 IList 介面,提供了資料儲存和檢索,ArrayList物件的大小動態伸縮,支援不同型別的結點。
ArrayList雖然很完美,但結點型別是 Object,故不是型別安全的,也可能發生裝箱和拆箱操作,帶來很大的效能耗損。
List是泛型介面,規避了 ArrayList的兩個問題。

相關文章