JS+Canvas實現接球小遊戲的示例代碼
寫在最前
看瞭canvas的動畫系列,已經抑制不住內心的沖動想寫個小遊戲瞭,還是那個套路——多寫寫,你才能瞭解它。加上這兩天下班後我都沒有機會去摸摸籃球,所以就寫瞭個接球的小遊戲(準確的說不能叫遊戲,太簡單瞭,叫動畫吧…)。 都是一些基礎的實現,有時間你也可以試試,廢話說到這裡,我們開始吧。
git地址
https://github.com/ry928330/ballGame.git
成果展示
實現思路
這裡我們采用疑問的句式給出實現的思路(步驟),因為我寫這個demo的時候也是這樣去想的:
– canvas上畫一個小球,如何動起來?
– canvas上畫一個橫條兒,如何像在DOM上那樣去拖動它?
– 如何實現遊戲上面的小方塊兒的繪制與刪除?
– 碰撞問題:小球與canvas邊界之間怎麼去判斷碰撞?小球與小橫條之間怎麼去判斷碰撞?小球與上方的小方塊兒之間怎麼去判斷碰撞?(其實原理差不多)
主要就是這4步的實現,然後把他們串接起來,你的簡單接球小動畫就實現瞭。
詳細說明
如何讓小球動起來
其實這部分是比較簡單的canvas動畫,一個基本的動畫步驟可以歸納為以下幾個過程:
1.清除canvas:在每次畫新的圖形的時候,你都必須將之前的圖形給清理掉,這樣才有那種一幀一幀的重繪的感覺。
2.保存當前的state:在你沒走一步時,你都需要將你當前的canvas狀態給保存下來,狀態包括:當前圖形的位置(x,y軸的信息),當前圖形的大小(寬高信息),當前圖形的變化(也就是你對他做瞭拉伸,角度變化等等)等信息。
3.重新渲染你在當前位置所要繪制的圖形,也就是把你現在想畫的東西給它畫在你的canvas上。
4.恢復canvas的狀態,因為你之間對canvas的信息做瞭入棧保存,所以此時你必須restore它。
你以為這樣就可以瞭嗎?哈哈哈,並沒有。加入我們把上面的四個步驟封裝在一個名字叫draw的函數中,這時要讓繪制的圖形動起來,還需要借住下面三個函數之一:
setInterval(function, delay),setTimeout(function, delay),requestAnimationFrame(callback)
相信學習前端的小朋友對前兩個都不陌生,我就不說瞭,我說說後面這個函數,之前我也沒接觸這個函數,而且該次demo用的就是這個函數:requestAnimationFrame函數會告訴瀏覽器你希望執行動畫,並請求瀏覽器調用指定的函數(也就是你傳入的回調函數)在下一次重繪之前更新動畫。如果你想做逐幀動畫的時候,你應該用這個方法,所以當你把draw函數作為會調傳入requestAnimationFrame,你的draw函數就會不斷的執行。
貼下我的部分代碼:
//ball 對象用來存儲一個球 var ball = { x: 150, y: 200, vx: 5, //水平速度 vy: 5, //垂直速度 radius: 20, color: 'blue', draw: function() { ctx.beginPath(); ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true); ctx.closePath(); ctx.fillStyle = this.color; ctx.fill(); } }; function draw() { ctx.clearRect(0,0, canvas.width, canvas.height); ball.draw(); bar.draw(); scores.draw(); targetRectangle.draw(); ball.x += ball.vx; ball.y += ball.vy; animationJudge = window.requestAnimationFrame(draw); ... }
draw()函數裡面包含瞭小球的繪制,小橫條兒的繪制,得分的繪制。 在每次繪制之前都會清理下整個canvas,而且講各自的狀態保存在瞭各自命名的對象中,小球會通過水平和垂直速度不斷改變它x和y的值來改變他的位置。也就形成瞭運動的小球。
繪制小橫條兒,怎麼像操作DOM一樣去拖動它
在canvas上是沒法兒那麼自如的操作DOM元素,但是我們卻能對canvas本身進行事件監聽,拿到位置信息。所以跟DOM中拖拽的實現類似,在鼠標移動過程中不斷的統計移動的距離,然後改變橫條兒的位置, 重新繪制它來達到拖動的效果,照例貼代碼:
canvas.addEventListener('mousedown', function(e) { if (!beginGame) { draw(); beginGame = true; } var x = e.clientX; var y = e.clientY; //判斷拖拽的位置 if ((x >=bar.x && x <= bar.x + bar.width) && (y >= bar.y && y <= bar.y + bar.height) ) { bar.barDragJudge = true; bar.xDistance = x; } }) canvas.addEventListener('mousemove', function(e) { if (bar.barDragJudge) { var x = e.clientX; var distance = x - bar.xDistance; if (bar.x + distance + 60 >= canvas.width) { if (distance >= 0) { distance = 0; } } else if (bar.x + distance <= 10) { if (distance <= 0) { distance = 0; } } bar.x += distance; bar.xDistance = x; } }) window.addEventListener('mouseup', function(e) { bar.barDragJudge = false; })
監聽mousedown事件,當鼠標按下並且鼠標位置是和橫條所覆蓋的位置重合(當然在我們開始遊戲後,會繪制一次橫條兒,即頁面相應位置會出現橫條兒)時,我將拖拽標志barDragJudge設置為true,表示可以進行拖拽瞭。然後在鼠標移動過程中通過計算鼠標移動的距離更新橫條兒的位置,完成橫條的拖動,並且判斷當橫條移動到canvas邊界之後不能左拖和右拖。最後,結束點擊,將barDragJudge設置為false。
如何實現遊戲上面的小方塊兒的繪制與刪除?
其實,光是繪制小方塊兒並不能難,你就寫一個二維數組,存儲你要繪制的矩形方塊兒的信息,貼下我的代碼:
function initialTargetRectangleArr() { var targetRectangleArr = []; for (var i = 0; i < 4; i++) { targetRectangleArr[i] = []; for (var j = 0; j < 4; j++) { targetRectangleArr[i][j] = {}; targetRectangleArr[i][j].x = 35 + j*(50 + 10); targetRectangleArr[i][j].y = 35 + i*(20 + 10); targetRectangleArr[i][j].width = 50; targetRectangleArr[i][j].height = 20; } } return targetRectangleArr; }
上面的函數返回瞭一個二維數組,數組裡面的元素是對象,每個對象包含瞭你存儲的小方塊兒的位置以及大小。
接下來說下小方塊兒的刪除,這裡我們先假設小球碰到瞭我們的小方塊兒,碰到之後我們需要講該方塊兒擦除掉,
即借用clearRect函數,然後問題來瞭,你隻是降頁面的方塊兒擦除掉瞭,但是方塊兒還是在的,這時我采取的辦法是降方塊兒"移出"我的canvas,然後將他的寬高都設置為0。
碰撞問題
這個是本次DEMO的關鍵問題瞭,該如何去判斷小球和各種東西之間的碰撞呢?我們這裡拿小球和底部小橫條兒的碰撞來說明下。 其實碰撞的核心在於位置,你要準確的拿到橫條兒在canvas中的覆蓋區域,然後小球在進入這個區域後就當作是發生瞭碰撞。照理,貼下代碼(關鍵部分就一句話):
if ((ball.x + ball.vx >= bar.x - ball.radius && ball.x + ball.vx - ball.radius <= bar.x + bar.width) && (ball.y + ball.vy + ball.radius >= bar.y)) { ball.vy = -ball.vy; }
我們稍微來解析下,(ball.x + ball.vx >= bar.x – ball.radius && ball.x + ball.vx – ball.radius <= bar.x + bar.width) 這部分主要判斷兩個事:&&號左邊是判斷小球落在橫條兒左邊時,小球的右邊緣必須在橫條所覆蓋的區域;&&號右邊是判斷小球落在橫條右邊時,其左側必須在橫條覆蓋的范圍內。接著在滿足上面這個條件下,(ball.y + ball.vy + ball.radius >= bar.y)判斷小球的高度,球心加上當前小球的垂直移動速度以及半徑,如果高度值大於等於橫條的垂直高度則碰撞發生。怎麼樣,其實並沒有想象中的那麼難吧,至於小球和canvas邊緣以及小球和上面的小方塊兒碰撞原理和這裡一樣,就不再贅述。
關於計分以及碰到紅色小塊兒加速問題都是比較簡單的,可以直接看我的代碼,寫到這裡整個demo差不多就實現瞭吧。
寫在最後
剛想到寫這個demo的時候還感覺有點困難,主要就是想到碰撞可能不好實現。直到完成後,誒,其實也挺簡單的哈。各位大佬如果有時間玩兒玩兒這個小demo的話(現在隻能從git上靠代碼,自己在頁面玩兒),如果存在什麼bug之類的歡迎指出哈。(其實我有點擔心碰撞邊界的問題,設置不好會不會有什麼bug出現)接下來,我想試試難度升級,如果有多個小球呢,不停的重繪 會不會導致頁面卡頓,恩,當然,這是後話瞭。
到此這篇關於JS+Canvas實現接球小遊戲的示例代碼的文章就介紹到這瞭,更多相關JS Canvas接球遊戲內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!