赞
踩
想练习更多前端案例,请进个人主页,点击前端实战案例->传送门
觉得不错的记得点个赞?支持一下我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.Game
- this.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.Game
- if(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.interspace
- this.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:
- //判断鸟是否死亡,真则切换到场景3
- if(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:
- //游戏结束:GameOver
- 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);
- }
- //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;
- }
- }
- }
- })();

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。