🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付

這是提交給DEV Weekend Challenge: Community 的作品。

社群

讓我們從頭開始

我喜歡週末。不是因為我想逃避編程,恰恰相反。作為一名全職首席工程師,我的工作日都被各種會議、架構決策、程式碼審查和生產任務佔據。晚上時間很短,精力也有限。但周末呢?週末就不同了。週末是純粹的。

只有在這些日子裡,我才能全心投入創作中,純粹為了享受創作的樂趣。寫作。實驗。創造。

最近,我開始參加開發者挑戰賽,這絕對是我近期做出的最棒的決定之一。公開開發的感覺真的很棒。把一些有趣的東西發佈出來,和社群分享,這種感覺真的很有力量。

尤其是這個挑戰,感覺非常適合——週末挑戰

致我稱之為家的社區

自 2017 年我開啟職業軟體工程生涯以來,DEV 社群就一直是我的家。

很多時候,當我遇到困難時,我都能在這裡找到慰藉。尤其是在那些早年歲月裡,當冒名頂替症候群的陰影揮之不去,當犯錯的代價格外沉重,當成長緩慢之時。

閱讀其他開發者的故事很有幫助。看到他們的奮鬥歷程、他們的成功經驗、他們的教訓,這一切都提醒我,我們都在摸索前進。

多年後的今天,我想回饋社會。

我想建造一些東西,它能夠:

  • 讓其他成員微笑。

  • 幫助他們為自己的工作感到自豪。

  • 將他們的文章變成互動內容。

  • 鼓勵不愛發言的成員積極參與。

  • 甚至可能激勵非會員加入。

一些真正與眾不同的東西…一些真正有趣的東西…

我建造的

所以我搭建了 Sunday DEV Drive

Sunday DEV Drive 是一項遊戲,但並非傳統意義上的遊戲。

沒有需要打敗的boss,沒有支線任務,沒有目標,也沒有時間限制。

這是有意為之的不同之處。

這是一款基於瀏覽器的合成波風格駕駛遊戲,遊戲中你的 DEV 社群文章會變成沿著無盡的程式生成道路的霓虹廣告看板。你輸入用戶名,幾秒鐘之內,你就能馳騁在復古未來主義的城市景觀中,你的文章標題、封面圖片以及文章中的精彩語錄都會高高地出現在高速公路上方。

當你開車時,城市周圍的廣告看板會顯示文章——可以是你的文章,也可以是其他人的文章,這些文章都是使用DEV API動態拉取的。

想像一下,你正穿梭在一座數位城市中,突然看到自己寫下的文字在你頭頂閃耀。

或在駕車經過虛擬天際線時,偶然發現另一位開發者的文章。

這無關輸贏,而關乎反思,關乎獲得自豪感,並將文字內容轉化為空間體驗。

高架廣告牌

路邊廣告牌

它有三種工作模式

  • 您的文章:輸入任意開發者使用者名稱。遊戲透過開發者 API 獲取他們的帖子,從文章正文中提取真實引語,並即時渲染到廣告看板紋理上。

  • 激勵模式:如果使用者擁有帳戶但還沒有文章,則每個公告牌都會變成一條鼓勵訊息,內容為「點擊此處開始寫作!」 - 每個公告牌都直接連結到dev.to/new

  • 試玩:沒有開發者帳號?沒問題。先體驗試玩版,讓遊戲說服你加入社區,每一塊廣告看板都像是一份邀請。

邀請函

每個廣告看板都可以點擊。看到想看的貼文了嗎?點擊它,就會在新標籤頁中開啟。

除了廣告牌,綠色路標還會在你開車時顯示你的個人資料統計資訊:文章總數、終身按讚數、閱讀時間、你的熱門貼文、最喜歡的標籤以及你加入 DEV 的日期。

路標

但等等,還有更多。

你甚至可以切換到第一人稱視角,直接從駕駛艙內駕駛,獲得完全沉浸式的體驗。

內部模式

背後的理念

我們通常被動地閱讀文章,在資訊流中滑動瀏覽。但如果你的文章成為一個世界的一部分呢?如果你的想法出現在數位廣告看板上呢?如果發現開發者就像探索一座城市,而不是瀏覽一個列表,那又會怎麼樣?

