游戏查看

源码和素材下载

博主学习前端一年多一点,还是个新手,不过勇于尝试,才能不断进步,如果代码质量不好,欢迎提意见,下面开始讲解,首先贴张游戏界面图:

游戏使用canvas画图制作,分析游戏确定有这几个元素:

  1. 天空背景不动
  2. 小鸟上下移动,左右不动
  3. 地板和水管向左移动(造成小鸟向前移动的错觉)

canvas画图是覆盖画图,所以画图顺序很重要,它的api我就不多说了,只有用到以下内容:

<!-- html代码 -->
<canvas id="canvas">您的浏览器不支持canvas</canvas>
/* js相关 */
var canvas = document.getElementById('canvas'),   //获取canvas节点ctx = canvas.getContext('2d');                //获取画布上下文画图环境
//画图(只举了一种,还有另一种传参方式)
ctx.drawImage(img, imgx, imgy, imgw, imgh, canx, cany, canw, canh);
//参数的含义依次为:图片资源、图片的x坐标、y坐标、宽度、高度、画布中x坐标、y坐标、宽度、高度
//因为我把所有的图片都合成一张,所以需要用截取图像的传参方式

下面简单说说整个游戏的运行代码结构:

var img = new Image();           //加载图像
img.src = './img.png';
img.onload = start;              //图像加载完成就运行start函数,所以start是入口function start(){//检查是否碰撞到地板,水管check();if(是否游戏结束){//游戏结束的操作然后退出return;}//画背景...if(isStarted){            //isStarted为是否开始游戏的变量,是全局的,默认为false//开始游戏就画小鸟,水管}else{//否则就画准备开始的图像}//画分数(默认为0,准备阶段画的是0)...//画地板...//设置定时器,保证动画在游戏中不断进行timer = requestAnimationFrame(start); //和setTimeout(start, 16)效果差不多
}document.ontouchstart = document.onmousedown = function(e){//点击屏幕时小鸟进行跳跃等处理
}

整体结构就是这样,然后我们一部分一部分完成就可以了。

第一步:获取设备的屏幕大小,兼容各种屏幕设备

var viewSize = (function(){var pageWidth = window.innerWidth,pageHeight = window.innerHeight;if (typeof pageWidth != 'number') {pageHeight = document.documentElement.clientHeight;pageWidth = document.documentElement.clientWidth;};if(pageWidth >= pageHeight){pageWidth = pageHeight * 360 / 640;}pageWidth = pageWidth >  414 ? 414 : pageWidth;pageHeight = pageHeight > 736 ? 736 : pageHeight;return {width: pageWidth,height: pageHeight};})();
//然后就设置画布宽高
canvas.width = viewSize.width;
canvas.height = viewSize.height;
//定义原图像与游戏界面的像素比
var k = viewSize.height / 600    //我找的背景图高度为600px,所以比例就是屏幕高除以600

第二步:完成游戏进行中的部分(没有gameover检查,isStarted为true时)

1)画背景(没有难点,主要是图像大小的计算要想清楚)

//清除
ctx.clearRect(0,0,viewSize.width,viewSize.height);
//画背景
ctx.drawImage(img, 0, 0, 800, 600, 0, 0, Math.ceil(k * 800), viewSize.height);

2)画小鸟:我在全局定义了一个小鸟类,如下:

function Bird(){//小鸟拍翅膀有三种状态,所以画图相关大多用一个数组来表示this.imgX = [170, 222, 275];                           //在原图中x的坐标this.imgY = [750, 750, 750];                           //在原图中y的坐标this.imgW = [34, 34, 34];                              //在原图中宽度this.imgH = [24, 24, 24];                              //在原图中高度var canX = Math.ceil(110 / 450 * viewSize.width);      //在画布中x的坐标this.canX = [canX, canX, canX];var canY = Math.ceil(380 / 800 * viewSize.height);     //在画布中y的初始坐标 this.canY = [canY, canY, canY];                        var canW = Math.ceil(34 * k);                          //在画布中的宽度  this.canW = [canW, canW, canW];                 var canH = Math.ceil(24 * k);                          //在画布中的高度this.canH = [canH, canH, canH];//下面三个变量是用来协助记住是在三个状态中的哪个状态,后面一看就知道了this.index = 0;                                        this.count = 0;this.step = 1;//表示小鸟飞行的时间,后面知道用途this.t = 0;//记住初始y坐标,也是后面一看就知道了this.y = [canY, canY, canY];
}

