透過這款休閒駕駛遊戲的無限程序化生成景觀,發掘 WebGL 的潛力。
Slow Roads 是一款休閒駕駛遊戲,重點是無限產生的程序化風景,全部以 WebGL 應用程式形式在瀏覽器中代管。對許多人來說,這種密集的體驗在瀏覽器的有限環境中似乎不太合適,而修正這種態度正是我參與這個專案的目標之一。本文將說明我如何克服成效障礙,並凸顯網路中 3D 技術經常被忽略的潛力,以便達成目標。
在瀏覽器中進行 3D 開發
推出「慢速路段」後,我發現意見回饋中經常出現以下留言:「我不知道瀏覽器可以做到這一點」。如果您也有這種想法,那麼您絕非少數,根據 2022 年 JS 現況調查,約 80% 的開發人員尚未嘗試 WebGL。我認為,錯失這麼多潛在商機實在可惜,尤其是在瀏覽器遊戲方面。我希望透過 Slow Roads 讓 WebGL 更受矚目,也許還能減少開發人員對「高效能 JavaScript 遊戲引擎」一詞的抗拒。
對許多人來說,WebGL 可能看起來神秘且複雜,但近年來,其開發生態系統已大幅成熟,成為功能強大且方便使用的工具和程式庫。前端開發人員現在可以更輕鬆地將 3D UX 融入工作中,即使沒有電腦圖形相關經驗也沒問題。領先的 WebGL 程式庫 Three.js 是許多擴充功能的基礎,包括將 3D 元件帶入 React 架構的 react-three-fiber。目前也有許多完整的網頁版遊戲編輯器,例如 Babylon.js 或 PlayCanvas,可提供熟悉的介面和整合式工具鍊。
儘管這些程式庫非常實用,但野心勃勃的專案最終仍會受到技術限制的限制。對於以瀏覽器為基礎的遊戲概念,懷疑論者可能會強調 JavaScript 是單執行緒且受限於資源。不過,克服這些限制後,您就能發掘隱藏的價值:沒有其他平台能提供瀏覽器提供的即時可用性和大量相容性。使用者只要在任何支援瀏覽器的系統中按一下,即可開始播放,無須安裝應用程式或登入服務。更不用說,開發人員還能享有優雅便利的功能,包括可用於建構 UI 或處理多人遊戲模式網路連線的強大前端架構。在我看來,這些價值讓瀏覽器成為玩家和開發人員都喜愛的絕佳平台。正如 Slow Roads 所示,技術限制通常可歸因於設計問題。
在慢速道路上順暢行駛
由於慢速道路的核心元素涉及高速動作和高成本的場景生成,因此我必須做出每項設計決策,以確保順暢的效能。我的主要策略是從簡化版的遊戲玩法設計開始,讓引擎架構中可採用內容快捷方式。缺點是,為了追求極簡,您可能會犧牲一些實用功能,但這會產生專屬的超級最佳化系統,可在不同瀏覽器和裝置上順利播放。
以下是讓慢速道路保持精簡的關鍵元件。
根據遊戲內容調整環境引擎
由於環境產生引擎是遊戲的重要元件,因此成本自然較高,因此記憶體和運算預算中,這項元件所占的比例也較高。這裡使用的技巧是在一段時間內排程並分散大量運算作業,以免因效能突然提升而中斷影格速率。
環境是由幾何圖塊組成,其大小和解析度 (分類為「細節等級」或 LoD) 會因相機鏡頭的距離而異。在具有自由漫遊相機的一般遊戲中,必須不斷載入及卸載不同的 LoD,才能詳細顯示玩家選擇前往的任何位置周遭環境。這項作業可能會耗費大量資源且浪費時間,尤其是當環境本身是動態產生時。幸好,由於使用者應留在道路上的內容情境預期,這項慣例可在慢速道路中完全顛覆。相反地,您可以為直接沿著路線兩側的狹窄走廊保留高細節幾何圖形。

道路本身的中線會在玩家抵達前很久就產生,因此可準確預測需要環境細節的時間和位置。這會產生精簡系統,可主動排定昂貴的工作,在每個時間點只產生所需的最低數量,不會浪費心力處理不會看到的細節。這項技巧之所以可行,是因為道路是單一、不分支的路徑,這也是在遊戲過程中做出權衡,以便採用架構捷徑的絕佳例子。

對物理定律的挑剔
除了環境引擎的運算需求之外,物理模擬也是另一個需要大量運算的作業。Slow Roads 使用自訂的簡易物理引擎,可利用所有可用的捷徑。
這裡的主要節省方式,就是避免一開始模擬太多物件,也就是說,您可以透過減少動態碰撞和可破壞物件等項目,讓遊戲環境保持最小化。假設車輛會停留在道路上,表示可以合理忽略與非道路物體的碰撞。此外,將道路編碼為稀疏的中線,可讓您輕鬆偵測道路表面和護欄的碰撞情形,而且所有偵測動作都會以距離檢查道路中心為依據。因此,越野駕駛的成本會提高,但這也是另一個適合遊戲情境的合理取捨例子。
管理記憶體用量
雖然 JavaScript 會進行垃圾收集,但它也是另一個受瀏覽器限制的資源,因此請務必謹慎管理記憶體。這很容易被忽略,但即使在遊戲迴圈中宣告少量新記憶體,在以 60Hz 執行時,也可能會演變成重大問題。除了在使用者可能執行多工作階段的情況下耗用使用者的資源,大量垃圾收集作業可能需要花費數個影格才能完成,導致明顯的卡頓現象。為避免這種情況,您可以在初始化時在類別變數中預先分配迴圈記憶體,並在每個影格中回收。

