發(fā)實(shí)戰(zhàn)教程)
最近在折騰 AI 輔助開(kāi)發(fā)工作流時(shí)嘗試用 Grok Build 來(lái)快速搭建一個(gè)火星模擬游戲的網(wǎng)頁(yè)原型。整個(gè)過(guò)程比預(yù)想中流暢但也有一些值得注意的坑生成代碼的幻覺(jué)、Canvas 渲染性能、模塊拆分方式等。這里把完整流程整理成一份可直接復(fù)現(xiàn)的實(shí)戰(zhàn)教程既包含 AI 輔助構(gòu)建的思路也包含一個(gè)真正能運(yùn)行的火星模擬游戲代碼新手可以照著做有經(jīng)驗(yàn)的開(kāi)發(fā)者也能直接參考代碼結(jié)構(gòu)和排錯(cuò)方案。1. 背景與核心概念1.1 什么是 Grok BuildGrok Build 是當(dāng)前 AI 編程輔助工具生態(tài)中的一個(gè)新方向它強(qiáng)調(diào)“通過(guò)自然語(yǔ)言描述需求讓 AI 幫助生成、修改和調(diào)試完整項(xiàng)目代碼”。相比傳統(tǒng)的人工手寫(xiě)全部代碼Grok Build 的核心價(jià)值在于快速生成原型把模糊的產(chǎn)品想法轉(zhuǎn)換成可運(yùn)行的代碼骨架。輔助調(diào)試把報(bào)錯(cuò)信息直接交給 AI讓它分析可能原因并給出修復(fù)建議。代碼補(bǔ)全與重構(gòu)在已有代碼基礎(chǔ)上做功能擴(kuò)展、性能優(yōu)化和結(jié)構(gòu)重構(gòu)。不過(guò)需要注意AI 生成代碼并不等于“無(wú)需驗(yàn)證”。Grok Build 生成的代碼仍然可能存在邏輯錯(cuò)誤、版本不匹配、安全邊界缺失等問(wèn)題。所以本文的實(shí)踐思路是用 Grok Build 做效率放大器但最終由開(kāi)發(fā)者負(fù)責(zé)審查、測(cè)試和收口。這種工作流在當(dāng)前落地性最強(qiáng)也最值得借鑒。網(wǎng)上關(guān)于 Grok Build 的版本迭代信息比較多不同來(lái)源說(shuō)法并不一致。本文不依賴(lài)某個(gè)具體版本的功能而是聚焦“AI 輔助構(gòu)建 網(wǎng)頁(yè)游戲開(kāi)發(fā)”的通用工作流。只要你的 AI 編程助手支持自然語(yǔ)言對(duì)話(huà)和代碼生成這套方法基本都可以復(fù)用。1.2 火星模擬游戲的技術(shù)選型火星模擬游戲是一個(gè)很適合用來(lái)練習(xí) AI 輔助開(kāi)發(fā)的項(xiàng)目因?yàn)樗藥最?lèi)典型需求場(chǎng)景渲染火星表面、天空、地貌、隕石坑。交互控制火星車(chē)移動(dòng)、視角切換、狀態(tài)操作。狀態(tài)管理氧氣、能量、距離、進(jìn)度等動(dòng)態(tài)數(shù)據(jù)。界面設(shè)計(jì)儀表盤(pán)、按鈕、彈窗。在技術(shù)選型上這里不引入重量級(jí)游戲引擎而是使用最輕量的方案技術(shù)作用說(shuō)明HTML5 Canvas2D 游戲渲染所有游戲元素都繪制在canvas上JavaScript游戲邏輯控制循環(huán)、事件、狀態(tài)更新CSS頁(yè)面布局與儀表盤(pán)負(fù)責(zé) UI 層樣式瀏覽器運(yùn)行環(huán)境不需要安裝額外依賴(lài)這樣做的優(yōu)勢(shì)很明顯代碼量可控、運(yùn)行環(huán)境通用、方便粘貼到任意 HTML 文件中直接打開(kāi)運(yùn)行。對(duì)初學(xué)者來(lái)說(shuō)Canvas 2D 比 WebGL / Three.js 更容易理解對(duì)于想擴(kuò)展的開(kāi)發(fā)者后續(xù)也能平滑升級(jí)到 Three.js 做 3D 版本。1.3 為什么用 AI 輔助構(gòu)建游戲原型傳統(tǒng)開(kāi)發(fā)流程需要先設(shè)計(jì)類(lèi)、寫(xiě)渲染函數(shù)、調(diào)事件綁定細(xì)節(jié)非常多。而使用 Grok Build 構(gòu)建原型時(shí)可以把工作重心放在“需求表達(dá)”和“代碼驗(yàn)收”上用自然語(yǔ)言描述游戲規(guī)則和界面布局。讓 AI 生成第一版完整代碼。運(yùn)行代碼檢查效果與預(yù)期偏差。把問(wèn)題反饋給 AI迭代優(yōu)化。人工審查關(guān)鍵邏輯補(bǔ)齊安全的邊界處理。這種模式不是“完全交給 AI”而是“讓 AI 承擔(dān)初稿工作量人負(fù)責(zé)判斷和收尾”。下面我們就按這個(gè)流程完整制作一個(gè)火星模擬游戲。2. 環(huán)境準(zhǔn)備與版本說(shuō)明2.1 運(yùn)行環(huán)境本文示例是一個(gè)純前端項(xiàng)目不需要安裝 Node.js、Python 或其他后端環(huán)境。需要的工具如下一個(gè)現(xiàn)代瀏覽器Chrome、Edge、Firefox 均可。一個(gè)文本編輯器VS Code、Sublime、記事本都可以。瀏覽器開(kāi)發(fā)者工具用于查看控制臺(tái)報(bào)錯(cuò)和性能情況。如果你的電腦上已經(jīng)安裝了 VS Code那就更好了可以配合 Live Server 插件打開(kāi)頁(yè)面體驗(yàn)會(huì)更好。沒(méi)有安裝也無(wú)所謂直接用瀏覽器打開(kāi) HTML 文件即可運(yùn)行。2.2 版本說(shuō)明因?yàn)檫@是一個(gè)不依賴(lài)第三方庫(kù)的網(wǎng)頁(yè)游戲所以不需要鎖定特定依賴(lài)版本。HTML5 Canvas 和 ES6 JavaScript 是現(xiàn)代瀏覽器的標(biāo)準(zhǔn)能力。唯一需要注意的是代碼中使用的是 ES6 語(yǔ)法包括const、let、箭頭函數(shù)、class等。如果你需要兼容 IE 等老舊瀏覽器需要額外做轉(zhuǎn)譯但本文不做兼容處理。版本需要根據(jù)你的項(xiàng)目實(shí)際情況調(diào)整本文示例以現(xiàn)代瀏覽器常見(jiàn)環(huán)境為例重點(diǎn)演示配置思路。2.3 AI 工具準(zhǔn)備如果你要在自己的電腦上復(fù)現(xiàn)“Grok Build 工作流”需要準(zhǔn)備好 AI 編程助手的訪(fǎng)問(wèn)方式。具體包括一個(gè)可用的 Grok Build 賬號(hào)或類(lèi)似 AI 編程助手入口。Grok Build的對(duì)話(huà)窗口或命令行終端。準(zhǔn)備好描述需求的表達(dá)能力。在本文示例中我們會(huì)模擬多輪“人機(jī)對(duì)話(huà)”展示如何向 Grok Build 描述需求、接收代碼、反饋問(wèn)題。你完全可以用類(lèi)似的提示詞在自己所用的 AI 工具上生成代碼。3. 火星模擬游戲功能設(shè)計(jì)3.1 需求分析在向 Grok Build 提問(wèn)之前先把需求想清楚。一個(gè)完整的火星模擬游戲至少需要以下模塊模塊功能說(shuō)明場(chǎng)景渲染繪制火星地表、天空、太陽(yáng)、星星、隕石坑火星車(chē)可移動(dòng)的探測(cè)車(chē)支持方向鍵或 WASD 控制儀表盤(pán)顯示氧氣、能量、行駛距離、當(dāng)前坐標(biāo)任務(wù)目標(biāo)到達(dá)指定目標(biāo)點(diǎn)觸發(fā)任務(wù)完成提示狀態(tài)反饋能量耗盡或氧氣歸零時(shí)游戲結(jié)束有了這個(gè)模塊列表就可以把它“翻譯”成 AI 能理解的指令。3.2 技術(shù)方案設(shè)計(jì)游戲采用單頁(yè)面結(jié)構(gòu)核心代碼放在 HTML 文件的script標(biāo)簽中。為了便于理解我們將邏輯拆成幾個(gè)對(duì)象gameState保存游戲全局狀態(tài)。rover火星車(chē)對(duì)象包含位置、角度、速度。target目標(biāo)點(diǎn)位置。update()每一幀更新邏輯。render()每一幀繪制畫(huà)面。keydown/keyup鍵盤(pán)事件監(jiān)聽(tīng)。這樣拆分的好處是邏輯清晰即使 AI 生成的初始版本結(jié)構(gòu)比較混亂我們也可以手動(dòng)整理成統(tǒng)一風(fēng)格。3.3 給 Grok Build 的提示詞示例下面是一個(gè)非常關(guān)鍵的環(huán)節(jié)如何向 AI 描述需求。一個(gè)高質(zhì)量的提示詞應(yīng)該包含項(xiàng)目類(lèi)型和語(yǔ)言。核心功能列表。界面布局說(shuō)明。運(yùn)行限制不要外部依賴(lài)等。例如請(qǐng)幫我寫(xiě)一個(gè)純 HTML CSS JavaScript 的火星模擬游戲。 要求 1. 使用 Canvas 2D 渲染火星表面畫(huà)面要有星星背景、紅色地面、隕石坑。 2. 用 WASD 控制一輛火星車(chē)移動(dòng)火星車(chē)用簡(jiǎn)單的幾何圖形繪制。 3. 頁(yè)面右側(cè)有儀表盤(pán)顯示氧氣、能量、行駛距離、當(dāng)前坐標(biāo)。 4. 場(chǎng)景中有一個(gè)目標(biāo)點(diǎn)到達(dá)后彈出提示“任務(wù)完成”。 5. 氧氣或能量降為 0 時(shí)游戲結(jié)束。 6. 不要使用任何第三方庫(kù)所有代碼放在一個(gè) index.html 中。把這段提示詞發(fā)給 Grok Build它會(huì)返回一份初始代碼。當(dāng)然第一版代碼通常不完美需要繼續(xù)追問(wèn)。我們會(huì)在下一節(jié)展示完整的迭代過(guò)程。4. 完整實(shí)戰(zhàn)用 Grok Build 構(gòu)建火星模擬游戲?yàn)榱俗屃鞒炭蓮?fù)現(xiàn)這里我把“AI 對(duì)話(huà) 人工整理”后的最終版本完整寫(xiě)出來(lái)。你可以直接復(fù)制下面的代碼到mars-game.html文件中用瀏覽器打開(kāi)運(yùn)行。4.1 創(chuàng)建項(xiàng)目結(jié)構(gòu)先創(chuàng)建一個(gè)文件夾例如mars-game/ └── index.html本項(xiàng)目只有一個(gè)文件便于演示和分發(fā)。如果你后續(xù)想擴(kuò)展也可以拆分成index.html、style.css、main.js三個(gè)文件但本文統(tǒng)一放在一個(gè)文件中。4.2 編寫(xiě) HTML 和 CSS 布局在index.html中先搭建頁(yè)面骨架。左側(cè)是游戲畫(huà)布右側(cè)是儀表盤(pán)面板。!-- 文件路徑mars-game/index.html -- !DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleGrok Build 火星模擬游戲/title style * { margin: 0; padding: 0; box-sizing: border-box; } body { background: #0a0e1a; font-family: Microsoft YaHei, Arial, sans-serif; color: #eee; min-height: 100vh; display: flex; align-items: center; justify-content: center; } .game-wrap { display: flex; gap: 16px; padding: 20px; flex-wrap: wrap; } canvas { border: 2px solid #c1440e; border-radius: 8px; background: #1a0e05; cursor: crosshair; } .dashboard { width: 260px; background: rgba(255, 255, 255, 0.06); border-radius: 12px; padding: 20px; display: flex; flex-direction: column; gap: 18px; } .dashboard h2 { font-size: 20px; color: #ffb347; text-align: center; border-bottom: 1px solid rgba(255, 255, 255, 0.1); padding-bottom: 10px; } .stat-item { display: flex; justify-content: space-between; align-items: center; font-size: 15px; } .stat-label { color: #aaa; } .stat-value { font-weight: bold; color: #ffb347; } .bar-wrap { height: 8px; background: rgba(255, 255, 255, 0.1); border-radius: 4px; margin-top: 4px; } .bar-fill { height: 100%; border-radius: 4px; transition: width 0.2s; } .bar-oxygen { background: linear-gradient(90deg, #23a6d5, #23d5ab); } .bar-energy { background: linear-gradient(90deg, #f7971e, #ffd200); } .tips { background: rgba(0, 0, 0, 0.3); padding: 12px; border-radius: 8px; font-size: 13px; line-height: 1.8; } .btn { padding: 10px; border: none; border-radius: 8px; background: #c1440e; color: #fff; font-size: 15px; cursor: pointer; transition: background 0.2s; } .btn:hover { background: #e05a17; } /style /head body div classgame-wrap canvas idgameCanvas width900 height600/canvas div classdashboard h2 火星探測(cè)車(chē)/h2 div classstat-item span classstat-label氧氣/span span classstat-value idoxygenValue100%/span /div div classbar-wrap div classbar-fill bar-oxygen idoxygenBar stylewidth: 100%/div /div div classstat-item span classstat-label能量/span span classstat-value idenergyValue100%/span /div div classbar-wrap div classbar-fill bar-energy idenergyBar stylewidth: 100%/div /div div classstat-item span classstat-label行駛距離/span span classstat-value iddistanceValue0 m/span /div div classstat-item span classstat-label坐標(biāo)/span span classstat-value idcoordValue(450, 300)/span /div div classtips pb操作說(shuō)明/b/p pW / S前進(jìn) / 后退/p pA / D左轉(zhuǎn) / 右轉(zhuǎn)/p p到達(dá)黃色目標(biāo)點(diǎn)即可完成任務(wù)/p /div button classbtn idresetBtn重置游戲/button /div /div script // 游戲邏輯代碼見(jiàn)下方 /script /body /html這段代碼先把游戲的整體外觀做好。canvas是游戲渲染區(qū)域右側(cè)面板用于展示實(shí)時(shí)數(shù)據(jù)。這里使用了少量 emoji 做視覺(jué)提示不影響代碼結(jié)構(gòu)。4.3 編寫(xiě) Canvas 游戲邏輯下面是最核心的 JavaScript 部分。先在script標(biāo)簽中定義必要的變量和常量。// 文件路徑mars-game/index.html 中 script 內(nèi)的核心代碼 const canvas document.getElementById(gameCanvas); const ctx canvas.getContext(2d); const W canvas.width; const H canvas.height; // 游戲狀態(tài) let gameOver false; let gameWin false; let lastTime 0; let elapsedTime 0; // 火星車(chē)屬性 const rover { x: 450, y: 300, angle: 0, // 朝向角度弧度 speed: 0, // 當(dāng)前速度 maxSpeed: 120, // 最大速度 px/s acceleration: 60, // 加速度 size: 16 // 繪制尺寸 }; // 目標(biāo)點(diǎn) const target { x: 150, y: 150, radius: 24 }; // 資源狀態(tài) const resources { oxygen: 100, energy: 100, distance: 0 };這些變量構(gòu)成了游戲的核心數(shù)據(jù)。rover對(duì)象保存火星車(chē)的位置、角度、速度resources保存氧氣和能量值target是目標(biāo)點(diǎn)坐標(biāo)。接下來(lái)寫(xiě)鍵盤(pán)事件監(jiān)聽(tīng)。const keys {}; document.addEventListener(keydown, (e) { keys[e.code] true; if (e.code Space) { e.preventDefault(); } }); document.addEventListener(keyup, (e) { keys[e.code] false; });keys對(duì)象用來(lái)記錄當(dāng)前按下的按鍵狀態(tài)。游戲循環(huán)中會(huì)根據(jù)keys中的值判斷是否加速、減速或轉(zhuǎn)向。4.4 實(shí)現(xiàn)游戲循環(huán)與更新邏輯游戲循環(huán)是整個(gè)模擬游戲的心臟。我們需要計(jì)算時(shí)間差然后更新?tīng)顟B(tài)、渲染畫(huà)面。function update(dt) { if (gameOver || gameWin) { return; } // 根據(jù)按鍵控制火星車(chē) if (keys[KeyW]) { rover.speed Math.min(rover.speed rover.acceleration * dt, rover.maxSpeed); } if (keys[KeyS]) { rover.speed Math.max(rover.speed - rover.acceleration * dt, -rover.maxSpeed * 0.5); } if (keys[KeyA]) { rover.angle - 2.0 * dt; } if (keys[KeyD]) { rover.angle 2.0 * dt; } // 摩擦減速 if (!keys[KeyW] !keys[KeyS]) { rover.speed * Math.pow(0.5, dt); if (Math.abs(rover.speed) 0.5) { rover.speed 0; } } // 更新位置 const prevX rover.x; const prevY rover.y; rover.x Math.cos(rover.angle) * rover.speed * dt; rover.y Math.sin(rover.angle) * rover.speed * dt; // 邊界約束 rover.x Math.max(20, Math.min(W - 20, rover.x)); rover.y Math.max(20, Math.min(H - 20, rover.y)); // 計(jì)算行駛距離 const moved Math.hypot(rover.x - prevX, rover.y - prevY); resources.distance moved; // 消耗資源移動(dòng)越快消耗越快 const moveFactor Math.abs(rover.speed) / rover.maxSpeed; resources.oxygen - 0.8 * dt * (0.5 moveFactor * 1.5); resources.energy - 1.2 * dt * (0.5 moveFactor * 2.0); // 資源下限 resources.oxygen Math.max(0, resources.oxygen); resources.energy Math.max(0, resources.energy); // 檢查游戲結(jié)束條件 if (resources.oxygen 0 || resources.energy 0) { gameOver true; } // 檢查是否到達(dá)目標(biāo)點(diǎn) const distToTarget Math.hypot(rover.x - target.x, rover.y - target.y); if (distToTarget target.radius rover.size) { gameWin true; } // 更新 UI 儀表盤(pán) updateDashboard(); }這里的關(guān)鍵邏輯是dt是上一幀到當(dāng)前幀的時(shí)間差避免不同刷新率下速度不一致。Math.cos和Math.sin把角度轉(zhuǎn)換成方向向量再乘以速度得到位移。行駛距離累加使用Math.hypot也就是兩點(diǎn)間歐幾里得距離。氧氣和能量消耗與速度相關(guān)速度越快消耗越快增加真實(shí)感。4.5 實(shí)現(xiàn) Canvas 場(chǎng)景渲染渲染部分分為背景、目標(biāo)點(diǎn)、火星車(chē)和粒子特效。先寫(xiě)火星表面背景。function drawBackground() { // 星空 ctx.fillStyle #0a0e1a; ctx.fillRect(0, 0, W, H); // 星星 for (let i 0; i 120; i) { // 使用固定隨機(jī)種子避免每次刷新位置變化 const seed i * 137.5; const sx (seed * 7) % W; const sy (seed * 13) % H; const brightness 0.3 ((i * 29) % 70) / 100; ctx.globalAlpha brightness; ctx.fillStyle #fff; ctx.beginPath(); ctx.arc(sx, sy, 1.2, 0, Math.PI * 2); ctx.fill(); } ctx.globalAlpha 1; // 天空漸變 const skyGrad ctx.createLinearGradient(0, 0, 0, H * 0.5); skyGrad.addColorStop(0, rgba(180, 80, 30, 0.35)); skyGrad.addColorStop(1, rgba(0, 0, 0, 0)); ctx.fillStyle skyGrad; ctx.fillRect(0, 0, W, H * 0.5); // 火星地面 const groundY H * 0.72; const groundGrad ctx.createLinearGradient(0, groundY, 0, H); groundGrad.addColorStop(0, #8b3a1a); groundGrad.addColorStop(0.3, #6b240c); groundGrad.addColorStop(1, #3d1205); ctx.fillStyle groundGrad; ctx.fillRect(0, groundY, W, H - groundY); // 遠(yuǎn)處山丘 ctx.fillStyle rgba(100, 40, 15, 0.6); drawHill(H * 0.62, 80, 1.2); ctx.fillStyle rgba(130, 55, 20, 0.7); drawHill(H * 0.68, 110, 0.8); // 地面細(xì)節(jié)隕石坑 drawCrater(120, H * 0.82, 22); drawCrater(380, H * 0.78, 14); drawCrater(700, H * 0.85, 30); drawCrater(820, H * 0.74, 10); } function drawHill(baseY, height, scale) { ctx.beginPath(); ctx.moveTo(0, baseY height * 0.3); for (let x 0; x W; x 20) { const y baseY Math.sin(x * 0.005 * scale) * height * 0.4 Math.sin(x * 0.01) * height * 0.2; ctx.lineTo(x, y); } ctx.lineTo(W, H); ctx.lineTo(0, H); ctx.closePath(); ctx.fill(); } function drawCrater(x, y, r) { ctx.beginPath(); ctx.arc(x, y, r, 0, Math.PI * 2); ctx.fillStyle rgba(0, 0, 0, 0.2); ctx.fill(); ctx.beginPath(); ctx.arc(x - r * 0.15, y - r * 0.15, r * 0.7, 0, Math.PI * 2); ctx.fillStyle rgba(255, 180, 120, 0.15); ctx.fill(); }星空部分用了偽隨機(jī)數(shù)公式確保每次刷新畫(huà)面時(shí)星星位置固定同時(shí)不引入外部隨機(jī)函數(shù)導(dǎo)致閃爍。地面用了漸變和簡(jiǎn)單曲線(xiàn)繪制山丘視覺(jué)效果比較接近火星環(huán)境。接著繪制目標(biāo)和火星車(chē)。function drawTarget() { // 目標(biāo)點(diǎn)光暈 const pulse 0.6 0.4 * Math.sin(elapsedTime * 3); ctx.beginPath(); ctx.arc(target.x, target.y, target.radius 6, 0, Math.PI * 2); ctx.fillStyle rgba(255, 220, 100, ${0.3 * pulse}); ctx.fill(); // 目標(biāo)點(diǎn)核心 ctx.beginPath(); ctx.arc(target.x, target.y, target.radius, 0, Math.PI * 2); ctx.fillStyle #ffd700; ctx.fill(); ctx.strokeStyle #ff8c00; ctx.lineWidth 3; ctx.stroke(); // 旗幟 ctx.fillStyle #ff5722; ctx.fillRect(target.x 6, target.y - target.radius - 8, 18, 5); ctx.strokeStyle #ccc; ctx.lineWidth 2; ctx.beginPath(); ctx.moveTo(target.x 15, target.y - target.radius - 5); ctx.lineTo(target.x 15, target.y target.radius * 0.6); ctx.stroke(); } function drawRover() { const r rover.size; ctx.save(); ctx.translate(rover.x, rover.y); ctx.rotate(rover.angle); // 車(chē)身陰影 ctx.beginPath(); ctx.ellipse(0, 0, r * 1.6, r * 1.2, 0, 0, Math.PI * 2); ctx.fillStyle rgba(0, 0, 0, 0.35); ctx.fill(); // 車(chē)身 ctx.beginPath(); ctx.roundRect(-r * 1.4, -r * 0.9, r * 2.8, r * 1.8, 4); ctx.fillStyle #e8a33d; ctx.fill(); ctx.strokeStyle #8d5b1e; ctx.lineWidth 2; ctx.stroke(); // 車(chē)門(mén)線(xiàn) ctx.beginPath(); ctx.moveTo(0, -r * 0.9); ctx.lineTo(0, r * 0.9); ctx.strokeStyle #8d5b1e; ctx.lineWidth 1; ctx.stroke(); // 車(chē)頭燈 ctx.fillStyle #fff9c4; ctx.beginPath(); ctx.arc(r * 1.2, -r * 0.3, 4, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(r * 1.2, r * 0.3, 4, 0, Math.PI * 2); ctx.fill(); // 車(chē)輪 ctx.fillStyle #333; ctx.beginPath(); ctx.ellipse(-r, -r * 0.9, 6, 4, 0, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.ellipse(r, -r * 0.9, 6, 4, 0, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.ellipse(-r, r * 0.9, 6, 4, 0, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.ellipse(r, r * 0.9, 6, 4, 0, 0, Math.PI * 2); ctx.fill(); // 天線(xiàn) ctx.strokeStyle #aaa; ctx.lineWidth 1; ctx.beginPath(); ctx.moveTo(0, -r * 0.8); ctx.lineTo(r * 0.2, -r * 1.6); ctx.stroke(); ctx.fillStyle #ff0000; ctx.beginPath(); ctx.arc(r * 0.2, -r * 1.6, 2.5, 0, Math.PI * 2); ctx.fill(); ctx.restore(); }這里用ctx.save()和ctx.restore()包裹坐標(biāo)變換確保旋轉(zhuǎn)操作不會(huì)影響后續(xù)繪制。ctx.roundRect是現(xiàn)代瀏覽器支持的 API如果你的瀏覽器版本較舊可以替換為普通矩形或手動(dòng)繪制圓角矩形。接下來(lái)實(shí)現(xiàn)粒子特效和提示文字。let particles []; function spawnParticles(x, y, count) { for (let i 0; i count; i) { const angle Math.random() * Math.PI * 2; const speed 30 Math.random() * 80; particles.push({ x, y, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed, life: 0, maxLife: 0.6 Math.random() * 0.6, size: 2 Math.random() * 3, color: Math.random() 0.5 ? #ffb347 : #ff5722 }); } } function updateParticles(dt) { for (let i particles.length - 1; i 0; i--) { const p particles[i]; p.x p.vx * dt; p.y p.vy * dt; p.life dt; if (p.life p.maxLife) { particles.splice(i, 1); } } } function drawParticles() { for (const p of particles) { const alpha 1 - p.life / p.maxLife; ctx.globalAlpha alpha; ctx.fillStyle p.color; ctx.beginPath(); ctx.arc(p.x, p.y, p.size * alpha, 0, Math.PI * 2); ctx.fill(); } ctx.globalAlpha 1; }粒子系統(tǒng)用于火星車(chē)移動(dòng)時(shí)揚(yáng)起的塵土增加游戲沉浸感。4.6 實(shí)現(xiàn)儀表盤(pán) UI 更新儀表盤(pán)中的數(shù)據(jù)需要在每幀更新同時(shí)把氧氣和能量值同步到進(jìn)度條和文本。function updateDashboard() { const oxygenPct Math.max(0, resources.oxygen).toFixed(1); const energyPct Math.max(0, resources.energy).toFixed(1); document.getElementById(oxygenValue).textContent oxygenPct %; document.getElementById(oxygenBar).style.width Math.max(0, resources.oxygen) %; document.getElementById(energyValue).textContent energyPct %; document.getElementById(energyBar).style.width Math.max(0, resources.energy) %; const distance Math.floor(resources.distance); document.getElementById(distanceValue).textContent distance m; document.getElementById(coordValue).textContent ( Math.floor(rover.x) , Math.floor(rover.y) ); }注意這里的氧氣和能量是浮點(diǎn)數(shù)顯示時(shí)保留一位小數(shù)即可。進(jìn)度條寬度直接使用百分比數(shù)值。4.7 渲染主循環(huán)最后把主循環(huán)函數(shù)和重置邏輯補(bǔ)上游戲就完整了。function render() { drawBackground(); drawTarget(); drawParticles(); if (!gameOver) { drawRover(); } // 任務(wù)完成提示 if (gameWin) { ctx.fillStyle rgba(0, 0, 0, 0.55); ctx.fillRect(0, 0, W, H); ctx.font bold 44px Arial; ctx.textAlign center; ctx.fillStyle #ffd700; ctx.fillText(任務(wù)完成, W / 2, H / 2 - 20); ctx.font 18px Arial; ctx.fillStyle #fff; ctx.fillText(火星車(chē)成功抵達(dá)目標(biāo)點(diǎn), W / 2, H / 2 30); } // 游戲結(jié)束提示 if (gameOver !gameWin) { ctx.fillStyle rgba(0, 0, 0, 0.6); ctx.fillRect(0, 0, W, H); ctx.font bold 40px Arial; ctx.textAlign center; ctx.fillStyle #ff5252; ctx.fillText(探測(cè)任務(wù)失敗, W / 2, H / 2 - 10); const reason resources.oxygen 0 ? 氧氣耗盡 : 能量耗盡; ctx.font 18px Arial; ctx.fillStyle #fff; ctx.fillText(reason 請(qǐng)重置游戲重新嘗試, W / 2, H / 2 30); } } let lastTimestamp 0; function gameLoop(timestamp) { const dt Math.min((timestamp - lastTimestamp) / 1000, 0.05); lastTimestamp timestamp; elapsedTime dt; update(dt); updateParticles(dt); render(); requestAnimationFrame(gameLoop); } function resetGame() { rover.x 450; rover.y 300; rover.angle 0; rover.speed 0; resources.oxygen 100; resources.energy 100; resources.distance 0; gameOver false; gameWin false; particles []; elapsedTime 0; updateDashboard(); } document.getElementById(resetBtn).addEventListener(click, resetGame); requestAnimationFrame(gameLoop);dt上限限制為 0.05 秒這是為了防止瀏覽器標(biāo)簽頁(yè)切換導(dǎo)致時(shí)間差過(guò)大、游戲出現(xiàn)瞬間跳躍的問(wèn)題。requestAnimationFrame是瀏覽器原生動(dòng)畫(huà)循環(huán)方式不需要額外庫(kù)。4.8 運(yùn)行與驗(yàn)證保存文件后用瀏覽器打開(kāi)mars-game/index.html。你會(huì)看到紅色火星地表和星空背景。一臺(tái)橙色火星車(chē)位于畫(huà)面中央。右上角儀表盤(pán)顯示氧氣 100%、能量 100%、行駛距離 0 米。左上角有一個(gè)閃爍的黃色目標(biāo)點(diǎn)。按 W 前進(jìn)、S 后退、A/D 左右旋轉(zhuǎn)。當(dāng)火星車(chē)到達(dá)目標(biāo)點(diǎn)時(shí)畫(huà)面會(huì)顯示“任務(wù)完成”。如果氧氣或能量耗盡游戲結(jié)束點(diǎn)擊“重置游戲”按鈕可重新開(kāi)始。4.9 結(jié)果說(shuō)明通過(guò)上面的代碼我們用 Grok Build 輔助完成了一個(gè)功能完整的火星模擬游戲。雖然畫(huà)面不是 3D 的但它已經(jīng)包含了一個(gè)游戲的核心要素交互、狀態(tài)、目標(biāo)、失敗條件、UI 反饋。現(xiàn)在你可以嘗試調(diào)整以下參數(shù)觀察變化// 例如調(diào)高移動(dòng)速度 rover.maxSpeed 200; // 降低資源消耗 resources.oxygen - 0.4 * dt * (0.5 moveFactor * 1.0);這種修改方式非常直觀適合逐步驗(yàn)證 AI 生成代碼中每個(gè)變量的含義。5. 常見(jiàn)問(wèn)題與排查思路在實(shí)際開(kāi)發(fā)中無(wú)論是 AI 生成代碼還是手動(dòng)修改都可能遇到問(wèn)題。下面是我在構(gòu)建火星模擬游戲時(shí)遇到的典型問(wèn)題以及對(duì)應(yīng)的排查方案。問(wèn)題現(xiàn)象常見(jiàn)原因解決思路頁(yè)面打開(kāi)后一片空白script代碼報(bào)錯(cuò)導(dǎo)致后續(xù)不執(zhí)行打開(kāi)瀏覽器 F12 控制臺(tái)查看具體報(bào)錯(cuò)位置火星車(chē)無(wú)法移動(dòng)鍵盤(pán)事件沒(méi)有正確綁定檢查keydown監(jiān)聽(tīng)器確認(rèn)keyCode對(duì)應(yīng)正確roundRect is not a function瀏覽器版本過(guò)舊把ctx.roundRect改為手動(dòng)矩形繪制游戲運(yùn)行時(shí)卡頓明顯粒子數(shù)量過(guò)多或每幀創(chuàng)建大量對(duì)象限制粒子總數(shù)使用對(duì)象池重置后游戲狀態(tài)沒(méi)有恢復(fù)resetGame中漏掉某些狀態(tài)變量把gameOver、gameWin、particles都重置目標(biāo)點(diǎn)到達(dá)判定不準(zhǔn)距離計(jì)算錯(cuò)誤使用Math.hypot計(jì)算兩點(diǎn)距離并考慮兩者半徑下面展開(kāi)說(shuō)明兩個(gè)常見(jiàn)問(wèn)題。5.1 鍵盤(pán)事件未生效很多初學(xué)者把keydown事件寫(xiě)成了document.addEventListener(keydown, myFunction);但沒(méi)有檢查myFunction內(nèi)部是否使用了e.code或e.keyCode。本文使用的是e.code對(duì)應(yīng)的按鍵是物理位置比如KeyW、KeyA。如果你希望支持方向鍵可以繼續(xù)添加if (e.code ArrowUp) { keys[KeyW] true; }或者在update函數(shù)里同時(shí)判斷if (keys[ArrowUp] || keys[KeyW]) { rover.speed rover.acceleration * dt; }無(wú)論采用哪種方案關(guān)鍵是要保證事件監(jiān)聽(tīng)函數(shù)被正確注冊(cè)并且keys對(duì)象在頁(yè)面加載時(shí)存在。5.2 AI 生成代碼存在“幻覺(jué)”API這是使用 Grok Build 時(shí)最需要警惕的問(wèn)題。AI 可能會(huì)生成一個(gè)并不存在的瀏覽器 API或者使用了庫(kù)函數(shù)但導(dǎo)入方式不對(duì)。遇到這種情況按以下順序排查閱讀控制臺(tái)報(bào)錯(cuò)信息。確認(rèn)錯(cuò)誤類(lèi)型是TypeError、ReferenceError還是語(yǔ)法錯(cuò)誤。在瀏覽器兼容性網(wǎng)站查詢(xún)?cè)?API 是否受支持。如果需要直接修改代碼采用更基礎(chǔ)的實(shí)現(xiàn)。例如本文中的ctx.roundRect就是較新的 API如果瀏覽器不支持可以改成function drawRoundRect(x, y, w, h, r) { ctx.beginPath(); ctx.moveTo(x r, y); ctx.lineTo(x w - r, y); ctx.arcTo(x w, y, x w, y r, r); ctx.lineTo(x w, y h - r); ctx.arcTo(x w, y h, x w - r, y h, r); ctx.lineTo(x r, y h); ctx.arcTo(x, y h, x, y h - r, r); ctx.lineTo(x, y r); ctx.arcTo(x, y, x r, y, r); ctx.closePath(); }然后把ctx.roundRect(...)替換為drawRoundRect(...)。6. 最佳實(shí)踐與工程建議6.1 Prompt 工程如何讓 AI 生成更高質(zhì)量的代碼使用 Grok Build 時(shí)提問(wèn)方式直接決定生成代碼的質(zhì)量。建議遵循以下原則需求具體化。不要只寫(xiě)“做一個(gè)火星游戲”而要寫(xiě)清楚畫(huà)布尺寸、控制方式、UI 布局、失敗條件。比如請(qǐng)用 Canvas 生成一個(gè) 900x600 的火星游戲畫(huà)布火星車(chē)用 WASD 控制 右側(cè)儀表盤(pán)顯示氧氣和能量使用進(jìn)度條展示地圖中有目標(biāo)點(diǎn)。限制依賴(lài)和版本。如果對(duì)技術(shù)棧有明確要求必須提前說(shuō)明。比如“不使用任何第三方庫(kù)”“使用 ES6 語(yǔ)法”。迭代而非一次完成。第一版代碼往往結(jié)構(gòu)松散正確做法是讓 AI 先生成骨架再逐步補(bǔ)充細(xì)節(jié)。例如第一輪生成基本場(chǎng)景和移動(dòng)。第二輪添加資源消耗和 UI。第三輪增加粒子特效和游戲結(jié)束提示。6.2 代碼審查與安全邊界AI 生成的代碼不能直接上生產(chǎn)需要人工審查以下內(nèi)容事件監(jiān)聽(tīng)是否有全局變量污染。資源消耗計(jì)算數(shù)值是否合理是否可能出現(xiàn)除以零或負(fù)數(shù)。性能每幀是否創(chuàng)建過(guò)多對(duì)象是否頻繁操作 DOM。邊界條件火星車(chē)是否可能移動(dòng)到畫(huà)布外是否已經(jīng)處理。在本項(xiàng)目中邊界處理比較簡(jiǎn)單因?yàn)榈貓D就是畫(huà)布范圍。如果擴(kuò)展為更大的地圖還需要考慮攝像頭跟隨、坐標(biāo)換算等問(wèn)題。6.3 性能優(yōu)化建議當(dāng)前代碼在 900x600 畫(huà)布上運(yùn)行非常流暢但如果要擴(kuò)展功能建議關(guān)注以下性能點(diǎn)減少每幀調(diào)用盡量把不需要每幀更新的 UI 文本放在條件判斷中例如只有數(shù)值變化超過(guò) 1 時(shí)才更新 DOM。粒子對(duì)象池不要頻繁splice粒子可以復(fù)用數(shù)組尾部元素。離屏 Canvas靜態(tài)背景可以提前繪制到離屏 canvas只把動(dòng)態(tài)元素畫(huà)到主畫(huà)布減少重復(fù)計(jì)算。示例中的離屏優(yōu)化思路const bgCanvas document.createElement(canvas); bgCanvas.width W; bgCanvas.height H; const bgCtx bgCanvas.getContext(2d); function drawStaticBackground() { // 在 bgCtx 上繪制星空、地面、山丘 } // 每幀只把 bgCanvas 繪制到主畫(huà)布 ctx.drawImage(bgCanvas, 0, 0);這樣能顯著降低每幀繪制壓力。6.4 版本管理與項(xiàng)目擴(kuò)展雖然這個(gè)項(xiàng)目只有一個(gè) HTML 文件但后續(xù)擴(kuò)展建議拆分文件mars-game/ ├── index.html ├── css/ │ └── style.css ├── js/ │ ├── game.js │ ├── rover.js │ ├── render.js │ └── resources.js拆分后的好處是每個(gè)文件職責(zé)單一后續(xù)添加新功能時(shí)不需要在幾千行代碼里尋找函數(shù)。同時(shí)也可以引入模塊化語(yǔ)法// rover.js export const rover { x: 450, y: 300, angle: 0 };// game.js import { rover } from ./rover.js;需要注意的是瀏覽器原生 ES Module 需要在本地服務(wù)器中運(yùn)行直接雙擊index.html可能因跨域限制無(wú)法加載模塊。這時(shí)可以使用 VS Code 的 Live Server 插件。6.5 AI 輔助開(kāi)發(fā)的正確學(xué)習(xí)路徑想真正提升 AI 輔助開(kāi)發(fā)的效率不能只依賴(lài) AI還需要具備足夠的基礎(chǔ)知識(shí)。建議按以下順序?qū)W習(xí)掌握 HTML、CSS、JavaScript 基礎(chǔ)語(yǔ)法。理解 Canvas 2D 的坐標(biāo)系和基本繪圖 API。熟悉requestAnimationFrame動(dòng)畫(huà)循環(huán)機(jī)制。學(xué)會(huì)使用瀏覽器開(kāi)發(fā)者工具排查問(wèn)題。用 AI 工具生成代碼并人工審查、修改、驗(yàn)證。這樣即使 AI 生成了一段不完美甚至錯(cuò)誤的代碼你也能快速定位并修復(fù)。反過(guò)來(lái)AI 又能幫你把重復(fù)勞動(dòng)降到最低讓你有更多時(shí)間關(guān)注設(shè)計(jì)和邏輯。7. 總結(jié)與下一步學(xué)習(xí)方向這套“Grok Build 打造火星模擬游戲”的完整流程核心收獲可以總結(jié)為以下幾點(diǎn)用 AI 輔助構(gòu)建原型通過(guò)結(jié)構(gòu)化提示詞讓 AI 在幾分鐘內(nèi)生成可運(yùn)行的游戲初稿。理解游戲核心循環(huán)update負(fù)責(zé)邏輯render負(fù)責(zé)繪制requestAnimationFrame負(fù)責(zé)循環(huán)這是所有實(shí)時(shí)游戲/交互應(yīng)用的基礎(chǔ)。掌握 Canvas 基本渲染坐標(biāo)變換、幾何圖形繪制、粒子系統(tǒng)都能在火星模擬游戲里得到充分練習(xí)。重視代碼審查與邊界處理AI 能提速但最終質(zhì)量取決于開(kāi)發(fā)者的判斷和測(cè)試。下一步如果你想讓這個(gè)項(xiàng)目繼續(xù)進(jìn)化可以從幾個(gè)方向入手把 2D 場(chǎng)景升級(jí)為 3D使用 Three.js 渲染火星地形。接入真實(shí)火星地圖紋理讓地面更接近“毅力號(hào)”探測(cè)器拍攝的畫(huà)面。增加任務(wù)系統(tǒng)不只是單一目標(biāo)點(diǎn)而是多個(gè)站點(diǎn)和采集任務(wù)。加入聲音效果例如引擎聲、警報(bào)聲。把游戲狀態(tài)存儲(chǔ)到localStorage支持存檔和繼續(xù)游戲。每個(gè)方向都適合繼續(xù)用 Grok Build 輔助編碼建議你直接打開(kāi) AI 工具把當(dāng)前 HTML 文件和需求描述發(fā)給它開(kāi)始你的迭代版本。動(dòng)手實(shí)踐永遠(yuǎn)比看教程提升得快。