基礎遊戲編程最佳化 — 公有成員不計算原則 Public Member Non-Calculate Principle PMNCP

 引言

    由於一個遊戲開發過程的高度複雜性,遊戲開發通常會以物件導向的理念為基礎進行開發,即便是最近流行的ECS設計,雖然是以數據導向為理念,但當中的Entity和Component仍然可以見到一些許物件導向的色彩。而在物件導向的遊戲開發裡,有一個頗為簡單,但不少初學者未必會留意到的效能最佳化技巧,那便是「儘可能」不在公用成員( Public Member )中進行過多的計算,我把它稱之為公有成員不計算原則(這名字我隨便編的),其實簡單來說就是不要重覆計算。

內文

    這是一個十分好理解的做法,公用成員不單止不要進行過多的計算,甚至最好不要進行任何運算,儘可能把公有成員維持在O(1),也就是單一步驟便能完成。而在實務上的表現是在 Public Method 中只保留一句return var;便可,如果是C#的話,則可以直接使用public get ; private set;的變數,而該公用成員原有的計算過程都儘可能在該物件在每一幀的循例工作內完成。

    而這做法的好處在於可以避免不小心的重覆了不必要的相同計算。有不少函數的運算其實是過程相同、結果相同的,根本就不需要重覆運算相同的東西,但為什麼我強調是公用函數,原因在於正常情況下,私有函數被引用的次數是更加可控的,畢竟私有函數只能在類別本身內部和特定的友好類別被調用,因此,大概一、兩眼就能看出來被引用的次數,對效能的影響也會有所受限制,但如果是在外部被引用便會是另一回事,當專案的體量越來越大,在外部被引用的次數也會不知覺的上升,尤其是那些會被重覆實例化的類別更是會再加大。

    這裡作一個簡單的例子。下圖有個player和數個enemy,當中player有一個get_global_position的公用函數,該函數的目的在於獲取player的絕對座標。

    下圖是兩個player的寫法,player1是錯誤示範,當enemy在引用get_global_position時,player1會重新把local_position計算成global_position再回傳,如果enemy只有幾個,影響或許不大,但當enemy有幾百到幾千個,那重覆計算的local_position to global_position便會開始影響遊戲的效能表現。

    而player2是正確示範,在每一個幀,player2都會重新計算一次global_position並把結果存至記憶體,而當enemy引用get_global_position時,player2只會回傳記憶體內的global_position,而不會重新計算,花費在計算上的步驟便會減少,就這樣。








熱門文章