建立擲骰子

Justin Gitlin
Justin Gitlin

Roll ItChrome 實驗,只需使用手機和電腦上的瀏覽器,即可重新體驗經典的木板人遊戲。手機上的瀏覽器可讓您輕鬆瞄準並滾動球,而電腦上的瀏覽器則可透過 WebGL 和 Canvas 即時算繪 Roll It 街道的圖形。兩部裝置會透過 Websockets 進行通訊。沒有應用程式。沒有已下載的內容。沒有符記。只要有新式瀏覽器即可使用。

在 Google Creative Lab 的指導下,Legwork 開發了使用者體驗、介面和遊戲環境,然後與開發合作夥伴 Mode Set 合作,共同打造 Roll It。在整個專案期間,我們遇到了許多獨特的挑戰。本文將介紹我們在開發 Roll It 時採用的部分技術、發現的訣竅,以及學到的經驗。

3D 工作流程

一開始,我們就面臨了一個難題:如何將 3D 模型從軟體轉換為適合網頁的檔案格式。在 Cinema 4D 中建立資產後,模型就會簡化並轉換為低多邊形網格。每個網格都會獲得特定的多邊形選取標記,以便區分物件的不同部分,用於著色和紋理處理。接著,我們就能匯出 Collada 1.5 (.dae) 檔案,並匯入開放原始碼 3D 程式 Blender,以便製作與 three.js 相容的檔案。確認模型已正確匯入後,我們將網格匯出為 JSON 檔案,並使用程式碼套用燈光。以下是我們採取的詳細步驟:

在 C4D 中建立物件模型。確認網格法向量朝外。
在 C4D 中建立物件模型。請確認網格法向量朝外。
使用多邊形選取工具,為要製作紋理的特定區域建立選取標記。為每個選取標記套用素材資源。
使用多邊形選取工具,為要套用紋理的特定區域建立選取標記。為每個選取標記套用素材資源。
將網格匯出為 COLLADA 1.5 .dae 檔案。
將網格匯出為 COLLADA 1.5 .dae 檔案。
確認已勾選「匯出 2D 幾何圖形」在程式碼方面,匯出三角形通常在 3D 環境中獲得更廣泛的支援,但缺點是會使多邊形數量加倍。多邊形數量越多,模型對電腦處理器的負擔就越大。因此,如果您發現效能變慢,請取消勾選這個選項。
確認已勾選「匯出 2D 幾何圖形」。在程式碼方面,匯出三角形通常在 3D 環境中獲得更廣泛的支援,但缺點是會使多邊形數量加倍。多邊形數量越多,模型對電腦處理器的負擔就越大。因此,如果發現效能變慢,請保持勾選這個選項。
將 Collada 檔案匯入 Blender。
將 Collada 檔案匯入 Blender。
匯入 Blender 後,您會發現材質和選取標記也已保留。
匯入 Blender 後,您會發現材質和選取標記也已移植。
選取物體,然後調整物體的材質。
選取物件,並調整物件的材質。
將檔案匯出為 three.js 檔案
為確保與 WebGL 相容,請將檔案匯出為 three.js 檔案。

編寫程式碼

Roll It 是使用開放原始碼程式庫開發,可在現代瀏覽器中原生執行。有了 WebGL 和 WebSocket 等技術,網路就能提供媲美家用主機的遊戲和多媒體體驗。隨著 HTML 開發工具的進步,開發人員可輕鬆打造這類體驗的程度也大幅提升。

開發環境

Roll It 的大部分原始程式碼都是使用 CoffeeScript 編寫,這是一種簡潔明瞭的語言,可轉譯為結構完整且經過 lint 處理的 JavaScript。CoffeeScript 的優異繼承模型和更清晰的範圍處理機制,使其在 OOP 開發方面表現出色。這份 CSS 是使用 SASS 架構編寫而成,可為開發人員提供多種實用工具,用於改善及管理專案的樣式表。將這些系統加入建構程序需要花點時間設定,但成果絕對值得,尤其是像 Roll It 這類較大型的專案。我們設定了 Ruby on Rails 伺服器,在開發期間自動編譯素材資源,讓所有編譯步驟都一目了然。