定义类的好处就是可以不用设置那么多的全局变量,你可以直接定义小鸟为一个对象,接着定义小鸟画图方法:

Bird.prototype.draw = function(){var index = this.index;//翅膀拍动, this.count就是用来控制拍动的频率,记住定时器1秒运行16帧,频率很快的this.count++;if(this.count == 6){this.index += this.step;   this.count = 0;}//this.index的变化过程为0、1、2、1、0、1、2、1...所以需要this.index +1和-1变化if((this.index == 2 && this.step == 1) || (this.index == 0 && this.step) == -1){this.step = - this.step;} //计算垂直位移,使用公式 y = a * t * (t - c),这里就知道了this.t是代表着小鸟起跳后到现在的时间//我使用了抛物线的函数方程,你也可以自己选择,代码下面我会给出函数坐标图就很清除了var c = 0.7 * 60;var minY = - 85 * viewSize.height / 800;var a = -minY * 4 / (c * c);var dy = a * this.t * (this.t - c);  //dy是小鸟的位移//下面是小鸟飞到顶部的情况,我的处理是,使再点击失效,要小鸟飞下来才能继续点击if(this.y[0] + dy < 0){canClick = false;}else{canClick = true;}//然后小鸟在画布的y坐标就等于原先的y坐标加上位移for(var i = 0; i < 3; i++){this.canY[i] = this.y[i] + Math.ceil(dy);}this.t++;ctx.drawImage(img, this.imgX[index], this.imgY[index], this.imgW[index], this.imgH[index], this.canX[index], this.canY[index], this.canW[index], this.canH[index]);};

给出小鸟计算方程的坐标图

因为canvas的y正方向是向下的,所以跳跃应该位移是先负后正,自由落体又是抛物线,接下来就是数学知识了,图中可以看出:

  • 如果this.t > c,dy > 0,所以可以得出,当this.t = c小鸟到最高点,选c的大小就可以控制上升和下落的速度

  • 在this.t = c/2时,dy达到了最小值,所以,控制Ymin可以确定小鸟的垂直移动最大距离。

要画小鸟就可以:

var bird = new Bird();
bird.draw();

3)画水管:

游戏画面中最多出现两组水管,当第一组水管到中间时,第二组开始出现,当第一组水管从游戏界面的左边出去了,第二组水管刚刚到达中间,而最右边又开始有水管进来,以此类推,不断重复。

先解决一组水管的画法,仍然先定义水管类,分为上水管和下水管:

//基类,属性的含义同小鸟类
function Pie(){this.imgY = 751;              this.imgW = 52;              this.imgH = 420;this.canX = viewSize.width;               //默认在画布的最右边this.canW = Math.ceil(80 / 450 * viewSize.width);this.canH = Math.ceil(this.canW * 420 / 52);
}
//其中top我们随机生成,代表的是同一组水管中,上水管的左下角在画布中的y坐标
//上水管类
function UpPie(top){  Pie.call(this);                       //继承相同的属性this.imgX = 70;                       //上水管在原图中的x坐标  this.canY = top - this.canH;          //上水管在画布中的y坐标计算this.draw = drawPie;
};
//下水管类
function DownPie(top){Pie.call(this);this.imgX = 0;this.canY = top + Math.ceil(150 / 800 * viewSize.height);  //上水管和下水管的距离固定,大小可调this.draw = drawPie;
}
function drawPie(){var speed = 2 * k;this.canX -= speed;  //每画一次就向左边走ctx.drawImage(img, this.imgX, this.imgY, this.imgW, this.imgH, this.canX, this.canY, this.canW, this.canH);
}

然后开始画水管:

