模擬經營遊戲《Project Hospital》的優化經驗

遊資網發表於2019-07-18
本文將由《Project Hospital》的開發者Jan Benes分享該專案的優化經驗。

由獨立工作室Oxymoron Games使用Unity打造的《Project Hospital》(中文名:《醫院計劃》)是一款模擬經營類遊戲。它具備該型別遊戲的特點,包括:由玩家建立的動態場景,大量的活動角色和物體以及可擴充套件的UI系統。

讓《Project Hospital》在不同硬體上執行需要不少努力,而且這也是“積小流以成江海”的典型效能優化案例,正是我們處理了許多小步驟,解決了大量具體問題,花費很多時間進行效能分析,才有最後效能卓越的提升。

模擬經營遊戲《Project Hospital》的優化經驗

效能目標

在早期開發階段中,我們為將要支援的大型場景設定了主要效能目標以及硬體要求。

我們的目標是至少在一個螢幕上同時顯示100個活動的動態角色。《Project Hospital》共有300個活動角色,大小約為100 x 100的瓦片地圖,最多有4個樓層。

我們希望遊戲以合適的幀率執行在1080P的畫面中,即使在整合顯示卡上也有這樣的效果。由於CPU是此專案標的主要影響因素,所以這種效果並不難實現。隨著醫院的擴大,整合顯示卡在2560 x 1440的高解析度下才開始比較吃力。

為了實現簡單的模組支援,遊戲中的大部分資料都是開放的。這意味著和打包檔案相比會犧牲一些效能,但這不會造成太大影響,只會稍微延長載入時間。

模擬經營遊戲《Project Hospital》的優化經驗


圖形

由於《Project Hospital》是一款典型的2D等距遊戲,所有內容都是從後向前渲染。在Unity中,這種渲染方式通過設定圖形物件上正確的Z值或攝像機距離來表現。

在可能的情況下,不相互互動的物件會組織到不同圖層,例如:地板和物體及角色是相互獨立的。

等距渲染場景中的所有幾何體都是使用C#程式碼動態建立的,因此對於圖形效能而言,幾何體重構的頻率是重要部分之一,另一個重要部分是Draw Call的數量。

模擬經營遊戲《Project Hospital》的優化經驗

Draw Call

無論物件的簡單程度如何,一幀中繪製的獨立物件數量都是主要的限制,特別是在低端的硬體上,而Unity本身也有額外的開銷。最好的解決方案是將更多圖形物件批處理到單個Draw Call中。

這樣得到一些有趣的結果,例如:由於支援批處理的物件是和攝像機有相同距離的物件,因此其它圖形會得到正確的渲染。

模擬經營遊戲《Project Hospital》的優化經驗

下面列舉幾個數字:在96 x 96的貼圖上,我們理論上可以放9216個物件,也就是要進行9216次Draw Call,在批處理後,Draw Call減少為192次。

在實際環境下,情況會變得更加複雜,因為只有具有相同的紋理的物件才可以進行批處理,所以得到結果的優化程度不那麼高,然而這種方法依舊是很有效的。

模擬經營遊戲《Project Hospital》的優化經驗

大多數批處理通過手工完成,從而對結果進行控制。我們也使用了Unity的動態批處理作為後備方案,但這種方案是一把雙刃劍。

Unity的動態批處理確實可以幫助減少Draw Call的數量,但是每幀都有額外效能開銷,在某些情況下,效能開銷會難以預測。

例如:與攝像機距離相同的二個重疊精靈會在不同幀以不同順序進行渲染,這會造成精靈閃爍現象,手工進行的批處理則不會出現這種現象。

多樓層建築

允許玩家構建多樓層建築會增加很多複雜度,但是對效能有所幫助。

只有在活動樓層和戶外環境的角色和物體需要動畫和渲染,醫院中活動樓層之下或之上的內容都可以隱藏起來。


模擬經營遊戲《Project Hospital》的優化經驗


著色器

《Project Hospital》使用相對簡單的自定義著色器,利用了顏色替換等多個技巧。

例如:通過在著色器程式碼中使用條件,角色著色器可以使用最多5種顏色替代。它的效能開銷較大,但因為角色很少佔據螢幕的大量空間,所以這不是我們需要擔心的問題。

我們也學會了如何避免設定著色器引數,轉而使用頂點顏色。

紋理質量