除了打造簡化且舒適的程式碼環境,我們也手動最佳化素材資源,盡可能減少要求,加快網站載入速度。我們將每張圖片都經過幾個壓縮程式處理,包括 ImageOptimImageAlpha。每個程式都會以各自的方式最佳化圖片,分別為無損和有損。只要正確搭配使用這些設定,就能大幅縮減圖片的檔案大小。這不僅可在載入外部圖片時節省頻寬,而且在經過最佳化後,圖片會轉譯為更小的 base64 編碼字串,可在 HTML、CSS 和 JavaScript 中內嵌。在 Base64 編碼主題上,我們也使用這個技巧,將 Open Sans WOFF 和 SVG 字型檔案直接嵌入 CSS,進而減少總要求次數。

啟用物理效果的 3D 場景

THREE.js 是網路上常見的 3D JavaScript 程式庫。它會包裝低階 3D 數學和硬體 WebGL 最佳化,讓一般使用者輕鬆建立光線充足且美觀的互動式 3D 場景,不必編寫自訂著色器或手動執行矩陣轉換。Physijs 是針對已轉譯為 JavaScript 的熱門 C++ 物理程式庫所設計的 THREE.js 專屬包裝函式。我們利用這個程式庫,模擬球在 3D 空間中滾動、跳躍及彈跳至目的地的情況。

從一開始,我們就設法讓玩家在滾動球時,能感受到真實的物理體驗,同時確保遊戲中的物件能讓玩家感受到真實感。這需要多次調整 Physijs 場景的整體重力、球從玩家投擲時滾動的速度、車道跳躍的斜率,以及球和車道材質的摩擦力和復原力 (彈力) 屬性。結合更強的重力和更快的速度,可帶來更逼真的遊戲體驗。

平滑處理

大多數的新型瀏覽器和顯示卡組合應可在 WebGL 環境中充分利用原生硬體抗鋸齒功能,但有些組合可能無法順利運作。如果反鋸齒功能無法原生運作,THREE.js 場景中任何明顯且對比強烈的邊緣都會變得鋸齒狀且難看 (至少對我們的眼睛來說)。

所幸,我們有修正方式:透過程式碼片段,我們可以偵測平台是否原生支援反鋸齒。如果是,那麼我們就可以繼續進行。如果不是,THREE.js 內建一系列後製著色器,可以協助我們解決問題。也就是 FXAA 反鋸齒濾鏡。透過在每個影格中使用此著色器重新繪製算繪場景,我們通常可以讓線條和邊緣看起來更平滑。請參考以下示範:

// Check for native platform antialias support via the THREE renderer
// from: http://codeflow.org/entries/2013/feb/22/how-to-write-portable-webgl/#antialiasing
var nativeAntialiasSupport = (renderer.context.getParameter(renderer.context.SAMPLES) == 0) ? false : true;

以加速計為基礎的遊戲控制

Roll It 的許多神奇之處,都來自玩家使用手機做出的滾動手勢。行動裝置在瀏覽器中使用加速計已有一段時間,但我們這個產業才剛開始探索在網頁上使用以動作為基礎的手勢辨識功能。我們會受到手機加速計提供的資料限制,但只要稍微發揮創意,就能打造出令人驚豔的新體驗。

偵測 Roll It 的主要「捲動」手勢時,系統會先追蹤來自視窗 deviceorientation 事件的最新 10 個加速計更新。我們會將目前傾斜值減去先前的傾斜值,藉此儲存事件之間的角度差異。接著,我們會持續加總過去十個角度差異,以便在手機移動時偵測連續旋轉動作。當手機超過掃描角度變更的門檻時,系統就會觸發旋轉動作。接著,我們會在掃描動作中找出最大的單一傾斜差異,藉此估算球的速度。在 Roll It 中,系統會使用附加至每個加速計更新的時間戳記,將這項速度標準化。這有助於在不同裝置上,讓加速計更新流程以變化速度流入瀏覽器。

