建立擲骰子

Justin Gitlin
Justin Gitlin

Roll It 是一項 Chrome 實驗功能,能讓您直接在手機和電腦上使用瀏覽器,暢玩經典的木棧道遊戲。手機上的瀏覽器可讓你透過轉動手腕的方式將球瞄準並轉動球,電腦上的瀏覽器則可以透過 WebGL 和 Canvas 顯示 Roll Iteye 的即時圖形。這兩部裝置會透過 Websocket 進行通訊。沒有任何應用程式。沒有已下載的內容。沒有任何權杖。只要使用新式瀏覽器即可。

隨著 Google 廣告素材研究室的方向,Legwork 就開發了使用者體驗、介面和遊戲環境,隨後與開發合作夥伴 Mode Set 攜手合作,打造 Roll It。在整個專案期間,過程中有許多獨特的挑戰。本文將逐一介紹我們所使用的技術、發現的技巧,以及我們在製作 Roll It 的過程中學到的經驗。

3D 工作流程

一開始很難克服的困難之一,就是想找出將軟體中的 3D 模型整合到可連上網路的理想檔案格式。在 Cinema 4D 中建立資產後,模型即可簡化,並轉換成低多邊形網格。每個網格都會提供特定多邊形選取標記,以區分物體的一部分,用於上色和紋理。我們之後能夠匯出為 Collada 1.5 (.dae) 檔案,並匯入開放原始碼 3D 程式 Blender,以便製作適用於 3.js 的檔案。確認模型已正確匯入後,我們將網格匯出為 JSON 檔案,並使用程式碼套用照明。以下詳細說明我們採取的步驟:

在 C4D 內建立物件模型。確認網格的正常現象朝外。
建立 C4D 內的物件模型,確認網格的正常現象朝外。
使用多邊形選取工具,為您想紋理的特定區域建立選取標記。將材質套用至每個選取標記。
使用多邊形選取工具,為您想設定紋理的特定區域建立選取標記。將材質套用至每個選取標記。
將網格匯出為 COLLADA 1.5 .dae 檔案。
將網格匯出為 COLLADA 1.5 .dae 檔案。
確認已勾選 [匯出 2D 幾何圖形]。程式碼端的 3D 環境通常匯出三角形匯出範圍,但會使多邊形數量加倍。多邊形數量越高,在電腦處理器上模型的稅金就越多。如果發現成效緩慢,請不勾選這個選項。
確認已勾選「匯出 2D 幾何圖形」。程式碼端的 3D 環境通常匯出三角形匯出範圍,但會使多邊形數量加倍。多邊形數量越高,在電腦處理器上模型的稅金就越多。如果發現效能緩慢,請不勾選這個選項。
將 Collada 檔案匯入 Blender。
將 Collada 檔案匯入 Blender。
匯入至 Mixer 後,您會發現材料和選取標記也隨之移動。
匯入至 Mixer 後,您會發現材質和選取標記都已轉移完成。
選取您的物件,依照您偏好的方式調整物件的材質。
選取物件,依需求調整物件的材質。
將檔案匯出為 Three.js 檔案
將檔案匯出為 Three.js 檔案,以便與 webGL 相容。

編寫程式碼

Roll It 是採用開放原始碼的程式庫開發的,可在新型瀏覽器中以原生方式執行。透過 WebGL 和 WebSocket 等技術,網路將提供媲美主機等級的遊戲和多媒體體驗。隨著 HTML 開發技術日趨現代,開發人員在打造這些體驗時,操作起來輕鬆又方便,

開發環境

大多數的 Roll It 的原始程式碼是以 CoffeeScript 編寫而成,這種簡潔明瞭的語言可轉譯成格式正確且程式碼檢查的 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 是熱門 C++ 物理程式庫的 THREE.js 專用包裝函式,已轉譯為 JavaScript。我們利用這個程式庫模擬球體的滾動、跳躍和彈跳動作,可進行 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 通訊

在玩家拿著手機擲球後,系統會從手機發送訊息到筆記型電腦,通知該球開球。這則「捲動」訊息是透過兩部機器之間的 WebSocket 連線,透過 JSON 資料物件傳送,JSON 資料很小,主要由訊息類型、擲回速度和目標方向組成。

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

筆電和手機之間的所有通訊都是透過像這樣的小 JSON 訊息進行。每次遊戲在電腦上更新狀態,或是使用者傾斜或輕觸手機按鈕時,系統就會在電腦之間傳送 WebSocket 訊息。為了讓通訊過程簡單且方便管理,WebSocket 訊息是透過從任一瀏覽器進入的單一離開點來播送。相反地,接收的瀏覽器上只會有一個進入點,可透過一個 WebSocket 物件處理所有傳入和傳出的訊息。收到 WebSocket 訊息時,JSON 資料會使用 jQuery 的 trigger() 方法在 JavaScript 應用程式中重新播送。此時,傳入資料的行為與任何其他自訂 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 的後端是使用 GoGoogle Compute EngineApp Engine 平台上建構而成。

傾斜選單畫面

除了遊戲過程中以事件驅動的 WebSocket 訊息外,如要控制 Roll It 中的選單,只要傾斜手機並輕觸按鈕,即可確認選取項目。因此,從手機傳輸到筆電的資料必須更加穩定一致。為降低頻寬用量,並避免傳送不必要的更新,那麼只有在裝置的傾斜角度改變幾度時,系統才會傳送這些訊息。如果手機平放在桌子上,就不會傳送傾斜資料串流!傳輸速率也會受到節流限制;在 Roll It 中,每秒最多傳送 15 個 WebSocket 訊息 (即使裝置主動傾斜)。

在電腦上擷取傾斜值後,系統會使用 requestAnimationFrame 進行內插,確保平滑。最終結果會不斷旋轉的選單和球球,以協助指出使用者的選擇。手機傳送傾斜資料時,這些 DOM 元素會即時更新,方法是在 requestAnimationFrame 迴圈內重新計算 CSS 轉換。菜單的容器只會旋轉,但球看起來會沿著地板滾動。為了實現這種效果,我們實作一些基本的三角函數,藉以建立球數 x 座標與球體的旋轉之間的關係。簡單方程式為:旋轉 = x / (直徑 * π)

總結

擲骰子是時間的徵兆。無論是支持自家開發作業的開放原始碼專案、我們桌上的裝置處理能力,以及平台就像平台一樣,在開放網路環境中交流互動的體驗,都是可以帶來前所未有的絕佳時間。就在幾年前,這項技術大多只存在於專屬系統中,無法自由使用和發布。如今,我們每天都能創作並分享新謎題,只要減少工作量,就能擁有更多想像力。還在等什麼?打造優質內容,與全世界分享!

Roll It 標誌