FlappBird——canvas
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>FlappBird</title> <style type="text/css"> html,body{margin:0;padding:0;} #canvas{ display: block; margin:0 auto; background-color: gainsboro; } </style> </head> <body> <canvas id="canvas"></canvas> <script src="script/Background.js"></script> <script src="script/Bird.js"></script> <script src="script/Land.js"></script> <script src="script/Pipe.js"></script> <script src="script/SceneManager.js"></script> <script src="script/Game.js"></script> <script type="text/javascript"> let game = new Game(); </script> </body> </html>
class Background{ constructor(){ /** @type {HTMLCanvasElement} */ this.w = game.allImg["bg_day"].width; this.h = game.allImg["bg_day"].height; this.x = 0; this.speed = 1; } //更新 update(){ this.x -= this.speed; this.x <= -this.w ? this.x=0 : null; } //渲染 render(){ game.draw.fillStyle="#4ec0ca"; game.draw.fillRect(0,0,game.canvas.width,game.canvas.height); game.draw.drawImage(game.allImg["bg_day"],this.x,game.canvas.height-this.h); game.draw.drawImage(game.allImg["bg_day"],this.x+this.w,game.canvas.height-this.h); game.draw.drawImage(game.allImg["bg_day"],this.x+this.w*2,game.canvas.height-this.h); } } class Land{ constructor(){ /** @type {CanvasRenderingContext2D} */ this.w = game.allImg["land"].width; this.h = game.allImg["land"].height; this.x = 0; this.y = game.canvas.height - this.h; this.speed = 1; } update(){ this.x -= this.speed; this.x < -this.w ? this.x=0 : null } render(){ game.draw.drawImage(game.allImg["land"],this.x,this.y) game.draw.drawImage(game.allImg["land"],this.x+this.w,this.y) game.draw.drawImage(game.allImg["land"],this.x+this.w*2,this.y) } } class Bird{ constructor(){ this.w = game.allImg["bird0_0"].width; this.h = game.allImg["bird0_0"].height; this.x = game.canvas.width/2 - this.w/2; this.y = game.canvas.height * (1-0.618) - 50; this.wing = 0; this.status = "drop";//up this.changY = 0; this.rotate = 0; } update(){ if(this.status == "drop"){ this.changY += 0.5; this.y += this.changY; this.rotate += 0.08; }else if(this.status == "up"){ this.changY -= 0.6; this.changY <= 0 ? this.status = "drop" : null; this.y -= this.changY; this.y <= 0 ? this.y = 0 : null; this.wing >= 2 ? this.wing = 0 : (game.frame%4==0 ? this.wing++ : null); } //34*24,小鸟上下左右四个值 this.x1 = this.x - 17; this.x2 = this.x + 17; this.y1 = this.y - 12; this.y2 = this.y + 12; if(this.y >= game.canvas.height - game.allImg["land"].height){ // this.y = game.canvas.height - game.allImg["land"].height; game.SM.enter(3); } } render(){ game.draw.save();//包裹一下,只对里面的图起作用 //下落旋转,更改中心点,坐标改为图片宽高负数的一半 game.draw.translate(this.x,this.y); game.draw.rotate(this.rotate); game.draw.drawImage(game.allImg["bird0_" + this.wing],-this.w/2,-this.h/2); game.draw.restore(); } fly(){ this.changY = 6; this.rotate = -1.2; this.status = "up"; } } class Pipe{ constructor(){ this.w = game.allImg['pipe_down'].width; this.h = game.allImg['pipe_down'].height; this.h1 = Math.round(Math.random()*200+100);//上面管子截取高度 this.space = 140;//空隙 this.h2 = game.canvas.height - game.allImg["land"].height - this.h1 - this.space;//下面管子截取高度 this.x = game.canvas.width; this.speed = 1; this.done = true; //每new一个管子放进数组中 game.pipeArr.push(this); } update(){ this.x -= this.speed; //销毁没用的管子 for(let i=0;i<game.pipeArr.length;i++){ if(game.pipeArr[i].x <= -this.w){ game.pipeArr.splice(i,1); i--; } } //管子的上下左右 this.x1 = this.x; this.x2 = this.x + this.w; this.y1 = this.h1; this.y2 = this.h1 + this.space; //碰撞检测 if(game.bird.x2 >= this.x1 && game.bird.x1 <= this.x2 && (game.bird.y1 <= this.y1 || game.bird.y2 >= this.y2)){ // clearInterval(game.timer) game.SM.enter(3); } if(this.done && game.bird.x1 > this.x2){ game.score++; this.done = false; } } render(){ game.draw.drawImage(game.allImg["pipe_down"],0,this.h-this.h1,this.w,this.h1,this.x,0,this.w,this.h1); game.draw.drawImage(game.allImg["pipe_up"],0,0,this.w,this.h2,this.x,this.h1+this.space,this.w,this.h2); } }
class SceneManager{ constructor(){ game.bg = new Background();//实例化 game.land = new Land(); this.bindEvent(); } enter(number){ switch (number){ case 0://第1场景 this.titleW = game.allImg["title"].width; this.titleX = game.canvas.width/2 - this.titleW/2; this.titleY = -50; this.btnX = game.canvas.width/2 - game.allImg["button_play"].width/2; this.btnY = game.canvas.height; this.birdX = game.canvas.width/2 - game.allImg["bird0_0"].width/2; this.birdY = 220; this.valve = true; this.birdChangeY = 2; // game.scene = 0; break; case 1: game.scene = 1; this.tutorialW = game.allImg["tutorial"].width; this.tutorialX = game.canvas.width/2 - this.tutorialW/2; this.tutorialY = 250; this.alpha = 1 this.birdChangeA = 0.03; break; case 2: game.scene = 2; game.bird = new Bird(); game.pipeArr = []; break; case 3: game.scene = 3; this.isBoom = false; this.index = 0; break; case 4: game.scene = 4; let arr = JSON.parse(localStorage.getItem("FB")); if(arr.length!=0){ if(game.score == 0){ this.model = "medals_0"; }else{ for(let i=0;i<=arr.length;i++){ if(!arr[0]&&game.score >= arr[0]){ this.best = game.score; arr[0] = game.score; this.model = "medals_1"; }else if(!arr[1]&&game.score >= arr[1]){ arr[1] = game.score; this.model = "medals_2"; }else if(!arr[2]&&game.score >= arr[2]){ arr[2] = game.score; this.model = "medals_3"; }else{ this.model = "medals_0"; } } } }else{ arr[0] = game.score; this.model = "medals_1"; } this.best = arr[0]; localStorage.setItem("FB",JSON.stringify(arr)); this.overX = game.canvas.width/2 - game.allImg["game_over"].width/2; this.overY = -80; this.panelX = game.canvas.width/2 - game.allImg["score_panel"].width/2; this.panelY = game.canvas.height; break; } } updateAndRender(){ switch (game.scene){ case 0://第1场景 game.bg.render();//渲染 game.land.render(); this.titleY >= 160 ? this.titleY = 160 : this.titleY += 5; this.btnY <= 400 ? this.btnY = 400 : this.btnY -= 10; // if(this.valve){ // this.birdY > 350 ? this.valve = false : this.birdY += 3; // }else{ // this.birdY < 220 ? this.valve = true : this.birdY -= 3; // } if(this.birdY > 300 || this.birdY < 220){ this.birdChangeY *= -1 } this.birdY += this.birdChangeY; game.draw.drawImage(game.allImg["title"],this.titleX,this.titleY); game.draw.drawImage(game.allImg["button_play"],this.btnX,this.btnY); game.draw.drawImage(game.allImg["bird0_0"],this.birdX,this.birdY); break; case 1: game.bg.render(); game.land.render(); game.draw.drawImage(game.allImg["bird0_0"],game.canvas.width/2-24,150); game.draw.save(); if(this.alpha > 1 || this.alpha < 0.3){ this.birdChangeA *= -1 } this.alpha += this.birdChangeA; game.draw.globalAlpha = this.alpha; game.draw.drawImage(game.allImg["tutorial"],this.tutorialX,this.tutorialY); game.draw.restore(); break; case 2: game.bg.update(); game.bg.render(); game.land.update(); game.land.render(); if(game.frame%180 == 0){ new Pipe(); } game.pipeArr.forEach((item)=>{ item.update(); item.render(); }) game.bird.update(); game.bird.render(); this.scoreRender(); break; case 3: game.bg.render(); game.land.render(); game.pipeArr.forEach((item)=>{ item.render(); }) if(this.isBoom){ this.index++; if(this.index > 8){ this.enter(4); return; } game.draw.drawImage(game.allImg["baozha" + this.index],game.bird.x-50,game.bird.y-50,100,100) }else{ game.bird.y += 5; if(game.bird.y >= game.canvas.height - game.allImg["land"].height) this.isBoom = true; game.bird.render(); } break; case 4: game.bg.render(); game.land.render(); this.overY >= 160 ? this.overY = 160 : this.overY+=5; this.panelY <= 250 ? this.panelY = 250 : this.panelY-=10; game.draw.drawImage(game.allImg["game_over"],this.overX,this.overY); game.draw.drawImage(game.allImg["score_panel"],this.panelX,this.panelY); game.draw.drawImage(game.allImg[this.model],this.panelX+32,this.panelY+44); game.draw.fillStyle = "#666"; game.draw.font = "20px consolas"; game.draw.textAlign = "right"; game.draw.fillText(game.score,(game.canvas.width/2)+90,this.panelY+50); game.draw.fillText(this.best,(game.canvas.width/2)+90,this.panelY+96); break; } } bindEvent(){ game.canvas.onclick = (e)=>{ switch (game.scene){ case 0://第1场景 if(e.offsetX >= this.btnX && (e.offsetX <= this.btnX + 116) && e.offsetY >= this.btnY && (e.offsetY <= this.btnY + 70)){ this.enter(1); } break; case 1: this.enter(2); break; case 2: game.bird.fly(); break; case 3: break; case 4: this.enter(1); break; } } } scoreRender(){ let str = game.score.toString(); let line = game.canvas.width/2 - str.length*30/2; for(let i=0;i<str.length;i++){ game.draw.drawImage(game.allImg["shuzi" + str[i]],i*30+line,100) } } }
class Game{ constructor(){ /** @type {HTMLCanvasElement} */ this.canvas = document.getElementById("canvas"); //创建一个绘制环境 this.draw = this.canvas.getContext("2d"); let W = document.documentElement.clientWidth > 420 ? 420 : document.documentElement.clientWidth; let H = document.documentElement.clientHeight > 750 ? 750 : document.documentElement.clientHeight; this.canvas.width = W; this.canvas.height = H; this.timer = null; this.frame = 0; this.score = 0;//分数 this.scene = 0;//记录场景编号 if(!localStorage.getItem("FB")){ localStorage.setItem("FB","[]") } this.imgLoad(); // this.bindEvent(); }; //清屏 clear(){ this.draw.clearRect(0,0,this.canvas.width,this.canvas.height); }; //开始 start(){ this.SM = new SceneManager(); this.SM.enter(this.scene) /* this.bg = new Background();//实例化 this.land = new Land(); this.bird = new Bird(); this.pipeArr = []; */ this.timer = setInterval(()=>{ this.frame++; this.clear(); this.SM.updateAndRender(); /* this.clear(); this.bg.update();//更新 this.bg.render();//渲染 this.land.update(); this.land.render(); if(this.frame%180 == 0){ new Pipe(); } //将数组中存放的管子更新渲染出来 this.pipeArr.forEach((item)=>{ item.update(); item.render(); }) this.bird.update(); this.bird.render(); */ },20) }; imgLoad(){ this.allImg={ "bg_day":"images/bg_day.png", "bg_night":"images/bg_night.png", "land":"images/land.png", "pipe_down":"images/pipe_down.png", "pipe_up":"images/pipe_up.png", "bird0_0":"images/bird0_0.png", "bird0_1":"images/bird0_1.png", "bird0_2":"images/bird0_2.png", "title":"images/title.png", "button_play":"images/button_play.png", "tutorial":"images/tutorial.png", "number_score_00":"images/number_score_00.png", "number_score_01":"images/number_score_01.png", "number_score_02":"images/number_score_02.png", "number_score_03":"images/number_score_03.png", "number_score_04":"images/number_score_04.png", "number_score_05":"images/number_score_05.png", "number_score_06":"images/number_score_06.png", "number_score_07":"images/number_score_07.png", "number_score_08":"images/number_score_08.png", "number_score_09":"images/number_score_09.png", "blink_00":"images/blink_00.png", "blink_01":"images/blink_01.png", "blink_02":"images/blink_02.png", "medals_0":"images/medals_0.png", "medals_1":"images/medals_1.png", "medals_2":"images/medals_2.png", "medals_3":"images/medals_3.png", "game_over":"images/text_game_over.png", "score_panel":"images/score_panel.png", "shuzi0":"images/font_048.png", "shuzi1":"images/font_049.png", "shuzi2":"images/font_050.png", "shuzi3":"images/font_051.png", "shuzi4":"images/font_052.png", "shuzi5":"images/font_053.png", "shuzi6":"images/font_054.png", "shuzi7":"images/font_055.png", "shuzi8":"images/font_056.png", "shuzi9":"images/font_057.png", "baozha1":"images/Bomb1.png", "baozha2":"images/Bomb2.png", "baozha3":"images/Bomb3.png", "baozha4":"images/Bomb4.png", "baozha5":"images/Bomb5.png", "baozha6":"images/Bomb6.png", "baozha7":"images/Bomb7.png", "baozha8":"images/Bomb8.png", }; let count = 0,total = Object.keys(this.allImg).length; for(let key in this.allImg){ let img = new Image(); img.src = this.allImg[key]; img.onload = ()=>{ this.allImg[key] = img; count++; if(count>=total) this.start(); } } }; /* bindEvent(){ this.canvas.onclick = ()=>{ this.bird.fly(); } } */ }