//用一个数组存在画面中的水管
var Pies = [];
//创建水管函数,首先随机生成top,然后分别实例化上、下水管然后存进Pies里
function createPie(){var minTop = Math.ceil(90 /800 * viewSize.height),maxTop = Math.ceil(390 /800 * viewSize.height),top = minTop + Math.ceil(Math.random() * (maxTop - minTop));Pies.push(new UpPie(top));Pies.push(new DownPie(top));
};
//画水管时,首先判断
//第一组水管出左边屏幕,移除水管
if(Pies[0].canX <= -Pies[0].canW && Pies.length == 4){Pies[0] = null;Pies[1] = null;Pies.shift();Pies.shift();canCount = true;
}
//第一组水管到达中间时创建水管
if(Pies[0].canX <= 0.5 * (viewSize.width - Pies[0].canW) && Pies.length == 2){createPie();
}
//然后就可以画水管
for(var i = 0, len = Pies.length; i < len; i++){Pies[i].draw();
}

4)画分数,比较简单,主要是需要计算居中:

/*** 分数类*/
function Score(){this.imgX = 900;this.imgY = 400;this.imgW = 36;this.imgH = 54;this.canW = Math.ceil(36 * k);this.canH = Math.ceil(54 * k);this.canY = Math.ceil(50 / 800 * viewSize.height);this.canX = Math.ceil(viewSize.width / 2 - this.canW / 2);this.score = 0;
}
Score.prototype.draw = function(){var aScore = ('' + this.score).split('');var len = aScore.length;//计算一下居中this.canX = 0.5 * (viewSize.width - (this.canW + 10) * len + 10);for(var i = 0; i < len; i++){var num = parseInt(aScore[i]);if(num < 5){var imgX = this.imgX + num * 40;var imgY = 400;}else{var imgX = this.imgX + (num - 5) * 40;var imgY = 460;}var canX = this.canX + i * (this.canW + 2);ctx.drawImage(img, imgX, imgY, this.imgW, this.imgH, canX, this.canY, this.canW, this.canH);}
};

然后画就简单了

var score = new Score();score.draw();

5)画地板,主要是需要让它向左移动

//地板类
function Ground(){this.imgX = 0;this.imgY = 600;this.imgH = 112;this.imgW = 600;this.canH = Math.ceil(112 * k);this.canW = Math.ceil(k * 800);this.canX = 0;this.canY = viewSize.height - this.canH;
}
Ground.prototype.draw = function(){if(this.imgX > 24) this.imgX = 0;   //因为无限滚动,所以需要无痕接上ctx.drawImage(img, this.imgX, this.imgY, this.imgW, this.imgH, this.canX, this.canY, this.canW, this.canH);this.imgX += 2;
};

画的时候实例就可以了:

var ground = new Ground();ground.draw();

到这里你就可以看到水管和地板可以向后走了,小鸟也能飞起来了,只是会不断下落,所以我们要设置点击弹跳。

第三步:点击处理

//touchstart是手机端,mousedown是PC端
document.ontouchstart = document.onmousedown = function(e){//游戏如果结束点击无效if(gameover) return;if(isStarted){//游戏如果开始了,那么久开始//刚才在小鸟飞出顶部我做了点击屏蔽,if(canClick){//当我们点击的时候,我们应该恢复初始状态,初始状态就是this.t=0, bird.y[i]储存了初始高度for(var i = 0; i < 3; i++){bird.y[i] = bird.canY[i];}bird.t = 0;}else{return;}}else{//游戏没有开始说明在准备,所以开始isStarted = true;}//在ios客户端,touch事件之后还会触发click事件,阻止默认事件就可以屏蔽了var e = e || window.event;if(e.preventDefault){e.preventDefault();}else{e.returnValue = false;}
};

现在你已经可以使小鸟跳跃了,胜利就在前方。

第四步:check函数

检测小鸟和地板是否碰撞最为简单:

//地板碰撞,小鸟的y坐标 + 小鸟的高度 >= 地板的y坐标,表示撞了地板
if(bird.canY[0] + bird.canH[0] >= ground.canY){gameover = true;return;
}

检测小鸟和水管是否碰撞,可以化成两个矩形是否重合,重合的情况比较复杂,我们可以看不重合的情况:只有4种,如图:

(1) (2)

(3) (4)

只要符合上面一种情况就不重合,其余情况就是重合,所以:

//检测两个矩形是否重合,可以反着看,先找出矩形不重合的情况,
function isOverLay(r1, r2){var flag = false;if(r1.top > r2.bottom || r1.bottom < r2.top || r1.right < re2.left || r1.left > r2.right){flag = true;}//反之就是重合return !flag;
}
//水管碰撞
var birdRect = {top: bird.canY[0],bottom: bird.canY[0] + bird.canH[0],left: bird.canX[0],right: bird.canX[0] + bird.canW[0]
};
for(var i = 0, len = Pies.length; i < len; i++){var t = Pies[i];var pieRect = {top: t.canY,bottom: t.canY + t.canH,left: t.canX,right: t.canX + t.canW};if(isOverLay(birdRect,pieRect)){gameover = true;return;}
}

还需要检查是否得分

if(Math.floor(bird.canX[0]) > Math.floor(Pies[0].canX + Pies[0].canW) && canCount){//小鸟的左边出了第一组水管的右边就得分,得分以后,第一组水管还没出屏幕左边时不能计算得分canCount = false;score.score++;
};

所以check函数为:

function check(){function isOverLay(r1, r2){var flag = false;if(r1.top > r2.bottom || r1.bottom < r2.top || r1.right < re2.left || r1.left > r2.right){flag = true;}//反之就是重合return !flag;}//地板碰撞if(bird.canY[0] + bird.canH[0] >= ground.canY){console.log(viewSize)console.log(bird.canY[0],bird.canH[0],ground.canY)gameover = true;return;}//水管碰撞var birdRect = {top: bird.canY[0],bottom: bird.canY[0] + bird.canH[0],left: bird.canX[0],right: bird.canX[0] + bird.canW[0]};for(var i = 0, len = Pies.length; i < len; i++){var t = Pies[i];var pieRect = {top: t.canY,bottom: t.canY + t.canH,left: t.canX,right: t.canX + t.canW};if(isOverLay(birdRect,pieRect)){gameover = true;return;}}//是否得分if(Math.floor(bird.canX[0]) > Math.floor(Pies[0].canX + Pies[0].canW) && canCount){canCount = false;score.score++;};
}

现在游戏已经可以玩了,就是还差gameover处理,和重新开始处理了

第五步:gameover处理:

//画gameover字样
ctx.drawImage(img, 170, 990, 300, 90, Math.ceil(viewSize.width * 0.5 - k * 277 * 0.5), Math.ceil(200 / 800 * viewSize.height), 277 * k, 75 * k);
//画重新开始点击按钮
ctx.drawImage(img, 550, 1005, 160, 90, Math.ceil(viewSize.width * 0.5 - k * 160 * 0.5), Math.ceil(400 / 800 * viewSize.height), 160 * k, 90 * k)
//因为有重新点击开始,所以在html中有个隐藏的div用来点击重新开始,现在让它出现
startBtn.style.display = 'block';
startBtn.style.width = 160 * k + 'px';
startBtn.style.height = 90 * k + 'px';
startBtn.style.left = Math.ceil(viewSize.width * 0.5 - k * 160 * 0.5) + 'px';
startBtn.style.top = Math.ceil(400 / 800 * viewSize.height) + 'px';
//消除定时器
cancelAnimationFrame(timer);  //如果用setTimeout就是:cleatTimeout(timer)
//回收资源
ground = null;
bird = null;
score = null;
for(var i = 0, len = Pies.length; i < len; i++){Pies[i] = null;
}
Pies = [];

第六步:重新开始游戏处理

startBtn.ontouchstart = startBtn.onmousedown = function(e){//初始化参数canClick = true;gameover = false;canCount = true;isStarted = false;startBtn.style.display = 'none';ground = new Ground();bird = new Bird();score = new Score();Pies = [];createPie();//开定时器timer = requestAnimationFrame(start); //或者timer = setTimeout(start, 16);//阻止冒泡到documentvar e = e || window.event;if(e.stopPropagation){e.stopPropagation();}else{e.cancelBubble = false;}}

到此结束,贴上全部代码,有耐心看完的估计没有几个,哈哈哈

html代码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Flappy Bird</title><meta name="viewport" content="width=device-width"/><style>body,html{padding:0;margin:0;height:100%;width:100%;backgroung:#f1f1f1;cursor:pointer;overflow: hidden;}canvas{position:relative;z-index:998;}#restart{position:absolute;top:0;left:0;z-index:999;display:none;}</style>
</head>
<body><canvas id="canvas"></canvas><div id="restart"></div><script src="index.js"></script>
</body>
</html>

js代码

var viewSize = (function(){var pageWidth = window.innerWidth,pageHeight = window.innerHeight;if (typeof pageWidth != 'number') {if (document.compatMode == 'CSS1Compat') {pageHeight = document.documentElement.clientHeight;pageWidth = document.documentElement.clientWidth;} else {pageHeight = document.body.clientHeight;pageWidth = document.body.clientWidth;}};if(pageWidth >= pageHeight){pageWidth = pageHeight * 360 / 640;}pageWidth = pageWidth >  414 ? 414 : pageWidth;pageHeight = pageHeight > 736 ? 736 : pageHeight;return {width: pageWidth,height: pageHeight};})();(function(){var lastTime = 0;var prefixes = 'webkit moz ms o'.split(' '); //各浏览器前缀var requestAnimationFrame = window.requestAnimationFrame;var cancelAnimationFrame = window.cancelAnimationFrame;var prefix;
//通过遍历各浏览器前缀,来得到requestAnimationFrame和cancelAnimationFrame在当前浏览器的实现形式for( var i = 0; i < prefixes.length; i++ ) {if ( requestAnimationFrame && cancelAnimationFrame ) {break;}prefix = prefixes[i];requestAnimationFrame = requestAnimationFrame || window[ prefix + 'RequestAnimationFrame' ];cancelAnimationFrame  = cancelAnimationFrame  || window[ prefix + 'CancelAnimationFrame' ] || window[ prefix + 'CancelRequestAnimationFrame' ];}//如果当前浏览器不支持requestAnimationFrame和cancelAnimationFrame,则会退到setTimeoutif ( !requestAnimationFrame || !cancelAnimationFrame ) {requestAnimationFrame = function( callback, element ) {var currTime = new Date().getTime();//为了使setTimteout的尽可能的接近每秒60帧的效果var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );var id = window.setTimeout( function() {callback( currTime + timeToCall );}, timeToCall );lastTime = currTime + timeToCall;return id;};cancelAnimationFrame = function( id ) {window.clearTimeout( id );};}//得到兼容各浏览器的APIwindow.requestAnimationFrame = requestAnimationFrame;window.cancelAnimationFrame = cancelAnimationFrame;
})()var canvas = document.getElementById('canvas'),ctx = canvas.getContext('2d'),img = new Image(),k= viewSize.height / 600,canClick,gameover,canCount,isStarted,timer,ground,bird,score,Pies,startBtn = document.getElementById('restart');
//导入图像
img.onload = start;
img.src = './img.png';
//设置画布宽高
canvas.width = viewSize.width;
canvas.height = viewSize.height;
init();
function init(){canClick = true;gameover = false;canCount = true;isStarted = false;startBtn.style.display = 'none';ground = new Ground();bird = new Bird();score = new Score();Pies = [];createPie();
}
function destroy(){ground = null;bird = null;score = null;for(var i = 0, len = Pies.length; i < len; i++){Pies[i] = null;}Pies = [];
}
/*** 开始游戏*/
function start(){check();if(gameover){console.log(1)ctx.drawImage(img, 170, 990, 300, 90, Math.ceil(viewSize.width * 0.5 - k * 277 * 0.5), Math.ceil(200 / 800 * viewSize.height), 277 * k, 75 * k)ctx.drawImage(img, 550, 1005, 160, 90, Math.ceil(viewSize.width * 0.5 - k * 160 * 0.5), Math.ceil(400 / 800 * viewSize.height), 160 * k, 90 * k)startBtn.style.width = 160 * k + 'px';startBtn.style.height = 90 * k + 'px';startBtn.style.left = Math.ceil(viewSize.width * 0.5 - k * 160 * 0.5) + 'px';startBtn.style.top = Math.ceil(400 / 800 * viewSize.height) + 'px';startBtn.style.display = 'block';cancelAnimationFrame(timer);destroy();}else{//清除ctx.clearRect(0,0,viewSize.width,viewSize.height);//画背景ctx.drawImage(img, 0, 0, 800, 600, 0, 0, Math.ceil(k * 800), viewSize.height);if(isStarted){//第一组水管出左边屏幕,移除水管if(Pies[0].canX <= -Pies[0].canW && Pies.length == 4){Pies[0] = null;Pies[1] = null;Pies.shift();Pies.shift();canCount = true;}//画小鸟bird.draw();//创建水管if(Pies[0].canX <= 0.5 * (viewSize.width - Pies[0].canW) && Pies.length == 2){createPie();}//画水管for(var i = 0, len = Pies.length; i < len; i++){Pies[i].draw();}}else{//画readyctx.drawImage(img, 170, 900, 300, 90, Math.ceil(viewSize.width * 0.5 - k * 277 * 0.5), Math.ceil(200 / 800 * viewSize.height), 277 * k, 75 * k)ctx.drawImage(img, 170, 1150, 230, 150, Math.ceil(viewSize.width * 0.5 - k * 200 * 0.5), Math.ceil(400 / 800 * viewSize.height), 200 * k, 150 * k)}//画分数score.draw();//画地板ground.draw();//设置定时器timer = requestAnimationFrame(start);}};
/*** 检查是否碰撞、得分*/
function check(){function isOverLay(rect1, rect2){var flag = false;if(rect1.top > rect2.bottom || rect1.bottom < rect2.top || rect1.right < rect2.left || rect1.left > rect2.right) flag = true;return !flag;}//地板碰撞if(bird.canY[0] + bird.canH[0] >= ground.canY){console.log(viewSize)console.log(bird.canY[0],bird.canH[0],ground.canY)gameover = true;return;}//水管碰撞var birdRect = {top: bird.canY[0],bottom: bird.canY[0] + bird.canH[0],left: bird.canX[0],right: bird.canX[0] + bird.canW[0]};for(var i = 0, len = Pies.length; i < len; i++){var t = Pies[i];var pieRect = {top: t.canY,bottom: t.canY + t.canH,left: t.canX,right: t.canX + t.canW};if(isOverLay(birdRect,pieRect)){gameover = true;return;}}//是否得分if(Math.floor(bird.canX[0]) > Math.floor(Pies[0].canX + Pies[0].canW) && canCount){canCount = false;score.score++;};
}
/*** 点击*/
document.ontouchstart = document.onmousedown = function(e){if(gameover) return;if(isStarted){if(canClick){for(var i = 0; i < 3; i++){bird.y[i] = bird.canY[i];}bird.t = 0;}else{return;}}else{isStarted = true;}var e = e || window.event;if(e.preventDefault){e.preventDefault();}else{e.returnValue = false;}
};startBtn.ontouchstart = startBtn.onmousedown = function(e){var e = e || window.event;if(e.stopPropagation){e.stopPropagation();}else{e.cancelBubble = false;}init();timer = requestAnimationFrame(start);
}
/*** 分数类*/
function Score(){this.imgX = 900;this.imgY = 400;this.imgW = 36;this.imgH = 54;this.canW = Math.ceil(36 * k);this.canH = Math.ceil(54 * k);this.canY = Math.ceil(50 / 800 * viewSize.height);this.canX = Math.ceil(viewSize.width / 2 - this.canW / 2);this.score = 0;
}
Score.prototype.draw = function(){var aScore = ('' + this.score).split('');var len = aScore.length;this.canX = 0.5 * (viewSize.width - (this.canW + 10) * len + 10);for(var i = 0; i < len; i++){var num = parseInt(aScore[i]);if(num < 5){var imgX = this.imgX + num * 40;var imgY = 400;}else{var imgX = this.imgX + (num - 5) * 40;var imgY = 460;}var canX = this.canX + i * (this.canW + 2);ctx.drawImage(img, imgX, imgY, this.imgW, this.imgH, canX, this.canY, this.canW, this.canH);}
};
/*** 小鸟类*/
function Bird(){this.imgX = [170, 222, 275];this.imgY = [750, 750, 750];this.imgW = [34, 34, 34];this.imgH = [24, 24, 24];this.index = 2;this.count = 0;this.step = 1;var canX = Math.ceil(110 / 450 * viewSize.width);this.canX = [canX, canX, canX];var canY = Math.ceil(380 / 800 * viewSize.height);this.canY = [canY, canY, canY];var canW = Math.ceil(34 * k);this.canW = [canW, canW, canW];var canH = Math.ceil(24 * k);this.canH = [canH, canH, canH];this.t = 0;this.y = [canY, canY, canY];
}
Bird.prototype.draw = function(){var index = this.index;//翅膀拍动this.count++;if(this.count == 6){this.index += this.step;this.count = 0;}if((this.index == 2 && this.step == 1) || this.index == 0 && this.step == -1) this.step = - this.step;//计算垂直位移,使用公式 y = a * t * (t - c)var c = 0.7 * 60;var minY = - 85 * viewSize.height / 800;var a = -minY * 4 / (c * c);var dy = a * this.t * (this.t - c);if(this.y[0] + dy < 0){canClick = false;}else{canClick = true;}for(var i = 0; i < 3; i++){this.canY[i] = this.y[i] + Math.ceil(dy);}this.t++;ctx.drawImage(img, this.imgX[index], this.imgY[index], this.imgW[index], this.imgH[index], this.canX[index], this.canY[index], this.canW[index], this.canH[index])};
/*** 水管基类*/
function Pie(){this.imgY = 751;this.imgW = 52;this.imgH = 420;this.canX = viewSize.width;this.canW = Math.ceil(80 / 450 * viewSize.width);this.canH = Math.ceil(this.canW * 420 / 52);
}
/*** 上水管类*/
function UpPie(top){Pie.call(this);this.imgX = 70;this.canY = top - this.canH;this.draw = drawPie;
};
UpPie.prototype = new Pie();
/*** 下水管类*/
function DownPie(top){Pie.call(this);this.imgX = 0;this.canY = top + Math.ceil(150 / 800 * viewSize.height);this.draw = drawPie;
}
DownPie.prototype = new Pie();function drawPie(){var speed = 2 * k;this.canX -= speed;ctx.drawImage(img, this.imgX, this.imgY, this.imgW, this.imgH, this.canX, this.canY, this.canW, this.canH);
}/*** 创建水管*/
function createPie(){var minTop = Math.ceil(90 /800 * viewSize.height),maxTop = Math.ceil(390 /800 * viewSize.height),top = minTop + Math.ceil(Math.random() * (maxTop - minTop));Pies.push(new UpPie(top));Pies.push(new DownPie(top));
};
/*** 地板类*/
function Ground(){this.imgX = 0;this.imgY = 600;this.imgH = 112;this.imgW = 600;this.canH = Math.ceil(112 * k);this.canW = Math.ceil(k * 800);this.canX = 0;this.canY = viewSize.height - this.canH;
}
Ground.prototype.draw = function(){if(this.imgX > 24) this.imgX = 0;ctx.drawImage(img, this.imgX, this.imgY, this.imgW, this.imgH, this.canX, this.canY, this.canW, this.canH);this.imgX += 2;
};

JavaScript操作canvas制作前端H5小游戏——Flappy Bird相关推荐

  1. 如何用canvas制作一个华容道小游戏(乞丐版)

    我大抵是废了φ(..) ,横竖都学不进去,上课知识不进脑子,学习光想划水摸鱼,心中仅剩的良知告诉我这样下去是铁定不行的哇,既然学不进去,何不打把游戏,既然要打游戏,为啥不自己写个小游戏嘞٩(๑> ...

  2. 【新手上路】Java必备小游戏——Flappy Bird(飞翔的小鸟)

    <飞翔的小鸟>是一款曾经比较火热的小游戏,本文可以带你你从零开始,一步一步的开发出这款小游戏.如果你只是刚入门java的新手,不用担心,只要你简单掌握了该游戏所需要的javase基础知识, ...

  3. 我的第一个Unity的2D小游戏(Flappy Bird)

    前言 兜兜转转跑来学习unity了,学习利用的是unity2017.2版本,在看过网上所谓的一堆零基础入门的视频后(确实0基础,无外乎都从界面开始介绍,然后是脚本基础几个API的介绍,然后讲解了下UG ...

  4. java小游戏------Flappy Bird(飞翔的小鸟含源码)

    前言:本小游戏可作为java入门阶段收尾创作. 需:掌握面向对象的使用,了解多线程和异常处理等知识. 如上图所示:我们需要绘制背景,小鸟,障碍物,当然也包括游戏开始界面以及死亡界面. 一:思路解析: ...

  5. 用Unity3D开发2D小游戏 Flappy Bird

    简介: 最近在学习Unity3D,用了两天时间做了个小游戏打算放上了和大家分享一下,项目名定义为Flapping,是参考Flappy Bird做的,高手勿喷. 这是原本游戏效果图: 这是本项目效果图: ...

  6. 基于html5贪吃蛇小游戏,使用HTML5 Canvas制作贪吃蛇小游戏

    近重要在研究OAuth2,等demo完成了,会给大家来一个专题分享. 看到我身边的一个同事在玩 蛇蛇争霸 感觉蛮有意思的,这里找了一个 贪吃蛇的游戏 分享给大家. 按理说canvas与其应用是老生常谈 ...

  7. HTML5用canvas制作飞机大战小游戏

    css样式: <!DOCTYPE html> <html lang="en"><head><meta charset="UTF- ...

  8. HTML5/Canvas太空射击类小游戏源码

    下载地址 JavaScript HTML5/Canvas太空射击类小游戏源码,非常值得学习的一款js射击小游戏代码,美术有点老旧,但是代码是完全开源的,有参考价值. dd:

  9. 游戏Flappy Bird走红启示:没人知道玩家想要什么

    [导读]Flappy Bird现排名中国区App Store免费榜第四名.该游戏日平均广告收入达到了5万美元. 腾讯科技 王鑫 2月7日报道 游戏开发者一直在试图了解,到底玩家会喜欢什么样的游戏?免费 ...

  10. 盘点在H5小游戏里常用的动效制作套路

    近年来,越来越多的自带绚丽动效的H5小游戏如雨后春笋一般冒出来,而也正是这些炫酷的交互动效,才能够成功地吸引住用户的眼球,让用户为此驻足. 而「动效制作」也伴随着H5的大热而火遍前端圈及设计界,下面就 ...

最新文章

  1. 2019中国独角兽新增数锐减62%,仅有22家;美国新增78家,占全球大半
  2. 完全卸载Oracle方法(亲测有效)
  3. 中国有多少python程序员-“刚毕业1年,做Python能挣多少?”网友:吹的不多……...
  4. php 多维数组按值排序,按子值对php多维数组排序
  5. python数据结构的列表_Python自带数据结构 列表(list)
  6. TZOJ 2999 Network(连通图割点数量)
  7. OSS内文件如何设置为无时间限制的下载链接
  8. 替换Android中VM 加载动态库方式
  9. Atitit 异常的实现原理 与用户业务异常
  10. ip地址切换批处理脚本
  11. fc安卓模拟器_跨平台游戏模拟器RetroArch,一个软件畅玩FC 、MD、SFC、GBA游戏
  12. Open Distro for Elasticsearch
  13. 全球及中国报刊行业运营策略与未来发展态势研究报告2022版
  14. JAVA面试问题及答案
  15. http://trans.godict.com/index.php
  16. 【OJ每日一练】1039 - 阶乘数列和
  17. c语言kbhit函数在哪里,kbhit()函数
  18. HTC ONE X刷ruu的详细刷机教程
  19. AIR2 Betal版可以下载了flash player10.1可以下载了
  20. 兰州大学计算机专业张教授,兰州大学信息科学与工程学院

热门文章

  1. 损失函数MSE和MAE的区别以及如何选择
  2. STM32的存储器与寄存器
  3. AUTOMATE THE BORING STUFF WITH PYTHON读书笔记 - 第4章:LISTS
  4. easypanel mysql错误_kangle easypanel面板安装后初始化教程
  5. VulnHub靶场-Tiki
  6. excel技巧——F9键
  7. MybatisPlusException: Your property named “xxx“ cannot find the corresponding database column name!
  8. 基于Vue的标尺插件(刻度尺)
  9. python中interval_Python 数值区间处理_对interval 库的快速入门详解
  10. Matplotlib风羽自定义