建立遊戲
建立一個可遊玩、正確的瀏覽器遊戲——而非靜態截圖。遊戲只是一個 React 路由,包含一個 <canvas>(或 R3F 的 <Canvas>)加上 DOM 覆蓋 UI。使用 design-ui 技能設計覆蓋層(開始畫面、HUD、選單);本技能擁有遊戲邏輯迴圈與世界。
控制 / 反轉 A/D: 在編寫 WASD、轉向或飛行輸入之前,請開啟 .grok/skills/controls/SKILL.md。如果只閱讀此檔案或單一類型手冊,載具/飛行示範常會出現 A/D 翻轉的問題。
範圍說明——單人、機器人或小型 P2P 合作:
- 預設提供單人或單人 + AI/機器人。
- 2–8 人合作 / 休閒即時(共享游標、派對遊戲、朋友間的休閒動作)受支援——請使用
multiplayer-p2p技能(WebRTC 網狀網路,透過/api/rtc發送信號)。請先閱讀其信任模型。 - P2P 是目前唯一支援的多人模式。請勿半途建立無法連線的 Socket。
參考資料(按需載入):
controls技能 (../controls/) — 必須用於移動/轉向/飛行:玩家可見的 A/D、反轉轉向反模式、飛行副翼、強制自我測試 +window.__controlsTest。對於載具/飛機來說不可省略。references/threejs-foundational.md— 深入的 3D/迴圈/效能參考。用於 3D 專案。references/3d-libs.md— three.js + @react-three/fiber + drei + rapier 的使用說明。threejs技能 (../threejs/) — 官方完整的 Three.js + TSL API 轉儲 (llms-full.txt)。用於進階材質/著色器/WebGPU/載入器;不適用於簡單的 2D Canvas 遊戲。references/babylon.md— Babylon.js,功能齊全的 3D 引擎替代方案。references/phaser.md— Phaser 3,2D 遊戲的預設引擎。references/ecs-architecture.md— 大型遊戲的實體-元件-系統結構。references/genres/*.md— 各類型遊戲手冊(FPS、2D 平台遊戲、賽車卡丁車、益智三消俄羅斯方塊、體素我的世界、無盡跑酷、俯視雙搖桿射擊、塔防、棋盤卡牌象棋)。類型檔案不能取代controls。references/game-feel-juice.md、input.md、audio.md、collision-physics.md、save-persistence.md、procedural-generation.md、ai-pathfinding.md。game-asset-core(+game-animation-frames/game-tilesets/game-character-consistency/game-ui-icons)— 產生精靈圖、圖集、圖塊或 UI 時,引擎就緒的 2D 美術預設值和驗證(參見 §6)。
為建置選取特定的類型/主題參考;此檔案是所有遊戲共用邏輯迴圈/世界的核心。輸入符號 → controls。 2D 遊戲美術 → game-asset-core。
1. 遊戲迴圈與時間控制(#1 正確性問題)
- 使用引擎的 RAF 迴圈驅動遊戲迴圈(
renderer.setAnimationLoop、R3F 的useFrame,或 2D Canvas 的requestAnimationFrame)。絕不使用setInterval/setTimeout/Date.now()進行遊戲計時。 - 將所有移動/動畫按 delta 時間(秒)縮放,使速度與幀率無關(60Hz vs 144Hz)。每幀計算一次 delta 並重複使用。
- three.js:使用
THREE.Timer(而非Clock—Clock.getDelta()在同一幀第二次呼叫時回傳 ~0,是經典的凍結錯誤)。
- three.js:使用
- 限制 delta 上限(
min(delta, 0.1)),以防止後台分頁造成物體瞬移。 - 物理/遊戲邏輯使用固定時間步長: 累積 delta,並以固定速率(例如 1/60)執行模擬步驟,同時以顯示更新率渲染——防止穿透和非確定性。
2. 控制(委派給 controls 技能)
在實作任何 WASD / 轉向 / 飛行程式碼之前,請開啟 .grok/skills/controls/SKILL.md。 該技能是以下內容的權威來源:
- 玩家可見的 A = 左 / D = 右(跟隨鏡頭,向前移動時)
- 為什麼
KeyA → steer−+yaw += steer * +rate會反轉(最常見的錯誤) - 載具 vs FPS(平移 ≠ 轉向)、固定翼副翼、直升機/無人機注意事項
- 強制自我測試 +
window.__controlsTest探針
不要將 genres/racing-kart.md 視為轉向符號的唯一參考——飛機、水上摩托車和機甲從不開啟它。
簡短提醒(完整細節在 controls 中):
// 載具偏航本體(跟隨鏡頭):A 必須在此基礎上增加偏航
forward = (-sin(yaw), 0, -cos(yaw))
// KeyA → steer = +1; yaw += steer * turnRate * speedFactor * dt
// 錯誤(已反轉出貨):KeyA → steer = -1; yaw += steer * turnRate * dt
- 指標鎖定: 僅限滑鼠視角——自行實作 WASD;點擊即玩的覆蓋層;鎖定時關閉。
- 使用按鍵按住狀態 + dt 追蹤按鍵;透過
references/input.md統一裝置。 - 完成: 執行
controls技能檢查清單。對於載具/飛行,僅截圖是不夠的。
3. 3D 方向與世界物件(「朝向側面/背面」錯誤)
- three.js 是右手座標系,+Y 向上:+X 向右,+Y 向上,+Z 朝向觀察者。網格面向 +Z;攝影機看向 −Z(經典的「攝影機朝向背面」陷阱)。
- 像
Cone/Cylinder這樣的原始物體預設指向 +Y → 需旋轉以使其尖端對齊前方方向(geo.rotateX(Math.PI/2))。 - 將網格朝向
forward(網格面向 +Z):最簡單的正確方式是mesh.lookAt(mesh.position.clone().add(forward))。要手動建構基底,請將 +Z 行設為forward,並選擇能保持正確右手旋轉(det = +1)的 x 軸:xAxis = normalize(cross(up, forward)),然後makeBasis(xAxis, up, forward)。 注意這裡是cross(up, forward)——不是 §2 中的移動right = cross(forward, up)。以 +Z 為目標(而非攝影機的 −Z)會翻轉 x 軸符號,使得xAxis × up = forward;框架保持右手系(無鏡像)。對於網格不要使用makeBasis(xAxis, up, -forward)——那是以 −forward 為目標(攝影機慣例),會導致網格朝向背面。 - 將攝影機朝向
forward(攝影機看向 −Z):將 +Z 行設為-forward——xAxis = normalize(cross(forward, up)),然後makeBasis(xAxis, up, -forward)(或僅用camera.lookAt(target))。 - 保持一致的世界
up以使物體保持直立;只將平面原始物體旋轉至站立狀態。 - 驗證 glTF 匯入方向;使用
AxesHelper/ArrowHelper除錯。直立自我測試:角色站在地面上,而非躺下或陷入地面。
4. 攝影機必須與移動方向一致
- 維護一個專用的
moveForward/moveRight用於移動,計算一次後永不讓攝影程式碼變異(對共用的暫時向量取別名會導致攝影機和移動不一致——這是一個真正可重現的錯誤)。 - 第三人稱跟隨:
desired = playerPos + up*height + moveForward*(-followDist);將攝影機向目標位置插值(使用指數平滑,按 delta 縮放),lookAt(player)。 - 隔離各層的除錯順序:(1) 按鍵註冊 → (2) 移動符號正確 → (3) 攝影機一致。按此順序修復。
5. 效能
- 最小化繪製呼叫(
renderer.info.render.calls,目標 <100):共用材質,對重複物體使用InstancedMesh/BatchedMesh,使用圖集。 - 自行處置 GPU 資源(
geometry/material/texture.dispose())在關卡切換時——three.js 不會對它們進行垃圾回收。物件池化子彈/敵人/粒子。 - 無每幀分配(重複使用暫時向量)。壓縮紋理;對遠距離使用 LOD。
6. 資源(避免生成照片陷阱)
- 互動式 3D 元素(武器視角模型、角色、道具、投射物)→ 使用 3D 幾何體 / glTF 建構,而非生成的圖片。將一個平面的逼真 JPG 槍枝拿在手中當作 FPS 視角模型看起來很怪異,無法動畫化,而且(JPG 沒有 Alpha 通道)渲染成黑色方塊。將真實的 3D 視角模型作為疊加渲染層掛載到攝影機。
- 保留圖像生成僅用於平面 2D 資源(紋理、天空/選單背景、2D 精靈圖、UI 美術)。絕不使用生成的照片作為 3D 網格、視角模型或角色替代品——應使用 3D 幾何體 / glTF 建構這些。在繪製到 Canvas/紋理的圖片上設定 `crossOrigin=