前端实战小案例--canvas实战之FlappyBird小游戏

想练习更多前端案例,请进个人主页,点击前端实战案例->传送门

觉得不错的记得点个赞?支持一下我0.0!谢谢了!

不积跬步无以至千里,不积小流无以成江海。

效果图如下:(由于图片有大小限制,所以录制的是10帧,有点卡顿的感觉,实际是很流畅的)

素材如下:

 

文件结构目录如下:

代码如下:

index.html

<!DOCTYPE html>
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"><title>FlappyBird</title><link rel="stylesheet" href="./css/index.css" /></head><body><canvas><span>您的浏览器不支持画布元素,请使用谷歌浏览器</span></canvas></body><script src="./js/Game.js"></script><script src="./js/Background.js"></script><script src="./js/Land.js"></script><script src="./js/Pipe.js"></script><script src="./js/Bird.js"></script><script src="./js/SceneManager.js"></script><script>var game = new Game();</script>
</html>

index.css

*{margin: 0;padding: 0;
}canvas{display: block;margin: 0 auto;
}

resource.json

[{"imgs":[{"name":"Back1", "url":"./resource/images/Back1.png"},{"name":"Bird", "url":"./resource/images/Bird.png"},{"name":"Flash", "url":"./resource/images/Flash.png"},{"name":"Logo", "url":"./resource/images/Logo.png"},{"name":"Number", "url":"./resource/images/Number.png"},{"name":"Land2", "url":"./resource/images/Road2.png"},{"name":"Pipe1", "url":"./resource/images/Pipe1.png"},{"name":"Pipe2", "url":"./resource/images/Pipe2.png"},{"name":"Bomb", "url":"./resource/images/Bomb.png"}]}
]

Background.js

//背景类
(function () {var Background = window.Background = function () {this.image = game.imgArr["Back1"];this.speed = 0;};Background.prototype.render = function () {game.ctx.clearRect(0,0,game.canvas.width,game.canvas.height);game.ctx.drawImage(this.image,this.speed,game.canvas.height*0.618-this.image.height*0.618);game.ctx.drawImage(this.image,this.image.width+this.speed,game.canvas.height*0.618-this.image.height*0.618);game.ctx.drawImage(this.image,this.image.width*2+this.speed,game.canvas.height*0.618-this.image.height*0.618);game.ctx.save();game.ctx.fillStyle = "#4EC0CA";game.ctx.beginPath();game.ctx.fillRect(0,0,game.canvas.width,game.canvas.height*0.618-this.image.height*0.618+10);game.ctx.restore();game.ctx.save();game.ctx.fillStyle = "#DED895";game.ctx.beginPath();game.ctx.fillRect(0,game.canvas.height*0.618+this.image.height*(1-0.618)-10,game.canvas.width,game.canvas.height*(1-0.618)-this.image.height*(1-0.618)+10);game.ctx.restore();};Background.prototype.update = function () {this.speed--;if(-this.speed === this.image.width){this.speed = 0;}}
})();

Bird.js

