天天看點

如何通過 JavaScript 編寫一個遊戲主循環

“遊戲主循環”是一種能夠随時間改變狀态的用于渲染動畫和遊戲的技術。它的核心是一個盡可能頻繁地運作的方法,來接收使用者輸入,更新随時間改變的狀态,然後繪制目前幀。

在這篇短文中你将了解這些基礎技術是如何工作的,并且可以自己制作出基于浏覽器的遊戲和動畫。

JavaScript 中的“遊戲主循環”看起來像這樣:

requestAnimationFrame 方法請求浏覽器在下一次重繪之前盡可能快地調用特定的方法。它是渲染動畫專用的 API,但你也可以用 setTimeout 方法設定一個短的逾時時間來達到相似的效果。當回調函數開始觸發時,requestAnimationFrame 傳入一個時間戳作為參數,它包含從視窗加載到現在的毫秒數,等價于 performance.now()。

progress 值,或者說每次渲染的時間間隔對于建立流暢的動畫是至關重要的。我們通過它來調整 update 方法中的 x 軸和 y 軸的位置,保證動畫以穩定的速度運動。

更新位置

我們的第一個動畫簡單到不行。一個紅色的方塊向右移動直到碰到畫布的邊緣,然後回到起始位置。

我們需要存儲方塊的位置,以及在 update 方法中 x 軸位置的增量。當到達邊界時我們可以減掉畫布的寬度來讓它回到起點。

繪制新一幀

本例使用 <canvas> 元素來渲染圖像,不過遊戲主循環也可以結合其他輸出,比如 HTML 或者 SVG 來使用。

draw 方法簡單地渲染遊戲世界的目前狀态。每一幀我們都要清空畫布,然後在state 對象中儲存的位置上重新畫一個 10px 的紅方塊。

然後我們就發現它動起來了!

在 SitePoint 的 CodePen 可以檢視示例:Game Loop in JavaScript: Basic Movement。

注:在這個例子中你可能會注意到畫布的大小是通過 CSS 和 HTML 元素的 width, height 屬設定的。CSS 樣式設定了畫布在頁面繪畫的真實尺寸,而 HTML 屬性則設定了畫布 API 需要用到的坐标系或者網格的大小。看看 Stack Overflow 上的這個問題來了解更多。

響應使用者輸入

下面我們要擷取鍵盤輸入來控制對象的位置,state.pressedKeys 會追蹤使用者按下了哪一個鍵。

我們監聽所有的 keydown 和 keyup 事件,并且同步更新 update.pressedKeys。我用 D 鍵作為向右方向,A 為左,W 為上,S 為下。你可以在這裡找到鍵盤碼清單。

然後我們就隻需要根據按下的鍵來更新 x軸 和 y軸 的值,并保證對象在邊界以内。

現在我們就可以響應使用者輸入了!

在 SitePoint的 CodePen 可以檢視示例:Game Loop in Javascript: Dealing with User Input。

行星遊戲

既然現在我們已經掌握了基本原理,那麼就可以做些更有意思的事了。

做一艘看起來像經典遊戲“行星”裡的飛船其實一點都不複雜。

state 對象需要額外存儲一個向量(一個 x、y 對)用來移動,還要儲存一個 rotation 值來标記飛船的方向。

update 方法需要做三件事:

根據左右鍵更新方向(rotation)

根據上下鍵和方向更新移動向量(movement)

根據移動向量和畫布邊界更新對象位置(position)

draw 方法在繪制箭頭之前會移動并轉動畫布的原點。

這就是我們需要重建類似“行星”遊戲飛船的所有代碼。本例的操作按鍵和前面那個完全一樣(D鍵向右,A 向左,W向上,S 向下)

在 SitePoint的 CodePen 可以檢視示例:Game Loop in JavaScript: Recreating Asteroids。

添加行星、子彈和碰撞監測的工作就交給你了~

更新

如果你對本文很感興趣,那你肯定會喜歡閱讀這篇《Mary Rose Cook live-code Space Invaders form scrach》來看一個更複雜的例子。雖然是發表于幾年前,但它是一篇介紹開發浏覽器遊戲的非常棒的文章。