另外,也請務必以經濟的方式管理較重的資料結構,例如幾何圖形及其相關資料緩衝區。在 Slow Roads 等無限產生的遊戲中,大部分的幾何圖形都會存在於某種跑步機上 - 一旦舊圖形落後,其資料結構便可儲存,並再次回收用於下一個世界區塊,這種設計模式稱為物件池。
這些做法可優先執行精簡的程式碼,但犧牲了程式碼的簡潔性。在高效能情境中,請務必留意便利性功能有時會向用戶端借用資源,以利開發人員。舉例來說,Object.keys()
或 Array.map()
這類方法非常實用,但很容易忽略的是,每個方法都會為其傳回值建立新的陣列。瞭解這類黑盒的內部運作方式,有助於強化程式碼並避免發生隱藏的效能衝擊。
使用程序化產生的素材資源縮短載入時間
雖然遊戲開發人員應將執行階段效能視為首要考量,但初始網頁載入時間的一般原則仍適用。使用者在明知內容較大時,可能會比較寬容,但載入時間過長仍會影響使用者體驗,甚至影響使用者留存率。遊戲通常需要紋理、音效和 3D 模型等大型資產,因此請務必仔細壓縮這些資產,盡可能減少細節。
或者,您也可以在用戶端上以程序化方式產生素材資源,避免長時間的傳輸作業。這對連線速度較慢的使用者來說是一大福音,也讓開發人員能更直接地控制遊戲的組成方式,不僅是初始載入步驟,也包括針對不同品質設定調整細節等級。
慢速道路中的幾何圖形大多是程序化產生的,且簡單明瞭,自訂著色器則結合多種紋理,以呈現細節。缺點是這些紋理可能會是重型素材資源,但這裡還有其他節省空間的機會,例如隨機紋理處理方法,可從小型來源紋理取得更多細節。在極端情況下,您也可以使用 texgen.js 等工具,在用戶端上完全產生紋理。音訊也是如此,Web Audio API 可透過音訊節點產生音訊。
有了程序化素材資源的優勢,產生初始環境的平均時間只需 3.2 秒。為了充分利用較小的預先下載大小,系統會使用簡單的啟動畫面向新訪客問候,並將耗時的場景初始化作業延後至按下確認按鈕後再執行。這也是彈出工作階段的方便緩衝區,可盡量減少動態載入資產的轉移浪費。
採用敏捷方法進行後期最佳化
我一直認為 Slow Roads 的程式碼集屬於實驗性質,因此採用了極為敏捷的開發方法。當您使用複雜且快速演變的系統架構時,可能很難預測可能發生重要瓶頸的時間點。重點應放在快速實作所需功能,而非確保功能的完整性,然後再反向操作,針對實際需要的系統進行最佳化。Chrome 開發人員工具中的效能分析器對這個步驟非常有用,它協助我診斷遊戲早期版本的一些主要問題。您的開發人員時間非常寶貴,因此請務必不要花時間討論可能不重要或重複的問題。
監控使用者體驗
在實作所有這些技巧時,請務必確保遊戲在實際環境中能正常運作。任何遊戲開發作業都必須考量各種硬體功能,但網頁遊戲可以同時鎖定頂級電腦和十年前的行動裝置等更廣泛的目標。最簡單的解決方法,就是提供設定,以便調整程式碼庫中最可能出現瓶頸的部分,包括剖析器顯示的 GPU 和 CPU 密集型工作。
不過,在您自己的電腦上進行剖析只能涵蓋部分內容,因此,透過某種方式與使用者建立回饋循環相當重要。針對「慢車道」,我會執行簡單的分析,以便針對成效和螢幕解析度等背景因素製作報表。這些數據分析會透過 socket.io 傳送至基本 Node 後端,並連同使用者透過遊戲內表單提交的任何書面意見回饋。在初期,這些分析工具就發現許多重要問題,只要簡單調整使用者體驗就能解決,例如在偵測到 FPS 持續偏低時醒目顯示設定選單,或是在效能特別差時警告使用者可能需要啟用硬體加速功能。
前方道路壅塞
即使採取所有這些措施,仍有相當一部分的玩家需要以較低的設定進行遊戲,尤其是使用缺少 GPU 的輕量裝置的玩家。雖然可用的品質設定範圍可帶來相當均勻的效能分布,但只有 52% 的玩家達到 55 FPS 以上。

幸運的是,您仍有許多機會可以節省效能成本。除了新增更多算繪技巧來降低 GPU 需求,我希望在近期內嘗試使用網路 worker 並行處理環境產生作業,並最終可能需要將 WASM 或 WebGPU 整合至程式碼庫。我能釋出的任何空間都會讓環境變得更豐富、多元,這將是專案後續持續努力的目標。
在休閒項目方面,Slow Roads 是一種令人滿足的方式,可展示瀏覽器遊戲的驚人複雜度、效能和人氣。如果我成功引起您對 WebGL 的興趣,請注意,Slow Roads 只是其完整功能的淺層範例。我強烈建議讀者探索 Three.js 展示內容,如果您對網頁遊戲開發有興趣,歡迎加入 webgamedev.com 社群。