在線演示地址 http://200ok.fun:3100/html/game_demo.htmlhtml
繼上次js canvas遊戲初級demo-上下左右移動(https://www.cnblogs.com/lzs-888/p/7427440.html),以後,對其新加了矩形下落和碰撞檢測功能canvas
1.頭像移動
設置兩個按鍵監聽事件(keydown - 按下,keyup - 鬆開),每當按下時就將按下的鍵的state改成true,鬆開則相反,
而後,設置了一個定時器,每必定時間會讀取這些按鍵state,根據state和速度進行頭像位置座標的調整,而且擦除頭像,從新畫上dom
2.矩形下落
與頭像移動相似,只是擦除和繪製有多個(本代碼中用設置多個定時器來維護這些矩形的繪製),而且橫座標和下落速度在必定範圍內隨機函數
3.碰撞檢測
這個直接看代碼,本代碼寫的有點囉嗦spa
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>按鍵盤的上下左右躲開這些黑色障礙物</title> </head> <style> #canvas_dom{border: 1px solid #000;margin: 0 auto;display: block;background: #fff;} .title{text-align: center;} #game_time{text-align: center;} </style> <body> <h1 class="title">按鍵盤的上下左右躲開這些黑色障礙物</h1> <h2 id="game_time">你堅持的時間:<span>0</span>S</h2> <canvas id="canvas_dom"></canvas> <script> /* ddmm 2018/07/13 1.頭像移動 設置兩個按鍵監聽事件(keydown - 按下,keyup - 鬆開),每當按下時就將按下的鍵的state改成true,鬆開則相反, 而後,設置了一個定時器,每必定時間會讀取這些按鍵state,根據state和速度進行頭像位置座標的調整,而且擦除頭像,從新畫上 2.矩形下落 與頭像移動相似,只是擦除和繪製有多個(本代碼中用設置多個定時器來維護這些矩形的繪製),而且橫座標和下落速度在必定範圍內隨機 3.碰撞檢測 這個直接看代碼,本代碼寫的有點囉嗦 */ //全局變量 var g = { ctx: null,//畫布會話上下文 itv_ids: [],//用於記錄定時器id,便於一塊兒clear掉,減小內存消耗 game_over: false,//遊戲是否結束 refreshNumForSec: 60,//每秒畫面刷新次數 game_time: 0, //遊戲堅持的時間 canvasWidth: 1000,//畫布寬度 canvasHeight: 700,//畫布高度 faceDataUrl: 'http://www.200ok.fun:3100/images/wudier.png',//頭像地址 img: null,//頭像圖片引用 faceWidth: 80, faceHeight: 82.5, faceX: 0,//頭像位置x座標(其初始值會在首次加載被initFacePos方法計算得出,故設置是無效的) faceY: 0,//頭像位置y座標(其初始值會在首次加載被initFacePos方法計算得出,故設置是無效的) faceLastX: 0,//頭像上一次位置x座標,用於擦除(其初始值會在首次加載被initFacePos方法計算得出,故設置是無效的) faceLastY: 0,//頭像上一次位置y座標,用於擦除(其初始值會在首次加載被initFacePos方法計算得出,故設置是無效的) keyRight: false,//是否按了→ keyLeft: false,//是否按了← keyUp: false,//是否按了↑ keyDown: false,//是否按了↓ faceSpeed: 15,//頭像每次刷新畫布移動的像素 rectW: 200,//矩形的寬度 rectH: 20,//矩形的高度 rectSpeedMin: 3,//方塊速度最小值 rectSpeedMax: 15,//方塊速度最大值 rectFlowNumForSec: 1,//每秒生成矩形個數 }; _main();//主函數 //獲取畫布上下文 function getCtx(){ if(!g.ctx){ //獲取dom var canvas = document.querySelector('#canvas_dom'); //設置寬高 canvas.width = g.canvasWidth; canvas.height = g.canvasHeight; //獲取會話上下文 g.ctx = canvas.getContext('2d'); } return g.ctx; } function _main(){ getCtx();//獲取畫布上下文 //獲取頭像圖片 g.img = new Image(); g.img.src = g.faceDataUrl; g.img.width = g.faceWidth; g.img.height = g.faceHeight; g.img.onload = function(){//圖片加載完成 g.img.style.border = "1px solid #000"; initFacePos(); } //監聽keydown事件 window.addEventListener('keydown',function(e){ var k = e.key; stateJudge(k,true);//修改按鍵state }); //監聽keyup事件 window.addEventListener('keyup',function(e){ var k = e.key; stateJudge(k,false);//修改按鍵state }); //設置定時器,1秒重繪 g.refreshNumForSec 次頭像 g.itv_ids.push( setInterval(function(){ if(!g.game_over){ moveJudge(); //擦除上一次畫的 g.ctx.clearRect(g.faceLastX,g.faceLastY,g.img.width,g.img.height); //繪製以前保存一下位置,用於下次擦除 g.faceLastX = g.faceX; g.faceLastY = g.faceY; //繪製頭像 g.ctx.drawImage(g.img,g.faceX,g.faceY,g.img.width,g.img.height); } },1000/g.refreshNumForSec) ); //每1秒生成一個隨機位置,隨機速度的矩形往下掉 g.itv_ids.push( setInterval(function(){ var max = g.canvasWidth - g.rectW;//畫布寬度 - 矩形寬度 var x = get_random_num(0,max);//獲取x座標隨機值 var speed = get_random_num(g.rectSpeedMin,g.rectSpeedMax);//獲取速度隨機值 drawTheRect(x,0,g.rectW,g.rectH,speed); },1000/g.rectFlowNumForSec) ); //數秒,改dom g.itv_ids.push( setInterval(function(){ g.game_time++; document.querySelector('#game_time span').innerText = g.game_time; },1000) ); } //根據畫布和頭像寬高,初始化頭像位置,將其畫於底部中間位置 function initFacePos(){ g.faceX = g.canvasWidth/2 - g.img.width/2; g.faceY = g.canvasHeight - g.img.height; g.faceLastX = g.faceX; g.faceLastY = g.faceY; g.ctx.drawImage(g.img,g.faceX,g.faceY,g.img.width,g.img.height);//繪製頭像 } /** * [checkRectImpact 碰撞檢測,若是所提供的座標點碰撞則返回true,不然返回false] * - - 沒看過別人怎麼寫的,寫的有點囉嗦 * x1 x2 y1 y2 分別爲第一個矩形(下稱矩形A)的左邊橫座標,右邊橫座標,上邊縱座標,下邊縱座標 * a1 a2 b1 b2 分別爲第二個矩形(下稱矩形B)的左邊橫座標,右邊橫座標,上邊縱座標,下邊縱座標 * 座標點以下圖所示 x1 x2 ———————— y1 | | | | | | ———————— y2 a1 a2 ———————— b1 | | | | | | ———————— b2 */ function checkRectImpact(x1,x2,y1,y2,a1,a2,b1,b2){ //分別爲 左上角碰到 || 右上角碰到 || 右下角碰到 || 左下角碰到 || 矩形B大於矩形A且矩形B從下至上包含矩形A穿過 || 矩形B大於矩形A且矩形B從上至下包含矩形A穿過 || 矩形A大於矩形B且矩形A從下至上包含矩形B穿過 || 矩形A大於矩形B且矩形A從上至下包含矩形B穿過 return (x1 <= a1 && x2 >= a1 && y1 <= b1 && y2 >= b1) || (x1 <= a2 && x2 >= a2 && y1 <= b1 && y2 >= b1) || (x1 <= a2 && x2 >= a2 && y1 <= b2 && y2 >= b2) || (x1 <= a1 && x2 >= a1 && y1 <= b2 && y2 >= b2) || (x1 >= a1 && x2 <= a2 && y2 >= b1 && y1 <= b1) || (x1 >= a1 && x2 <= a2 && y2 >= b2 && y1 <= b2) || (a1 >= x1 && a2 <= x2 && b2 >= y1 && b1 <= y1) || (a1 >= x1 && a2 <= x2 && b2 >= y2 && b1 <= y2); } //獲取某個某個區間內的隨機整數 ,獲取到的值域爲[min,max) function get_random_num(min,max){ if(/^-?\d+$/.test(min) && /^-?\d+$/.test(max) && max>min){ return parseInt(Math.random()*(max - min) + min); }else{ return false; } } /** * [drawTheRect 畫矩形] * @param {[type]} x [矩形距離左邊框的距離] * @param {[type]} y [矩形距離上邊框的距離] * @param {[type]} w [矩形的寬度] * @param {[type]} h [矩形的高度] * @param {[type]} speed [每一次重繪畫面,矩形下墜的距離,即下墜速度] * @return {[type]} [description] */ function drawTheRect(x,y,w,h,speed){ var itv_id = setInterval(function(){ if(!g.game_over){ //繪製以前保存一下位置,用於下次擦除 var lastX = x; var lastY = y; // var speed = get_random_num(3,120); //經G民同窗提示,能夠考慮每次重繪方塊時給其變速 y += speed; g.ctx.clearRect(lastX,lastY,w,h); //擦除 g.ctx.fillRect(x,y,w,h); //畫 if(checkRectImpact(g.faceX,g.faceX + g.img.width,g.faceY,g.faceY + g.img.height,x,x+w,y,y+h)){//碰撞檢測 game_over(); return; } if(y > g.canvasHeight){//若是已經超過了畫布的高度那麼移除這個定時器,減小內存消耗 clearInterval(itv_id); } } },1000/g.refreshNumForSec); g.itv_ids.push(itv_id);//存到全局變量 } //遊戲結束 function game_over(){ clearAllInterval(); g.game_over = true; setTimeout(function(){//若是不延遲可能會致使上一次繪畫未完成就被阻塞了 if(confirm("遊戲結束,你堅持了" + g.game_time + "S" + ",繼續努力!\n從新開始遊戲請點'是'")){ window.location.reload(); } },50); } //clear全部定時器 function clearAllInterval(){ for(var i in g.itv_ids){ r = g.itv_ids[i]; clearInterval(r); } } //根據按鍵狀態修改頭像的座標 function moveJudge(){ if(g.keyRight === true){ g.faceX += g.faceSpeed; }else if(g.keyLeft === true){ g.faceX -= g.faceSpeed; }else if(g.keyUp === true){ g.faceY -= g.faceSpeed; }else if(g.keyDown === true){ g.faceY += g.faceSpeed; } //邊界狀況處理,不要讓頭像超出邊界 if(g.faceX > g.canvasWidth - g.img.width){ g.faceX = g.canvasWidth - g.img.width; }else if(g.faceX < 0){ g.faceX = 0; } if(g.faceY > g.canvasHeight - g.img.height){ g.faceY = g.canvasHeight - g.img.height; }else if(g.faceY < 0){ g.faceY = 0; } } //根據按鍵修改狀態 function stateJudge(k,v){ if(k == 'ArrowRight'){ g.keyRight = v; }else if(k == 'ArrowLeft'){ g.keyLeft = v; }else if(k == "ArrowUp"){ g.keyUp = v; }else if(k == "ArrowDown"){ g.keyDown = v; } } </script> </body> </html>