Astra 現在很擅長把我腦中的想法轉化為遊戲玩法與美術方向。我一直在 Codex 中使用它打造 Void Explorer,這是一款太空探索遊戲,讓你能從遙遠的恆星一路飛到外星地表。
Void Explorer 的遊戲精華剪輯。
遊戲中有 2,048 個恆星系統,以及超過 10,000 顆以程序生成的行星,其中也有和地球一樣大的世界。每顆看得見的恆星都是這個宇宙的一部分,也都能設為目標。你可以選擇遠方的一個光點,然後前往那裡。
你可以從太空接近一顆行星,穿越大氣層,持續下降,直到沿著海岸線上空飛行。你可以降落、走出太空船、四處散步,再次起飛。要讓整段旅程順利運作,就得一併解決尺度、地形、操控與算繪的問題。
我收錄了開發過程中的幾則提示詞,並調整篇幅與措辭,讓內容更清楚。這些例子呈現了我如何描述需求,以及技術工作如何從這些需求展開。
從體驗出發
我最初的需求說明描述了玩家應該能做哪些事:
我看得到的地方都應該到得了。保持真實距離,再透過尺度與速度設計讓航行可行。我想從太空飛入行星的大氣層,一路下降到地面。行星可以和地球一樣大,所以我們需要程序生成的地形和分塊算繪器。
這些需求給了 Astra 具體的限制。恆星不能只是畫在背景上的光點,行星也不能在我靠近時變成另一個獨立關卡。航行必須跨越真實距離,再透過虛構的脈衝航行與超空間引擎,讓這些距離在遊戲中切實可行。
在投入大量遊戲開發工作之前,我也先用圖像生成來探索視覺風格。第一批概念圖太寫實,接下來的方向又太簡單。這是我其中一次提出的修正:
現在有點太簡略了。我們需要折衷一下:更好的配色、霓虹光線,以及更強烈的對比,營造出深邃太空的感覺。讓我看看高速飛行時的樣子,太空船周圍要有星星與塵埃。
等到圖像讓我滿意後,我請 Astra 把它們存下來,將這個方向整理成遊戲的視覺參考。我們為軌道飛行、高速航行、進入大氣層與降落都訂出了具體目標。有了這些圖像,就更容易判斷下一個遊戲版本是否達到預期。

生成的概念圖,確立了遊戲的配色與視覺方向。
讓 Astra 提出架構
我設定限制,Astra 則提出實作方式。應用程式使用 TypeScript 與 Vite,並以 Three.js 進行算繪。如此一來,程式碼就能直接控制網格、材質、光照與程序生成的幾何形狀,而這些正是這款遊戲大部分視覺工作的核心。
第一個可遊玩的版本使用 WebGL2 算繪器。後來,我問 Three.js 是否限制了視覺效果。Astra 建議保留模擬系統,將算繪器改用 Three.js 的 WebGPU 架構。我們保留了宇宙、導航與地形系統,只更換算繪層。
算繪器使用 Three.js 的節點材質與 Three.js Shading Language (TSL),在程式碼中定義大氣、水體與光照效果。
地形生成在 Web Workers 中執行,因此準備幾何資料時,不會占用處理輸入與繪圖的執行緒。Vitest 負責測試生成結果的可重現性、座標運算及地形契約等項目,Playwright 則在瀏覽器中測試遊戲。這讓 Astra 能檢查底層系統,而我則持續評估遊戲玩起來的感受。
整個過程始終以既定的美術方向為目標。行星周圍的大氣、行星環的輝光、恆星的彩色光線,以及 Neon Phosphor 效果,共同構成了整體視覺風格。這個濾鏡增添了復古顯示器的質感,同時讓導航資訊保持清晰可讀。

