1. 程式人生 > 實用技巧 >HTML5 GAME TUTORIAL(五): Create a smooth canvas animation(譯)

HTML5 GAME TUTORIAL(五): Create a smooth canvas animation(譯)

原文地址:Create a smooth canvas animation

在HTML5畫布上建立動畫,無論幀頻如何均可流暢執行。瞭解如何呼叫時間和緩動函式。在本教程結束時,您可以使用JavaScript建立基本的動畫。

建立web動畫

您在本教程中學習建立動畫的基礎知識是建立遊戲的關鍵組成部分,但是您也可以應用相同的原理來建立Web動畫。

過去,人們曾經使用Adobe Flash建立web動畫。它們常用於橫幅廣告。SWF檔案將利用Flash Player外掛在瀏覽器中執行。為了更多的裝飾目的,使用了GIF。您可以在此處閱讀有關web動畫的使用和歷史的更多資訊。

現在,隨著瀏覽器對Flash Player的最終支援,HTML5畫布和CSS動畫已被取代。

CSSJavaScript可通過操作HTML元素製作簡單的動畫。對於更復雜的動畫或遊戲,可以使用本教程中介紹的畫布技術。

現在,讓我們繼續建立第一個畫布動畫。

在畫布上動態繪製物件

在上一教程中,您學習瞭如何在畫布上重複繪製。但是使用的draw()函式非常靜態。它只會在同一位置一遍又一遍地繪製一個矩形。我們讓事情變得更有趣。將舊的draw()函式更改為這一新函式:

function draw() {
    context.fillStyle = '#ff8080';
    context.fillRect(rectX, rectY, 150, 100);
}

如您所見,繪製操作fillRect()現在使用變數引數作為輸入。如果更改rectX和rectY的值,則應該得到一個移動的矩形。

讓我們將其與遊戲迴圈混在一起。使用以下程式碼,您可以更新矩形的位置:

let rectX = 0;
let rectY = 0;

function gameLoop(timeStamp) {
    // Update game objects in the loop
    update();
    draw();

    window.requestAnimationFrame(gameLoop);
}

function update() {
    rectX += 1;
    rectY += 1;
}

何時更新遊戲物件的位置?

一個簡單的update()函式會稍微改變矩形的座標。在gameLoop()中呼叫update()函式的每一幀,這將使矩形移動。在draw()之前執行update()很重要,這樣您就可以始終將遊戲的最新狀態繪製到畫布上。

當您開始向遊戲迴圈新增其他任務時,請記住這一點。始終先更新遊戲物件的狀態,並作為最後的動作將所有內容繪製到螢幕上。現在,您的遊戲迴圈如下所示:

重複圖形造成的模糊

執行程式碼時,您應該期望看到矩形從左上角到右下角以斜線移動。這是您得到的:

這是怎麼回事?看起來像是矩形的無盡重複,造成了很大的模糊。該矩形是在畫布上繪製的,但是從不刪除前一個矩形。這是為什麼?

畫布是如何工作的?

好吧,畫布充當繪圖板的角色。您可以在其上繪畫,更改矩形的位置,然後再次繪製。足夠快地執行此操作,您將獲得運動感知

但是,當您忘記清除畫布時,您將在前一張圖的上方繪製每張圖。 像上面的示例一樣,使圖形模糊。

您可以利用這種效果來建立一些有趣的圖形,但這不是您現在所需要的。您要為矩形設定動畫,而不要建立模糊。

繪製之前清除畫布

要解決此問題,您必須在每次新的繪製操作之前清除畫布。 因此,每次繪製時,您都必須從頭開始。這樣可以防止模糊效果。

將clearRect()新增到draw()函式,始終在繪製之前清除畫布:

function draw() {
    // Clear the entire canvas
    context.clearRect(0, 0, canvas.width, canvas.height);

    context.fillStyle = '#ff8080';
    context.fillRect(rectX, rectY, 150, 100);
}

clearRect()函式清除畫布的一部分。在這種情況下,設定為清除覆蓋整個畫布的區域。從左上角0,0開始,繼續執行canvas.width和canvas.height。

有了這種新的清除方法,您的遊戲迴圈將如下所示:

當您執行新程式碼時,矩形將正確設定動畫。 請參閱以下示例:

動態幀率對動畫的影響

目前,動畫非常簡單,並且可以在每個裝置上流暢執行。但是,如果要是動畫複雜很多,例如執行帶有許多不同動畫物件的遊戲,該怎麼辦?

您的計算機或移動裝置可能無法跟上並延遲某些幀。稍後,當系統上的壓力較小時,它可能會再次加速。動畫的動作也將被阻止或加速,您不希望這種情況發生。無論幀速率如何,您都希望以恆定的速度移動物件,因此您的動畫在任何型別的硬體或裝置上始終看起來相同。