WebSocket 通訊

玩家使用手機滾動球後,手機會傳送訊息給筆電,指示筆電發射球。這則「roll」訊息會透過 JSON 資料物件,透過兩台機器之間的 WebSocket 連線傳送。JSON 資料很小,主要包含訊息類型、投擲速度和瞄準方向。

{
  "type": "device:ball-thrown",
  "speed": 0.5,
  "aim": 0.1
}

筆電和手機之間的所有通訊都會透過類似以下的 JSON 訊息進行。每當遊戲在電腦上更新狀態,或是使用者傾斜或輕觸手機上的按鈕時,兩部裝置之間就會傳送 WebSocket 訊息。為了讓這項通訊簡單易管,WebSockets 訊息會透過任一瀏覽器的單一出口點進行廣播。相反地,接收端瀏覽器只有一個進入點,且一個 WebSocket 物件會處理兩端的所有傳入和傳出訊息。收到 WebSocket 訊息後,系統會使用 jQuery 的 trigger() 方法,在 JavaScript 應用程式中重新廣播 JSON 資料。此時,傳入的資料會像其他任何自訂 DOM 事件一樣運作,並可由應用程式中的任何其他物件擷取及處理。

var websocket = new WebSocket(serverIPAddress);

// rebroadcast incoming WebSocket messages with a global event via jQuery
websocket.onmessage = function(e) {
  if (e.data) {
    var obj = JSON.parse(e.data);
    $(document).trigger(data.type, obj);
  }
};

// broadcast outgoing WebSocket messages by passing in a native .js object
var broadcast = function(obj) {
  websocket.send(JSON.stringify(obj));
};

當兩部裝置同步遊戲代碼時,Roll It 的 WebSocket 伺服器會即時建立。Roll It 的後端是使用 Go 建構在 Google Compute EngineApp Engine 平台上。

傾斜選單畫面

除了在遊戲過程中使用的事件驅動 WebSocket 訊息外,您還可以透過傾斜手機和輕觸按鈕來確認選項,控制 Roll It 中的選單。這需要從手機傳輸到筆記型電腦的傾斜資料串流更為一致。為了減少頻寬並避免傳送不必要的更新,只有在裝置傾斜角度變化超過幾度時,系統才會傳送這些訊息。如果手機平放在桌上,就沒有必要傳送傾斜資料串流!傳輸速率也會受到限制,即使裝置正在傾斜,Roll It 每秒最多只會傳送 15 個 WebSocket 訊息。

電腦偵測到傾斜值後,就會使用 requestAnimationFrame 進行時間內插,以維持流暢的感受。最終結果是旋轉選單和滾動球,可協助顯示使用者所選項目。當手機傳送傾斜資料時,系統會在 requestAnimationFrame 迴圈中重新計算 CSS 轉換,即時更新這些 DOM 元素。選單的容器只是旋轉,但球似乎會沿著地板滾動。為了實現此效果,我們實作了一些基本三角函式,將球的 x 座標與其旋轉方向建立關聯。簡單的方程式如下:旋轉次數 = x / (直徑 * π)

總結

Roll It 是時代的象徵。從推動開發的開放原始碼專案、桌上型電腦和口袋裝置的處理能力,到網路作為平台的狀態,現在正是透過開放網路連線的令人興奮且具變革性的時刻。就在幾年前,這類技術大多只存在於專屬系統中,無法自由使用和散布。如今,我們每天都會創造及分享新的拼圖片段,讓複雜的體驗變得更容易實現,也更具想像力。還在等什麼?打造精彩內容,與全世界分享!

Roll it 標誌