啟用 Neon Phosphor 效果的遊戲螢幕擷取畫面。
讓 Astra 能夠檢查與試玩
我持續親自試玩遊戲,但 Astra 也需要有其他方式來調查問題,不能只靠閱讀我的描述。遊戲提供了一個小型 JavaScript 介面 window.__VOID_EXPLORER__,讓瀏覽器測試能呼叫它來檢查目前狀態:我正在接近哪個天體、飛行模式、地形是否就緒,以及目前使用的攝影機。這個介面也提供算繪與串流計數器,包括繪製呼叫次數、三角形數量、佇列中的地形作業,以及已緩衝的地形資料。
遊戲為軌道飛行、脈衝航行、大氣層內下降與海岸降落設有具名測試場景。這讓 Astra 能直接回到適合測試的起點,不必每次都飛越整個宇宙。Playwright 可以載入場景、等待地形就緒、擷取畫面,並檢查畫面背後的狀態。另外還有完整旅程測試,會使用實際的操控方式,在遊戲執行時記錄位置與狀態變化,因此設定好降落場景並不能取代對降落過程本身的測試。
這些測試涵蓋降落、離船、步行、儲存與重新載入、登船及起飛。它們能找出光看螢幕擷取畫面無法發現的問題,例如碰撞表面尚未就緒,或太空船在轉換過程中突然跳到另一個位置。
我也可以請 Astra 檢查我正在遊玩的瀏覽器分頁。當我問 Aurelia 的雲層是否仍然可見時,它擷取了我當時的視野,並讀取畫面上的飛行資訊。這些都是針對特定時刻的檢查;Astra 並沒有持續觀看我飛行過程中的每一個影格。
整個循環是重現問題、檢查螢幕擷取畫面與狀態、追查相關程式碼、進行修改,再重新檢查。如果再開發另一款遊戲,我會提早建立這些工具:幾個可重複測試的場景、實用的狀態與效能計數器,以及主要互動的瀏覽器測試。這些工具讓 Astra 能自行調查問題並測試修改,而我則持續評估視覺呈現與操控手感。
以多種尺度表示宇宙
有數千顆行星,不代表要載入數千個精細網格。一顆行星最初只是一組描述資料:半徑、軌道、大氣與種子值。種子值讓地形能夠重複生成。遊戲會生成我正在接近的區域周圍的幾何形狀,等我再次回來時,也能生成相同的地景。
座標也需要同樣謹慎處理。以光年計算的距離,與站在太空船旁的一個人,兩者的尺度差異極大。如果把那些龐大的位置數值直接送進 GPU,就會失去接近地面時所需的精確度。
Astra 將實際空間位置與繪圖使用的位置分開。宇宙使用以大整數標示的格網單元,搭配數值較小的局部偏移量。算繪前,遊戲會減去觀察者的位置,讓攝影機保持在原點,附近物體的座標值也維持在較小的範圍。顯示的距離與相對大小則維持一致。
移動中的各個物體也需要使用一致的時間。恆星、行星與衛星沿著解析式軌道運行,算繪器會以和觀察者相同的模擬時間計算它們的位置。停泊的太空船會隨著行星一起自轉。恆星的位置與顏色會用於光照計算,因此雙星日落中的兩顆太陽,就是我在軌道上看到的那兩顆恆星。
讓下降過程連續銜接
在 Void Explorer 中從太空飛向一顆衛星的表面。
從太空到地面的轉換經過了許多次反覆調整。有一則提示詞,就是在我以小角度接近行星時提出的:
當我幾乎平行於行星表面接近時,速度仍可能太快,而且在地形載入時,行星幾乎整顆消失。我們需要一起修正速度曲線與地形串流載入。能不能利用大氣效果,讓遠距離 LOD 與精細地形之間的轉換更柔和?接近的過程中,行星絕對不應該消失。
現在,飛行控制器會根據高度,以及由行星半徑和重力估算的軌道速度,為小角度接近的情況套用獨立的速度上限。更接近地面時,它也會取樣前方地形,據此調整煞車。
遊戲使用多個細節層級,也就是 LOD。從遠處看,行星是一個算繪成本低、帶有稜面的球體,稱為替代模型。隨著它占據的畫面面積增加,算繪器會加入更多細節。接近表面時,就會啟用由立方體六個面投影到球體上所構成的地形。每個面都能反覆分割成四個更小的正方形,形成四元樹。
這讓遊戲能細化可見區域與行進方向上的地形,不必以適合步行的解析度生成整個地球大小的表面。靠近地面時,局部區塊會在太空船與玩家周圍提供更精細的幾何形狀。
這些不同的呈現方式,全都取樣自同一份底層行星資料。一個共用函式會結合大陸、山脊、隕石坑與較小的地勢起伏,提供地景使用的海拔、水體、生態群系及其他訊號。粗略與精細的網格以不同解析度近似這些資料,並採用共用的配色規則,讓行星外觀保持一致。
不同層級之間的交接,和生成本身一樣重要。在六個粗略地形面全部就緒之前,遠處的行星會保持可見。在四個子區塊全部就緒之前,原本的地形區塊也會留在原位。轉換期間,互補的像素遮罩會將每個像素分配給舊表面或新表面,避免在替代模型載入時讓整顆行星變成透明。
大氣效果會隨實際高度改變,雲層則在行星地貌上方移動,幫助軌道視野與地表景觀銜接。算繪器只會在更精細的地形已就緒的區域移除粗略地形。隨著更精細的幾何形狀出現,山巒輪廓仍可能改變。
降落帶來了更嚴格的要求。同一接觸區塊中的可見地面、碰撞三角形與液體危險區域,都必須來自同一代生成資料,並一起就緒。步行使用 Rapier,在一個小型的局部物理世界中運作。太空船的降落邏輯則會另外依據地形,檢查支腳、坡度與淨空。
這裡有個刻意保留的限制:行星使用高度場,每個表面方向只有一個高度值。這適合大尺度地景,但無法提供洞穴、懸垂地形或可破壞的隧道。
量測影格變慢背後的工作量
我的一些回饋同時涉及視覺呈現與效能,因為我在同一次飛行中遇到了這兩類問題:
可以看看我從太空往地面飛行時,行星是如何載入與算繪的嗎?遠處的球體與精細地形看起來不一致,轉換很突兀,尤其是顏色改變的時候。我想了解這些視覺跳變的原因,以及哪些地方能減少載入更多細節所需的工作量,讓下降過程看起來更好,也執行得更順暢。
Astra 使用 Three.js 的算繪器計數器,檢查繪製呼叫、三角形、幾何物件與紋理的數量。一項 Playwright 測試收集了 90 個影格間隔,並連同這些計數回報平均時間與各百分位數時間。針對視覺問題,測試也能比較特定效果啟用與停用時算繪出的像素,例如比較啟用與停用隱藏重疊地面遮罩時的地形。這有助於釐清地面消失的原因,不必只憑一張異常場景的螢幕擷取畫面判斷。
較早的一次太空船改版就說明了這些量測為何有用。將程序生成的太空船換成在 Blender 中製作的第一個 AURORA 模型後,場景的三角形數量增加,繪製呼叫次數卻減少了。Astra 在變更前後,都使用 WebGPU 後端執行相同的 90 影格軌道測試:
| 量測項目 | 變更前 | 變更後 |
|---|---|---|
| 場景繪製呼叫總次數 | 119 | 77 |
| 場景三角形總數 | 48,809 | 61,092 |
| 平均影格間隔 | 251.48 ms | 199.26 ms |
這次比較使用無頭模式的 Chromium,搭配 SwiftShader 軟體算繪,進行時間早於下方四翼飛船的製作。它量測的是該測試環境中的變化,而非我的 GPU 上的影格率。這些工具讓 Astra 取得瀏覽器的時間量測資料、算繪影像和資源數量,但沒有量測個別 GPU 指令或著色器的執行時間。
Astra 調查了載入與下降過程中的運算工作:生成了多少幾何資料、Worker 與算繪器之間傳输了多少資料,以及地形作業還沒派上用場就被丟棄的頻率。
其中一項變更,是依照遠方行星在螢幕上所占的大小來分配細節。占滿畫面的行星需要立即呈現完整的輪廓;小於一個像素的行星則不需要同等的幾何細節。在控制條件的起始場景比較中,修訂後的策略使用了 7,040 個代理模型三角形,原先則是 51,200 個。兩種策略都使用相同的幾何資料生成器,因此能單獨比較載入決策的影響。
另一項改善完全保留了原有的地形三角形數量。索引網格會重複使用相鄰三角形共用的頂點,不必重複儲存其位置、顏色與法線資料。在地形品質設為「高」的測試中,六個地面圖層傳輸的緩衝區總量從約 35 MB 降至 15 MB,同時保留了 241,952 個三角形。地面與景物也拆成了不同作業,讓地面幾何資料可以先準備好,不必等待裝飾物。
地形選擇器也做了不少白工。它可能先請求區塊,接著改變決定、丟棄區塊,再請求替代區塊。Astra 讓這些細化決策更穩定,只有在資源預算足以容納全部四個子圖塊時,才會細化一個圖塊。在 Worker 延遲固定的受控模擬中,下降六秒後再等待三秒讓作業穩定下來,丟棄的作業從 6,074 個減少到 13 個。