Sunday DEV Drive 將文章變成風景,讓社群內容變得觸手可及。

不只是一款遊戲

從本質上講,這個專案是一封感謝信。

進入平台。

致各位編劇。

致讀者們。

致那些仍在自我懷疑的初學者們。

如果你曾經發表過哪怕一篇文章,你就應該看到它以霓虹燈的形式閃耀。

如果你一直想寫作,也許這就是個訊號。還在等什麼?現在就開始吧!一年後,你會後悔沒有早點開始。行動可以避免後悔。

歡迎來到週日 DEV Drive! 🚗✨

示範

親自體驗:🔗 sundaydevdrive.pilotronica.com

試試用自己的開發者用戶名,或直接點擊「試駕」立即體驗。

支援遊戲控制器

插入控制器,即可享受遊戲體驗。

控制

程式碼

整個程式碼庫都是公開的,歡迎造訪程式碼庫查看:

{% github georgekobaidze/sunday-dev-drive

請不要對我的 JavaScript 程式碼過於苛刻,我來自 API 和資料庫領域——我徹頭徹尾是一名後端開發人員。

我是如何建造它的

堆疊

  • Three.js (r170) - 3D渲染,透過ES模組從CDN導入地圖加載

  • 原生 JavaScript - 12 個 ES 模組,零建置步驟,零打包工具,零 npm 依賴

  • Canvas API-所有廣告看板和路標的紋理都是透過程式繪製的。

  • 開發 API - 公共端點,無需 API 金鑰,無需後端

無需 React,無需 Vite,無需 webpack。使用任何靜態檔案伺服器和磁碟機開啟index.html即可。

大樓

整個專案由 12 個 ES 模組組成,具有清晰的依賴關係圖,並且沒有循環導入:

scene.js ← road.js ← buildings.js
                    ← car.js ← camera.js
                              ← input.js
         ← billboards.js ← stats.js ← api.js
main.js imports everything

每個模組都有自己的領域: road.js生成路徑, car.js建立汽車, billboards.js處理文章到紋理的轉換流程main.js入口點將它們連接起來並執行動畫循環。

程序化道路生成:隨機轉向

道路會隨著你的駕駛不斷生成。沒有預設的路線,它使用隨機轉向演算法逐段擴展:

export function growPath(count) {
  for (let i = 0; i < count; i++) {
    if (--pathTurnCountdown <= 0) {
      pathTurnRate      = (Math.random() - 0.5) * 0.042;
      pathTurnCountdown = 4 + Math.floor(Math.random() * 6);
    }
    pathHead.angle += pathTurnRate;
    pathData.push({ pos: pathHead.pos.clone(), angle: pathHead.angle });
    pathHead.pos = new THREE.Vector3(
      pathHead.pos.x + Math.sin(pathHead.angle) * SEGMENT_LEN,
      0,
      pathHead.pos.z - Math.cos(pathHead.angle) * SEGMENT_LEN
    );
  }
}

每隔幾個路段,就會隨機選擇一個新的轉彎速率和持續時間。由於轉彎速率是在多個路段上累積的,而不是瞬間應用,因此道路呈現出平滑自然的曲線,緩緩延伸,而非突兀的之字形。透過隨機化轉彎的幅度(範圍為0.042 )和持續時間( 4–10 segments ),就能從簡單的數學計算中創造出令人驚訝的變化。

路面本身是一個帶狀網格:一個由 200 個線段組成的滑動窗口,這些線段由原始BufferGeometry重建而成。對於每個線段,使用路徑角度計算垂直邊緣點:

const rx = Math.cos(pd.angle);
const rz = Math.sin(pd.angle);
const lx = pd.pos.x - rx * ROAD_WIDTH / 2;  // Left edge
const hx = pd.pos.x + rx * ROAD_WIDTH / 2;  // Right edge

絲帶跟隨車輛行駛。當你行駛足夠遠的距離後,網格會以你的位置為中心重新建構。道路始終在你腳下,並延伸至地平線盡頭。

畫布渲染的廣告看板紋理

這裡是 DEV API 與 Canvas API 的交會點。每個廣告看板紋理都是一個512×768畫布(路邊)或1024×512畫布(俯視),逐部分繪製而成:

  1. 頂部欄- 帶有霓虹邊框的條形區域,顯示作者和按讚數

  2. 封面圖片- 從文章的cover_image URL 加載,並帶有黑邊,非同步加載

  3. 摘錄-從文章正文中擷取的實際引文

  4. 尾- 閱讀時間和標籤顯示

棘手之處在於:封面圖像是在紋理應用到 3D 網格之後才載入的。 Three.js 透過tex.needsUpdate = true很好地處理了這個問題:

if (article.cover_image) {
  const img = new window.Image();
  img.crossOrigin = 'anonymous';
  img.onload = () => {
    drawImageLetterbox(img, cx, cy, cw, ch);
    tex.needsUpdate = true;  // GPU re-uploads on next frame
  };
  img.src = article.cover_image;
}

對於沒有封面圖片的文章,封面區域會顯示一條趣味十足的“404 / cover_image: null / (作者太忙)”訊息,並配以復古網格背景。在激勵模式下,則會顯示「點擊此處開始寫作!」。

程式碼片段擷取:將 Markdown 轉換為廣告看板引用

僅僅在廣告看板上展示文章標題是不夠的。我想要的是原文引用。但是 DEV API 回傳的是原始 Markdown 程式碼,而不是乾淨的散文。提取流程會去除程式碼區塊、圖片、連結和標題,然後挑選出長度合適的段落——既要足夠長以保證內容有意義,又要足夠短以適應廣告看板的尺寸。

60-400 個字元的限制至關重要。太短會導致句子孤立,太長則會導致文字超出廣告看板螢幕範圍。最終效果:清晰易讀,真實反映作者原意。

汽車物理學:同時踩下兩個踏板時的掙扎效應

駕駛模型很簡單,但有一個細節讓我引以為傲。當玩家同時按下油門和煞車時(這是遊戲手把的常見操作),車輛不會立即抵消,而是會進入一種「掙扎」狀態,這種狀態基本上是低速高負荷運轉:

if (throttle > 0 && brake > 0) {
  const struggle = CAR.maxSpeed * 0.08;
  carState.speed += (struggle - carState.speed) * 0.04;
}

透過線性插值平滑處理,速度最終收斂到最大值的 8%。這模擬了踩煞車的感覺:車子既不會停下來,也不會加速,只是帶著一種被壓抑的能量微微震動。這是一個小細節,卻讓物理效果顯得更生動逼真

加速也遵循收益遞減規律,速度越快,再加速就越難:

const speedRatio = carState.speed / CAR.maxSpeed;
const effectiveAccel = CAR.acceleration * throttle * (1 - speedRatio * 0.85);

這條簡單的曲線取代了原本複雜的阻力模型。零速時,加速度為100%;最高速度時,加速度為15%;中間的曲線則完全平滑。

25篇文章的洗牌

DEV API 有個怪癖:清單端點傳回標題和元資料,但不傳回完整的文章正文。要獲取用於片段提取的實際文本,需要為每篇文章單獨發出一個請求。對於擁有數百篇文章的用戶來說,這意味著數百個請求——速度太慢,而且過於頻繁。

我並不打算讓各位創作者的平台不堪負荷。我會很溫柔的。所以,傑西、班、彼得,如果你們看到了這則訊息,別擔心,我罩著你們。 😄

解決方案:打亂文章列表順序,然後僅深度抓取 25 篇隨機文章。其餘文章則使用其description欄位作為備用程式碼片段:

const shuffled = [...list].sort(() => Math.random() - 0.5);
const toFetch = shuffled.slice(0, 25);
for (const art of toFetch) {
  const r = await fetch(`https://dev.to/api/articles/${art.id}`);
  if (r.status === 429) { art._snippets = [art.description || '']; continue; }
  const full = await r.json();
  art._snippets = extractSnippets(full.body_markdown || '');
  await new Promise(res => setTimeout(res, 350));
}

為了遵守速率限制,每個請求都有 350 毫秒的延遲。如果 API 傳回 429(請求過多),它會優雅地回退到文章描述,而不是直接失敗。玩家不會察覺到這一點,他們只會看到有引言的廣告看板。這些引言是來自完整的文章解析還是回退到文章描述,玩家是看不到的。

基於光線投射的廣告看板點擊檢測

在 3D 場景中點擊 3D 廣告看板打開文章並非易事。解決方案是使用 Three.js 的光線投射技術,將 2D 滑鼠位置轉換為 3D 光線,然後偵測光線與廣告看板幾何形狀的交點。

每個廣告看板都會將文章 URL 儲存在userData中。光線投射會同時檢查orbit.active看板的正面和背面,因此無論從哪個方向觀看,廣告看板都能正常運作。 orbit.active 保護機制可防止在相機旋轉過程中發生意外點擊。

用46個盒子組裝一輛汽車

小時候,我會撿一些看起來沒人需要的零碎木片(至少我當時是這麼認為的),用錘子把它們敲成一個簡陋的汽車形狀,再釘上四個瓶蓋做輪子。完美的玩具車。美好的回憶。

誰能想到,20 多年後,同樣的「堆疊與釘合」方法竟然會在程式碼中派上用場…

本專案中沒有使用任何 3D 模型。汽車的每個部件,例如車身、駕駛室、引擎蓋、後備箱、車輪、輪輞、車窗、儀錶板、大燈、尾燈/剎車燈/倒車燈、方向盤、內飾螢幕等,都是完全由 BoxGeometry 和 CylinderGeometry 基本體建置而成,並經過精心堆疊和手工定位。

我在這上面花了不少時間!但絕對值得。

Lower body → Mid body → Hood → Trunk → Cabin
→ 4× Wheels (cylinder + torus rim)
→ 4× Windows (transparent glass material)
→ Dashboard with neon trim + dual screens
→ Steering wheel (torus + cylinder spokes)
→ Headlights (emissive mesh + PointLight)
→ Tail/brake/reverse lights

轉彎時,方向盤實際上也會旋轉:

const sw = car.userData.steeringWheel;
if (sw) sw.rotation.z = -carState.steer * 0.5;

只有在車內攝影機模式下才能看到,但它的確存在。這類細節在真正發揮作用之前,往往無關緊要。

還有很多話要說

這個專案開發過程非常精彩。遊戲的每個部分都經過深思熟慮,我也從中學到了很多。我在這裡分享的只是其中最令人興奮的部分,還有很多其他引人入勝的細節。如果把所有內容都寫出來,我可能會寫成一本書。也許以後有機會再講這個故事吧。

我這個週末學到了什麼

在一個週末的時間裡,不使用任何依賴項和建構工具,建立一個 3D 遊戲,我學到了一些東西:

  1. Canvas API 在紋理處理方面功能極為強大。所有廣告看板、路標、太陽、天際線,都可以使用fillRectfillTextdrawImage繪製出來,無需紋理檔案。

  2. ES 模組已準備好投入生產環境。導入映射 + CDN + type="module"即可建立完整的模組系統,無需任何工具。十二個模組,清晰的依賴關係圖,無需打包工具。

  3. 簡單的物理原理勝過複雜的物理原理。一個簡單的遞減收益公式( 1 - speedRatio * 0.85 )比複雜的摩擦模擬更能帶來「感覺」。遊戲不是模擬,它們關乎的是事物的感受。

  4. 開發 API 簡直是個寶庫。它公開可用,無需身份驗證,速率限制寬鬆,還能完全存取 Markdown 正文。社群自己的 API 為社群服務,感覺非常合適。

這個專案是在一個週末,靠著咖啡、遊戲手把和對開發者社區的深深敬意完成的。你們寫的每一篇文章都值得成為無盡合成波音樂高速公路上的霓虹廣告看板。

現在輪到你了!開著你的車兜風,在留言區分享你的旅程,截圖炫耀給你的朋友、同事…所有人!我很想聽聽大家的反應。讓我們向所有人證明,我們才是最棒的社區!


原文出處:https://dev.to/georgekobaidze/sunday-dev-drive-a-synthwave-driving-experience-through-your-dev-community-articles-5032


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝26   💬2  
721
🥈
我愛JS
💬5  
17
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
🔧 阿川の電商水電行
Shopify 顧問、維護與客製化
💡
小任務 / 單次支援方案
單次處理 Shopify 修正/微調
⭐️
維護方案
每月 Shopify 技術支援 + 小修改 + 諮詢
🚀
專案建置
Shopify 功能導入、培訓 + 分階段交付