(function () {var Bird = window.Bird = function () {this.image = game.imgArr["Bird"];this.x = game.canvas.width*(1-0.618);this.y = game.canvas.height*0.618*(1-0.618);//小鸟旋转的角度this.deg = 0;//小鸟是否在飞this.isFly = false;//帧数this.fno = 0;//鸟的颜色this.birdColor = 24*parseInt(Math.random()*3);//鸟扇动翅膀的图片的编号this.wingNo = 0;//下面的四个变量用于碰撞检测,代表鸟的上下左右的最小矩形框this.L = this.R = this.T = this.B = 0;//该变量用于保存鸟是否为死亡状态this.isDie = false;};Bird.prototype.render = function () {game.ctx.save();game.ctx.translate(this.x,this.y);game.ctx.rotate(this.deg);game.ctx.beginPath();game.ctx.drawImage(this.image,34*this.wingNo,this.birdColor,34,24,-17,-12,34,24);game.ctx.restore();// game.ctx.fillRect(this.L,this.T,34,24);};Bird.prototype.update = function () {this.L = this.x-17;this.R = this.x + 17;this.T = this.y-12;this.B = this.y + 12;//判断鸟是否飞出天空,真则不让飞出,if(this.T < 0){//这里不能使用this.T = 0;因为//game.ctx.drawImage(this.image,34*this.wingNo,this.birdColor,34,24,-17,-12,34,24);//game.ctx.translate(this.x,this.y);这里是根据x,y来绘制鸟的,固定T鸟还是飞出屏幕this.y = 12;}if(this.isFly){//这里为扇动翅膀动画if(this.fno%2 === 0){this.wingNo++;}if(this.wingNo > 2){this.wingNo = 0;}//当有点击事件时,鸟儿向上飞一段距离this.y -= 0.4 * (20-this.fno);if(this.fno === 20){this.fno = 0;this.isFly = false;}this.deg -= (Math.PI/180)*6;if(this.deg < -(Math.PI/180)*45){this.deg = -(Math.PI/180)*45;}}else{this.y += 0.4 * this.fno;this.deg += (Math.PI/180)*3;if(this.deg > (Math.PI/180)*90){this.deg = (Math.PI/180)*90;}}this.fno++;};Bird.prototype.fly = function () {this.fno = 0;this.isFly = true;}
})();

Game.js

(function(){var Game = window.Game = function(){this.canvas = document.querySelector("canvas");this.canvas.width = document.documentElement.clientWidth;this.canvas.height = document.documentElement.clientHeight;this.init();this.ctx = this.canvas.getContext('2d');//这是图片字典this.imgArr = {};//这是管子数组this.pipeArr = [];//游戏分数:即通过的管子数this.score = 0;//记录过多少时间new一个管子this.time_count = 0;//这里也需要使用箭头函数,否则this指向window而不是window.Gamethis.loadResource(()=>{this.startGame();});};//初始化画布宽高,适配移动端不同机型Game.prototype.init =  function(){if(this.canvas.width < 320){this.canvas.width = 320;}else if(this.canvas.width > 414){this.canvas.width = 414;}if(this.canvas.height < 568){this.canvas.height = 568;}else if(this.canvas.height > 823){this.canvas.height = 823;}};//加载资源Game.prototype.loadResource = function(callback){//定义已加载的资源数var resource_count = 0;var xhr = new XMLHttpRequest();//这里需使用箭头函数,否则将出现this指向错误,xhr.onreadystatechange = ()=>{//此时的this指向window.Gameif(xhr.readyState === 4 && xhr.status === 200){var obj = JSON.parse(xhr.responseText);for(let i=0;i<obj[0]["imgs"].length;i++){this.imgArr[obj[0]["imgs"][i].name] = new Image();this.imgArr[obj[0]["imgs"][i].name].src = obj[0]["imgs"][i].url;this.imgArr[obj[0]["imgs"][i].name].onload = ()=> {resource_count++;this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height);this.ctx.save();let text = "正在加载资源"+ resource_count +"/"+obj[0]["imgs"].length;this.ctx.textAlign = "center";this.ctx.font = "20px 微软雅黑";this.ctx.beginPath();this.ctx.fillText(text,this.canvas.width/2,this.canvas.height*(1-0.618));this.ctx.restore();//当加载图片的数量等于图片资源的数量时,说明图片资源加载完毕,执行回调函数if(resource_count === obj[0]["imgs"].length){callback();}}}}};xhr.open('GET',"./resource.json");xhr.send();};//主线程:游戏开始Game.prototype.startGame = function () {//实例化场景管理器this.sm = new SceneManager();this.timer = setInterval(()=>{this.time_count += 20;this.sm.render();this.sm.update();},20)};
})();

Land.js

//大地类
(function () {var Land = window.Land = function () {this.image = game.imgArr["Land2"];this.speed = 0;};Land.prototype.render =  function () {game.ctx.drawImage(this.image,this.speed,game.canvas.height*0.618+99);game.ctx.drawImage(this.image,this.image.width+this.speed,game.canvas.height*0.618+99);};Land.prototype.update =  function () {//每次更新判断鸟是否着地,着地则结束游戏if(game.bird.B > game.canvas.height*0.618+80){game.bird.isDie = true;}//近处的物体速度快,故大地的速度比远处的树林和白云快this.speed-=2;if(-this.speed > 640){this.speed = 0;}}
})();

Pipe.js

(function () {var Pipe = window.Pipe = function () {this.Pipe1 = game.imgArr["Pipe1"];this.Pipe2 = game.imgArr["Pipe2"];this.height = game.canvas.height*0.618+99;//两个管子之间的空隙高度this.interspace = 150;//上面的管子随机高度this.randomHeight = 140 + parseInt(Math.random()*180);//下面的管子的高度等于this.height-this.randomHeight-this.interspacethis.Pipe2_height = this.height-this.randomHeight-this.interspace;this.speed = 0;//该变量用于碰撞检测this.L = 0;//该变量用于判断鸟是否已经通过管子,防止重复加分this.alreadyPass = false;//将自己推入数组game.pipeArr.push(this);};Pipe.prototype.render = function () {this.L = game.canvas.width+10+this.speed;//当下面的管子高度小于40时,重新取值while (this.Pipe2_height < 40){this.randomHeight = 140 + parseInt(Math.random()*180);this.Pipe2_height = this.height-this.randomHeight-this.interspace;}game.ctx.drawImage(this.Pipe1, 0, this.Pipe1.height-this.randomHeight, 52, this.randomHeight, this.L, 0, 52, this.randomHeight);game.ctx.drawImage(this.Pipe2, 0, 0, 52, this.Pipe2_height, this.L,  this.randomHeight+this.interspace, 52, this.Pipe2_height);};Pipe.prototype.update = function () {//每次更新判断管子是否碰到鸟clearInterval(game.timer)if(this.L < game.bird.R && this.L+52 > game.bird.L){if(this.randomHeight > game.bird.T || this.randomHeight + this.interspace < game.bird.B){game.bird.isDie = true;}}this.speed -= 2;//判断鸟是否通过管子,真则加分if(game.bird.L > game.canvas.width+this.speed+52 && !this.alreadyPass){game.score++;this.alreadyPass = true;}//当管子移出画布时,将其从数组中移除if(-this.speed > game.canvas.width + 60){game.pipeArr.shift();this.speed = 0;}}
})();

SceneManager.js

(function () {var sm = window.SceneManager = function () {//sm的场景编号this.sceneNo = 1;//实例化背景game.bg = new Background();//实例化大地game.land = new Land();//实例化鸟game.bird = new Bird();//Flash和Logo图片的Y值this.FlashY = 0;this.LogoY = game.canvas.height;this.bindEvent();//这两个参数用于控制爆炸效果this.i = 0;this.j = 0;//这个参数用于控制鸟死亡下落的重力this.g = 1;};sm.prototype.update = function () {switch (this.sceneNo) {case 1:this.FlashY += 4;this.LogoY -= 4;if(this.FlashY > game.canvas.height * (1-0.618)){this.FlashY = game.canvas.height * (1-0.618);}if(this.LogoY < game.canvas.height * (1-0.618)+100){this.LogoY = game.canvas.height * (1-0.618)+100}break;case 2://判断鸟是否死亡,真则切换到场景3if(game.bird.isDie){this.enter(3)}break;case 3:this.g ++;if(game.bird.y < game.canvas.height*0.618+99){game.bird.y += 0.4 * this.g;}else{this.enter(4);}break;case 4:if(game.time_count%100 === 0){this.i++;if(this.i > 3 && this.j === 0){this.i = 0;this.j = 1;}else if(this.i > 3 && this.j === 1){this.i = 3;this.j = 1;this.enter(5);}}break;case 5:this.FlashY += 4;if(this.FlashY > game.canvas.height * (1-0.618)){this.FlashY = game.canvas.height * (1-0.618);}break;}};sm.prototype.render = function () {//清屏game.ctx.clearRect(0,0,game.canvas.width,game.canvas.height);switch (this.sceneNo) {case 1://场景1:游戏开始界面game.bg.render();game.land.render();game.bg.update();game.land.update();game.ctx.drawImage(game.imgArr["Flash"],0,0,204,69,game.canvas.width / 2 - 102,this.FlashY,204,69);game.ctx.drawImage(game.imgArr["Logo"],game.canvas.width / 2 - 160,this.LogoY);break;case 2://场景2:游戏开始//每隔两秒new一个管子if(game.time_count > 2000){game.time_count = 0;new Pipe();}game.bg.render();game.land.render();game.bg.update();game.land.update();//遍历管子数组,更新并渲染for (let i=0;i<game.pipeArr.length;i++){game.pipeArr[i].update();game.pipeArr[i].render();}game.bird.update();game.bird.render();//绘制游戏分数game.ctx.save();game.ctx.font = "26px 微软雅黑";game.ctx.textBaseline = "top";game.ctx.beginPath();game.ctx.fillText("分数:",0,15);game.ctx.restore();var number = game.score.toString();for(let i=0; i<number.length; i++){game.ctx.drawImage(game.imgArr["Number"],28*parseInt(number[i]),0,28,36,28*i+75,10,28,36);}break;case 3://场景3:小鸟撞地死亡//每隔两秒new一个管子if(game.time_count === 2000){game.time_count = 0;new Pipe();}game.bg.render();game.land.render();//遍历管子数组,更新并渲染for (let i=0;i<game.pipeArr.length;i++){game.pipeArr[i].render();}game.bird.render();//绘制游戏分数game.ctx.save();game.ctx.font = "26px 微软雅黑";game.ctx.textBaseline = "top";game.ctx.beginPath();game.ctx.fillText("分数:",0,15);game.ctx.restore();var number = game.score.toString();for(let i=0; i<number.length; i++){game.ctx.drawImage(game.imgArr["Number"],28*parseInt(number[i]),0,28,36,28*i+75,10,28,36);}break;case 4://游戏结束:鸟撞管子死亡动画//每隔两秒new一个管子if(game.time_count === 2000){game.time_count = 0;new Pipe();}game.bg.render();game.land.render();//遍历管子数组,更新并渲染for (let i=0;i<game.pipeArr.length;i++){game.pipeArr[i].render();}//绘制游戏分数game.ctx.save();game.ctx.font = "26px 微软雅黑";game.ctx.textBaseline = "top";game.ctx.beginPath();game.ctx.fillText("分数:",0,15);game.ctx.restore();var number = game.score.toString();for(let i=0; i<number.length; i++){game.ctx.drawImage(game.imgArr["Number"],28*parseInt(number[i]),0,28,36,28*i+75,10,28,36);}//当鸟的y值大于地面的高度,则产生爆炸效果if(game.bird.y > game.canvas.height*0.618+99){game.ctx.drawImage(game.imgArr["Bomb"],128*this.i,128*this.j,128,128,game.bird.x-34,game.bird.y-34,34*2,34*2);}break;case 5://游戏结束:GameOverif(game.time_count === 2000){game.time_count = 0;new Pipe();}game.bg.render();game.land.render();//遍历管子数组,更新并渲染for (let i=0;i<game.pipeArr.length;i++){game.pipeArr[i].render();}//绘制游戏分数game.ctx.save();game.ctx.font = "26px 微软雅黑";game.ctx.textBaseline = "top";game.ctx.beginPath();game.ctx.fillText("分数:",0,15);game.ctx.restore();var number = game.score.toString();for(let i=0; i<number.length; i++){game.ctx.drawImage(game.imgArr["Number"],28*parseInt(number[i]),0,28,36,28*i+75,10,28,36);}//GameOver图标往下移动到屏幕中间game.ctx.drawImage(game.imgArr["Flash"],204,0,204,69,game.canvas.width / 2 - 102,this.FlashY,204,69);}};//进入某个场景时需要做的操作sm.prototype.enter = function (sceneNo) {switch (sceneNo) {case 1:this.sceneNo = 1;this.FlashY = 0;this.LogoY = game.canvas.height;game.bird = new Bird();//实例化背景game.bg = new Background();//实例化大地game.land = new Land();game.pipeArr = [];game.time_count = 0;game.score = 0;//这两个参数用于控制爆炸效果this.i = 0;this.j = 0;//这个参数用于控制鸟死亡下落的重力this.g = 1;break;case 2:this.sceneNo = 2;//sm的场景编号break;case 3:this.sceneNo = 3;break;case 4:this.sceneNo = 4;break;case 5:this.sceneNo = 5;this.FlashY = 0;break;}};//sm.prototype.bindEvent = function () {//当点击canvas时,通过判断在哪个场景的哪个位置来做出相应的事件game.canvas.onclick =  () => {switch (this.sceneNo) {case 1:game.bird.isDie = false;game.bird.x = game.canvas.width*(1-0.618);game.bird.y = game.canvas.height*0.618*(1-0.618);this.enter(2);break;case 2:game.bird.fly();break;case 3:break;case 4:break;case 5:this.enter(1);break;}}}
})();

前端实战小案例--canvas实战之FlappyBird小游戏相关推荐

  1. Linux:文件权限管理小案例1:警察和土匪游戏

    第一步,第二步:创建组,创建用户操作如下: 注:因为已经存在jack,所以使用userdel删除jack. 注:在此处创建jack用户时要定义下密码,使用passwd 第三步:jack创建一个文件,自 ...

  2. golang小案例 —— 剪子剪子包袱锤小游戏

    go语言简单的玩耍一下剪子包袱锤游戏...... package mainimport ("fmt""math/rand""time" )f ...

  3. 微信小程序 | canvas绘图

    1.新的尺寸单位 rpx rpx(responsive pixel): 可以根据屏幕宽度进行自适应. 规定屏幕宽为750rpx.如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则 ...

  4. 转发:11个web前端开发实战项目案例+源码!拿走就是了

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/weixin_43793782/arti ...

  5. jsp项目开发案例_Laravel 中使用 swoole 项目实战开发案例一 (建立 swoole 和前端通信)life...

    1 开发需要环境 工欲善其事,必先利其器.在正式开发之前我们检查好需要安装的拓展,不要开发中发现这些问题,打断思路影响我们的开发效率. 安装 swoole 拓展包 安装 redis 拓展包 安装 la ...

  6. 基因组层次聚类实战小案例

    基因组层次聚类实战小案例 预处理数据集 层次聚类 聚类结果分析 预处理数据集 提供的数据集是字符串形式,且不符合聚类的要求,需要进行转置,因此对数据进行预处理. 1.读入103个基因组的名称 原数据格 ...

  7. python计算银行余额_Python 小案例实战 —— 简易银行存取款查询系统

    Python 小案例实战 -- 简易银行存取款查询系统 涉及知识点 包的调用 字典.列表的混合运用 列表元素索引.追加 基本的循环与分支结构 源码 import sys import time ban ...

  8. jsp项目开发案例_Laravel中使用swoole项目实战开发案例一 (建立swoole和前端通信)

    Laravel中使用swoole项目实战开发案例二(后端主动分场景给界面推送消息) 工欲善其事,必先利其器.在正式开发之前我们检查好需要安装的拓展,不要开发中发现这些问题,打断思路影响我们的开发效率. ...

  9. iOS开发实战-基于SpriteKit的FlappyBird小游戏

    写在前面 最近一直在忙自己的维P恩的事情 公司项目也是一团乱 于是...随手找了个游戏项目改了改就上线了,就当充数了. SpriteKit简介 SpriteKit是iOS 7之后苹果推出的2D游戏框架 ...

最新文章

  1. CSDN博客的创建及使用
  2. 【原】Github系列之三:开源iOS下 渐变颜色的进度条WGradientProgress
  3. Gartner预测2019年十大「数据和分析技术」趋势:增强型分析成为重要卖点
  4. C#: 旋转图片到正确位置
  5. 【图像分类】简述无监督图像分类发展现状
  6. POJ1228(稳定凸包问题)
  7. Teams Bot开发系列:Middleware
  8. 苹果笔记本电脑进入新时代
  9. spring中log4j使用
  10. 流利说公布上市后首份财报:第三季净收入1.8亿
  11. 虚拟机管理需要哪些功能,以及虚拟机管理常见问题和解决方案
  12. CentOS修改tomcat端口
  13. veeam安装和部署_Veeam Backup Replication 8 安装配置
  14. Linux安装Beyond Compare
  15. PowerBI切换日期维度
  16. ORAN C平面 Section Extension 1
  17. controller 之@...
  18. Reactor模式:反应器模式
  19. Android数据库Cursor异常原因和Fd泄露分析
  20. Cent OS 7.7 搭建蓝鲸智云社区版5.1.27(2)——标准部署

热门文章

  1. 删除 Mac OS X 中“打开方式”里重复或无用的程序列表
  2. 灾备联盟牵头研制《云灾备技术发展趋势与应用白皮书》
  3. IE浏览器默认主页被篡改,无法改回
  4. 1166: 6102玛雅历法
  5. 中望CAD机械版学习-1-基础操作
  6. WINCC 报警语音播报
  7. CF1485C(整除分块+根号分块)
  8. 接口测试管理平台手册
  9. 北京师范大学新闻与传播专硕考研一战上岸经验分享
  10. 提升算法数据结构的几个网站