在使用兩個 Worker、模擬 Worker 延遲為 100 ms 的受控模擬中, 已排程但遭丟棄的作業從 6,074 個減少到 13 個。 這量測的是地形排程,而非影格率。
光靠 Worker 無法解決所有卡頓。主執行緒上的地形生成備援機制也需要適時讓出執行權。Astra 將生成流程拆成可暫停後繼續的步驟,每個影格分配約 4 ms,讓大型地形作業不必一次執行到底。算繪器也將世界畫面的解析度上限與介面分開控制,讓導航文字在畫質降低時仍保持清晰。
這些地形量測結果顯示,幾何資料、傳輸資料量與遭丟棄的作業都減少了。但這些並不是硬體影格率的基準測試。著色器編譯、GPU 資料上傳、動態表現,以及轉換過程實際看起來如何,仍需要透過瀏覽器試玩來檢查。
我提供的是試玩回饋:哪裡感覺慢、哪裡看起來不對,以及我想改善什麼。Astra 追查程式碼、撰寫可重複執行的地形測試腳本,並記錄量測結果。我核准方向後,它就實作變更,再次執行相同的測試來比較結果。這些調查與實作不需要我逐步指定做法,我則持續審視遊戲的畫面與操作感受。
將概念美術轉為遊戲素材
這個宇宙大多由程式碼生成,包括行星、地形、雲、星環與恆星。玩家飛船則是主要的獨立製作 3D 素材,我採用了不同的製作方式。
在飛船加入遊戲之前,我先透過生成的概念圖和 Blender 算繪圖來審視設計。當機翼與我的構想不符時,我要求提供更實用的參考圖:
從幾個角度生成這艘飛船的清晰視圖。請留意前翼和後翼。我們會用這些圖片重做 3D 模型。我不喜歡目前模型上連在一起、圓弧狀的機翼。
我選了一張具有四片獨立機翼、對稱外形,且翼尖帶有粉紅色燈光的參考圖。接著,我請 Astra 在 Blender 中製作新模型,保留遊戲的美術風格,再將它加入遊戲。
這個過程包括將參考圖轉為幾何模型、審視輪廓與材質,以及準備執行時使用的素材。Blender 原始檔包含 193 個可編輯網格。匯出的飛船有 14,968 個三角形,依不透明材質分成八個批次。我可以要求增加模型細節,同時由 Astra 控制匯出後的算繪成本。