在執行較舊的遊戲時,您可以非常清楚地看到這種效果。這些遊戲無法補償不同的幀頻並經過專門設計 適用於時鐘速度較慢的舊硬體。當您使用現代硬體執行這樣的遊戲時,它將以超快的速度執行並達到很高的幀頻。 現在,每個動作都以閃電般的速度執行。當您按箭頭鍵移動時,您最終將在螢幕末端閃爍。

物件的移動速度似乎並未考慮幀速率。但是如何為自己的動畫或遊戲解決此問題?

處理動態幀率

為了填補動態幀頻的影響,您需要在動畫中包括時間作為因素。這樣,決定遊戲速度的不再是幀速率(和硬體),而是時間。 請參閱以下程式碼:

let secondsPassed = 0;
let oldTimeStamp = 0;
let movingSpeed = 50;

function gameLoop(timeStamp) {
    // Calculate how much time has passed
    secondsPassed = (timeStamp - oldTimeStamp) / 1000;
    oldTimeStamp = timeStamp;

    // Pass the time to the update
    update(secondsPassed);
    draw();

    window.requestAnimationFrame(gameLoop);
}

function update(secondsPassed) {
    // Use time to calculate new position
    rectX += (movingSpeed * secondsPassed);
    rectY += (movingSpeed * secondsPassed);
}

在gameLoop()開始時,計算經過的秒數。將此值傳遞給update()函式。 在那裡,它用於計算矩形的新位置。使用時間作為一個條件因素。

相對於時間移動

讓我們進一步解釋一下。當您的遊戲以60fps的速度執行時,每幀大約需要0.0167秒。這意味著當您要以每秒50畫素的速度移動物件時,必須將50乘以自上一幀以來經過的秒數。以60fps執行的遊戲將使物件每幀移動0.835畫素。這就是update()函式中發生的事情。

當幀頻增加或減少時,移動速度也將增加。無論經過了多少時間,您的物件都將始終以所需的速度移動。這使動畫更適合於具有不同幀速率的各種硬體。當然,較低的幀速率會使動畫看起來不連貫,但物件的位移保持不變。

跳過時間限制

在某些特殊情況下,物件始終以相同速度移動,移動校正將變得很大,最終將破壞您的遊戲。想象一下無限慢的硬體,您的移動速度將會很大,才能彌補上一幀與當前幀之間的時間差。您的物件必須每幀大步移動,以致於遊戲邏輯變得不穩定。

通過在瀏覽器選項卡中運行遊戲並切換到另一個隨機選項卡,您可以輕鬆複製此行為。當您切換回第一個標籤時,最後兩幀之間的時間非常長。

要解決此問題,您需要將時間因數限制為每幀的最大值。通過將下一個程式碼新增到遊戲迴圈中,您的遊戲在(遊戲)時間內的前進時間永遠不會超過0.1秒。 對於通常以60fps執行的遊戲,這仍然意味著您已經將6幀壓縮為1。您可以使用此數字進行遊戲以使其適合您的遊戲。

// Move forward in time with a maximum amount
secondsPassed = Math.min(secondsPassed, 0.1);

放緩動畫的運動

現在您可以將時間作為一個因素,可以在動畫中做一些有趣的事情。還記得update()函式在哪裡更新遊戲物件的位置嗎? 好吧,您可以通過將緩動到動畫使其變得更加有趣。

let timePassed = 0;

function update(secondsPassed) {

    timePassed += secondsPassed

    // Use different easing functions for different effects.
    rectX = easeInOutQuint(timePassed, 50, 500, 1.5);
    rectY = easeLinear(timePassed, 50, 250, 1.5);
}

// Example easing functions
function easeInOutQuint (t, b, c, d) {
    if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b;
    return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
}

function easeLinear (t, b, c, d) {
    return c * t / d + b;
}

這個簡單的例子使用了五次緩和並將其應用於x位置。y位置以線性方式更新。結果如下:

緩動函式可能看起來很難理解,但是好訊息是您實際上並不需要去了解它。只要您知道要傳遞哪些引數,就可以使用它們。所有這些都在緩動函式的動畫工具中進行了說明。

使用動畫工具可瞭解有關緩動的更多資訊以及將哪些引數傳遞給緩動函式。在update()函式中播放以建立新的移動效果。就目前而言,這一切都是簡單的。

下一步是什麼?

您設法以平滑的方式移動HTML5畫布上的動畫物件,並瞭解了幀速率對動畫的影響。您還學習瞭如何在動畫中包括時間作為因素並使用緩動函式。隨時問評論中的任何問題。

在本教程的下一步中,您將學習如何建立多個物件,檢測碰撞以及使物件彼此互動。