有趣的是,我們沒有在《Project Hospital》中使用任何紋理壓縮。因為如果對遊戲中存在向量風格的圖形進行壓縮,有一些紋理會看起來非常糟糕。

為了節省系統上的GPU記憶體,使它小於1GB,除了使用者介面的紋理外,我們會自動減小遊戲內紋理大小為一半的解析度,我們可以將選項設定為“texture quality:low”(紋理質量:低)。UI紋理會保持原始解析度。

模擬經營遊戲《Project Hospital》的優化經驗


多執行緒處理

雖然Unity指令碼邏輯基本上是單執行緒的,但我們可以選擇使用C#程式碼執行多執行緒。對於遊戲邏輯而言,這可能不是一種合理的方法,但是有些非時間相關的任務可以受益於以Job System形式在各個單獨的執行緒上執行。

在這款遊戲中,我們把多執行緒用於二個功能:

  • 尋路作業,特別是在大型地圖上,由於糟糕的佈局可能需要數百毫秒處理,因此它是從主執行緒移除的理想選擇。並行作業的數量會考慮到機器上硬體執行緒的數量。
  • 光照貼圖也會在單獨的執行緒上更新,但每次僅更新一個樓層,它不是一個重要的系統,房間中的自動燈光會以較慢的更新速度淡出。


動畫

在開發階段早期,我們就決定使用2D骨架動畫系統。在考慮當時不同的動畫軟體後,我們最後修改和使用了幾年前開發的簡單系統,使它符合《Project Hospital》的特別用例,你可以把它看作直接支援建立角色變體的簡單樣條曲線。

類似於樣條曲線,該工具系統使用C#執行時,顯然這比原生程式碼的效能開銷更大,因此我們在開發期間進行了多輪優化。幸運的是繫結非常簡單,每個角色僅有20個骨骼。

最重要的部分是在訪問單獨骨骼的Transform時,從地圖查詢切換為簡單陣列索引的過程。

模擬經營遊戲《Project Hospital》的優化經驗

除了不使攝像機檢視外的角色有動態效果,另一個和動畫相關的技巧是讓主UI視窗背後的角色也不需要有動態效果,然而切換為半透明UI後,我們無法在最終版本使用這個技巧。

快取

在可能的情況下,我們會嘗試僅在有影響數值的改動時,執行有特別要求的計算。

最好例子大概是房間和電梯,在角色放置電梯或建起牆面時,我們會執行樓層填充演算法,它會標記可以訪問電梯和房間的瓦片。這樣會加快尋路過程,向玩家展示哪些房間目前無法訪問。

分散和延遲的更新

在一些情況下,我們會偶爾執行一次特定更新,我們使用了下面的方法。

一些更新每幀只可以在一部分角色執行,因此會出現這樣的情況:一半病人的行為指令碼僅在奇數幀更新,另一半會在偶數幀更新,而動畫和移動會流暢的執行。

在特別狀態下,有時角色閒置時會呼叫開銷較大的程式碼,例如:員工檢查過程需要填充和尋找可用裝置,該過程僅在特定時期完成,例如:每秒進行一次。

效能開銷最大且最常見的呼叫是每個病人可以進行哪些檢查的估算。我們需要評估很多因素,例如:哪個部門的員工目前處於忙碌狀態,哪些裝置目前已被預訂。

因為他們的指定醫生和談話技能也有影響,所以這些資訊對所有病人來說並不常見。遊戲中可能有多個可用檢查需要執行,因此更新只在每幀進行幾次,而且會持續到下一幀。

模擬經營遊戲《Project Hospital》的優化經驗

其它經驗

優化具有大量不同互動部分的遊戲是一個持續的過程,使用Unity的效能分析器能夠解決效能影響較大的部分問題。遊戲按照我們原來的目標執行,玩家可以給遊戲新增模組,使遊戲內容超過原始角色限制。

相對於我參與過的其他AAA級遊戲專案,《Project Hospital》具有最複雜的遊戲邏輯,因此很多問題是和專案相關的。

最後要建議大家:不管是什麼專案,一定根據遊戲的複雜性預留出足夠的時間進行優化。

模擬經營遊戲《Project Hospital》的優化經驗

結語

《Project Hospital》的優化經驗為大家介紹到這裡,更多Unity專案的優化經驗分享,盡在Unity Connect平臺(Connect.unity.com)。

來源:UNITY官方平臺

相關文章