我核准的飛船生成概念圖。

遊戲所用模型的 Blender 算繪圖。
嘗試程序化水體
我也花了許多時間在 Sunwake 中嘗試水體效果。這款遊戲讓玩家駕駛小船,航行於程序化生成的海洋。我想要有稜面的波浪、流暢的動態,以及彩繪玻璃般的色彩。Astra 在 Three.js 中打造了自訂水體算繪器,以同一套波浪模型驅動畫面中的海面與船隻浮力。船體隨波浪升降、俯仰與側傾,局部模擬則處理漣漪和泡沫,船隻行經之處也會留下尾流與水花。後來,我請 Astra 在 Blender 中製作更精細的船隻,再將它加入遊戲。這與 Void Explorer 採用相同的做法:程序化生成的環境搭配獨立製作的載具,透過模擬將兩者連結起來。

Sunwake 結合了程序化生成的海洋,以及在 Blender 中獨立製作的船隻。
Hollowflux 則朝另一個方向發展:這是一款圍繞發光地下河打造的小型 2D 動作 RPG。洞穴、角色與裝備都以程式碼繪製,沒有匯入精靈圖集。我持續要求 Astra 讓行走、衝刺與攻擊對水面的擾動更逼真。網格中的各個單元會追蹤水面高度、水流、泡沫與電荷。腳步留下漣漪,長矛刺出狹窄的尾流,鎚擊則激起環狀波紋。水流沿著河岸迴旋,將泡沫帶往下游,同時推動玩家與敵人。水也會影響戰鬥:鰻魚放出的電會沿著相連的水域傳播,因此玩家踏上乾燥的石頭,就能避開這次放電。

Hollowflux 以程式碼繪製遊戲美術,水體會隨著移動 與戰鬥產生反應。
製作與分享遊戲
我喜歡與 Astra 合作的一點是,可以從想要的體驗出發,用圖片將視覺方向具體化,再透過實際試玩提供回饋。反映行星消失的問題,可能會帶來串流載入、幾何資料與移動機制的調整。要求水體更逼真,可能會促成視覺效果與遊戲機制共用的模擬。我仍然需要判斷感覺對不對,但 Astra 能將這些決定落實到程式碼、素材與測試中。
分享瀏覽器遊戲也很簡單。透過 Sites 外掛程式,你可以請 Astra:
發布時開放公開存取,任何人都能直接在瀏覽器中遊玩。
你可以在瀏覽器中遊玩 Void Explorer、Sunwake 和 Hollowflux。
如果你心中一直有個遊戲構想,不妨試著用 Astra 做出一個小型的可玩版本。先挑一個你想做出理想感受的部分,提供幾張視覺參考圖,再測試成果。一艘船、一個房間,或單一互動,都足以作為起點。親自試玩,具體說明想改變的地方,再從那裡繼續打造。