遊戲檢視
源碼和素材下載下傳
部落客學習前端一年多一點,還是個新手,不過勇于嘗試,才能不斷進步,如果代碼品質不好,歡迎提意見,下面開始講解,首先貼張遊戲界面圖:
遊戲使用canvas畫圖制作,分析遊戲确定有這幾個元素:
- 天空背景不動
- 小鳥上下移動,左右不動
- 地闆和水管向左移動(造成小鳥向前移動的錯覺)
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{
//否則就畫準備開始的圖像
}
//畫分數(預設為,準備階段畫的是)
...
//畫地闆
...
//設定定時器,保證動畫在遊戲中不斷進行
timer = requestAnimationFrame(start); //和setTimeout(start, )效果差不多
}
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 * / ;
}
pageWidth = pageWidth > ? : pageWidth;
pageHeight = pageHeight > ? : pageHeight;
return {
width: pageWidth,
height: pageHeight
};
})();
//然後就設定畫布寬高
canvas.width = viewSize.width;
canvas.height = viewSize.height;
//定義原圖像與遊戲界面的像素比
var k = viewSize.height / //我找的背景圖高度為600px,是以比例就是螢幕高除以600
第二步:完成遊戲進行中的部分(沒有gameover檢查,isStarted為true時)
1)畫背景(沒有難點,主要是圖像大小的計算要想清楚)
//清除
ctx.clearRect(,,viewSize.width,viewSize.height);
//畫背景
ctx.drawImage(img, , , , , , , Math.ceil(k * ), viewSize.height);
2)畫小鳥:我在全局定義了一個小鳥類,如下:
function Bird(){
//小鳥拍翅膀有三種狀态,是以畫圖相關大多用一個數組來表示
this.imgX = [, , ]; //在原圖中x的坐标
this.imgY = [, , ]; //在原圖中y的坐标
this.imgW = [, , ]; //在原圖中寬度
this.imgH = [, , ]; //在原圖中高度
var canX = Math.ceil( / * viewSize.width); //在畫布中x的坐标
this.canX = [canX, canX, canX];
var canY = Math.ceil( / * viewSize.height); //在畫布中y的初始坐标
this.canY = [canY, canY, canY];
var canW = Math.ceil( * k); //在畫布中的寬度
this.canW = [canW, canW, canW];
var canH = Math.ceil( * k); //在畫布中的高度
this.canH = [canH, canH, canH];
//下面三個變量是用來協助記住是在三個狀态中的哪個狀态,後面一看就知道了
this.index = ;
this.count = ;
this.step = ;
//表示小鳥飛行的時間,後面知道用途
this.t = ;
//記住初始y坐标,也是後面一看就知道了
this.y = [canY, canY, canY];
}
定義類的好處就是可以不用設定那麼多的全局變量,你可以直接定義小鳥為一個對象,接着定義小鳥畫圖方法:
Bird.prototype.draw = function(){
var index = this.index;
//翅膀拍動, this.count就是用來控制拍動的頻率,記住定時器1秒運作16幀,頻率很快的
this.count++;
if(this.count == ){
this.index += this.step;
this.count = ;
}
//this.index的變化過程為0、1、2、1、0、1、2、1...是以需要this.index +1和-1變化
if((this.index == && this.step == ) || (this.index == && this.step) == -){
this.step = - this.step;
}
//計算垂直位移,使用公式 y = a * t * (t - c),這裡就知道了this.t是代表着小鳥起跳後到現在的時間
//我使用了抛物線的函數方程,你也可以自己選擇,代碼下面我會給出函數坐标圖就很清除了
var c = * ;
var minY = - * viewSize.height / ;
var a = -minY * / (c * c);
var dy = a * this.t * (this.t - c); //dy是小鳥的位移
//下面是小鳥飛到頂部的情況,我的處理是,使再點選失效,要小鳥飛下來才能繼續點選
if(this.y[] + dy < ){
canClick = false;
}else{
canClick = true;
}
//然後小鳥在畫布的y坐标就等于原先的y坐标加上位移
for(var i = ; i < ; 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 = ;
this.imgW = ;
this.imgH = ;
this.canX = viewSize.width; //預設在畫布的最右邊
this.canW = Math.ceil( / * viewSize.width);
this.canH = Math.ceil(this.canW * / );
}
//其中top我們随機生成,代表的是同一組水管中,上水管的左下角在畫布中的y坐标
//上水管類
function UpPie(top){
Pie.call(this); //繼承相同的屬性
this.imgX = ; //上水管在原圖中的x坐标
this.canY = top - this.canH; //上水管在畫布中的y坐标計算
this.draw = drawPie;
};
//下水管類
function DownPie(top){
Pie.call(this);
this.imgX = ;
this.canY = top + Math.ceil( / * viewSize.height); //上水管和下水管的距離固定,大小可調
this.draw = drawPie;
}
function drawPie(){
var speed = * 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( / * viewSize.height),
maxTop = Math.ceil( / * viewSize.height),
top = minTop + Math.ceil(Math.random() * (maxTop - minTop));
Pies.push(new UpPie(top));
Pies.push(new DownPie(top));
};
//畫水管時,首先判斷
//第一組水管出左邊螢幕,移除水管
if(Pies[].canX <= -Pies[].canW && Pies.length == ){
Pies[] = null;
Pies[] = null;
Pies.shift();
Pies.shift();
canCount = true;
}
//第一組水管到達中間時建立水管
if(Pies[].canX <= * (viewSize.width - Pies[].canW) && Pies.length == ){
createPie();
}
//然後就可以畫水管
for(var i = , len = Pies.length; i < len; i++){
Pies[i].draw();
}
4)畫分數,比較簡單,主要是需要計算居中:
/**
* 分數類
*/
function Score(){
this.imgX = ;
this.imgY = ;
this.imgW = ;
this.imgH = ;
this.canW = Math.ceil( * k);
this.canH = Math.ceil( * k);
this.canY = Math.ceil( / * viewSize.height);
this.canX = Math.ceil(viewSize.width / - this.canW / );
this.score = ;
}
Score.prototype.draw = function(){
var aScore = ('' + this.score).split('');
var len = aScore.length;
//計算一下居中
this.canX = * (viewSize.width - (this.canW + ) * len + );
for(var i = ; i < len; i++){
var num = parseInt(aScore[i]);
if(num < ){
var imgX = this.imgX + num * ;
var imgY = ;
}else{
var imgX = this.imgX + (num - ) * ;
var imgY = ;
}
var canX = this.canX + i * (this.canW + );
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 = ;
this.imgY = ;
this.imgH = ;
this.imgW = ;
this.canH = Math.ceil( * k);
this.canW = Math.ceil(k * );
this.canX = ;
this.canY = viewSize.height - this.canH;
}
Ground.prototype.draw = function(){
if(this.imgX > ) this.imgX = ; //因為無限滾動,是以需要無痕接上
ctx.drawImage(img, this.imgX, this.imgY, this.imgW, this.imgH,
this.canX, this.canY, this.canW, this.canH);
this.imgX += ;
};
畫的時候執行個體就可以了:
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 = ; i < ; i++){
bird.y[i] = bird.canY[i];
}
bird.t = ;
}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[] + bird.canH[] >= 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[],
bottom: bird.canY[] + bird.canH[],
left: bird.canX[],
right: bird.canX[] + bird.canW[]
};
for(var i = , 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[]) > Math.floor(Pies[].canX + Pies[].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[] + bird.canH[] >= ground.canY){
console.log(viewSize)
console.log(bird.canY[],bird.canH[],ground.canY)
gameover = true;
return;
}
//水管碰撞
var birdRect = {
top: bird.canY[],
bottom: bird.canY[] + bird.canH[],
left: bird.canX[],
right: bird.canX[] + bird.canW[]
};
for(var i = , 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[]) > Math.floor(Pies[].canX + Pies[].canW) && canCount){
canCount = false;
score.score++;
};
}
現在遊戲已經可以玩了,就是還差gameover處理,和重新開始處理了
第五步:gameover處理:
//畫gameover字樣
ctx.drawImage(img, , , , , Math.ceil(viewSize.width * - k * * ),
Math.ceil( / * viewSize.height), * k, * k);
//畫重新開始點選按鈕
ctx.drawImage(img, , , , , Math.ceil(viewSize.width * - k * * ),
Math.ceil( / * viewSize.height), * k, * k)
//因為有重新點選開始,是以在html中有個隐藏的div用來點選重新開始,現在讓它出現
startBtn.style.display = 'block';
startBtn.style.width = * k + 'px';
startBtn.style.height = * k + 'px';
startBtn.style.left = Math.ceil(viewSize.width * - k * * ) + 'px';
startBtn.style.top = Math.ceil( / * viewSize.height) + 'px';
//消除定時器
cancelAnimationFrame(timer); //如果用setTimeout就是:cleatTimeout(timer)
//回收資源
ground = null;
bird = null;
score = null;
for(var i = , 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);
//阻止冒泡到document
var 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:;
margin:;
height:%;
width:%;
backgroung:#f1f1f1;
cursor:pointer;
overflow: hidden;
}
canvas{
position:relative;
z-index:;
}
#restart{
position:absolute;
top:;left:;
z-index:;
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 * / ;
}
pageWidth = pageWidth > ? : pageWidth;
pageHeight = pageHeight > ? : pageHeight;
return {
width: pageWidth,
height: pageHeight
};
})();
(function(){
var lastTime = ;
var prefixes = 'webkit moz ms o'.split(' '); //各浏覽器字首
var requestAnimationFrame = window.requestAnimationFrame;
var cancelAnimationFrame = window.cancelAnimationFrame;
var prefix;
//通過周遊各浏覽器字首,來得到requestAnimationFrame和cancelAnimationFrame在目前浏覽器的實作形式
for( var i = ; 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,則會退到setTimeout
if ( !requestAnimationFrame || !cancelAnimationFrame ) {
requestAnimationFrame = function( callback, element ) {
var currTime = new Date().getTime();
//為了使setTimteout的盡可能的接近每秒60幀的效果
var timeToCall = Math.max( , - ( currTime - lastTime ) );
var id = window.setTimeout( function() {
callback( currTime + timeToCall );
}, timeToCall );
lastTime = currTime + timeToCall;
return id;
};
cancelAnimationFrame = function( id ) {
window.clearTimeout( id );
};
}
//得到相容各浏覽器的API
window.requestAnimationFrame = requestAnimationFrame;
window.cancelAnimationFrame = cancelAnimationFrame;
})()
var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
img = new Image(),
k= viewSize.height / ,
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 = , len = Pies.length; i < len; i++){
Pies[i] = null;
}
Pies = [];
}
/**
* 開始遊戲
*/
function start(){
check();
if(gameover){
console.log()
ctx.drawImage(img, , , , , Math.ceil(viewSize.width * - k * * ), Math.ceil( / * viewSize.height), * k, * k)
ctx.drawImage(img, , , , , Math.ceil(viewSize.width * - k * * ), Math.ceil( / * viewSize.height), * k, * k)
startBtn.style.width = * k + 'px';
startBtn.style.height = * k + 'px';
startBtn.style.left = Math.ceil(viewSize.width * - k * * ) + 'px';
startBtn.style.top = Math.ceil( / * viewSize.height) + 'px';
startBtn.style.display = 'block';
cancelAnimationFrame(timer);
destroy();
}else{
//清除
ctx.clearRect(,,viewSize.width,viewSize.height);
//畫背景
ctx.drawImage(img, , , , , , , Math.ceil(k * ), viewSize.height);
if(isStarted){
//第一組水管出左邊螢幕,移除水管
if(Pies[].canX <= -Pies[].canW && Pies.length == ){
Pies[] = null;
Pies[] = null;
Pies.shift();
Pies.shift();
canCount = true;
}
//畫小鳥
bird.draw();
//建立水管
if(Pies[].canX <= * (viewSize.width - Pies[].canW) && Pies.length == ){
createPie();
}
//畫水管
for(var i = , len = Pies.length; i < len; i++){
Pies[i].draw();
}
}else{
//畫ready
ctx.drawImage(img, , , , , Math.ceil(viewSize.width * - k * * ), Math.ceil( / * viewSize.height), * k, * k)
ctx.drawImage(img, , , , , Math.ceil(viewSize.width * - k * * ), Math.ceil( / * viewSize.height), * k, * 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[] + bird.canH[] >= ground.canY){
console.log(viewSize)
console.log(bird.canY[],bird.canH[],ground.canY)
gameover = true;
return;
}
//水管碰撞
var birdRect = {
top: bird.canY[],
bottom: bird.canY[] + bird.canH[],
left: bird.canX[],
right: bird.canX[] + bird.canW[]
};
for(var i = , 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[]) > Math.floor(Pies[].canX + Pies[].canW) && canCount){
canCount = false;
score.score++;
};
}
/**
* 點選
*/
document.ontouchstart = document.onmousedown = function(e){
if(gameover) return;
if(isStarted){
if(canClick){
for(var i = ; i < ; i++){
bird.y[i] = bird.canY[i];
}
bird.t = ;
}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 = ;
this.imgY = ;
this.imgW = ;
this.imgH = ;
this.canW = Math.ceil( * k);
this.canH = Math.ceil( * k);
this.canY = Math.ceil( / * viewSize.height);
this.canX = Math.ceil(viewSize.width / - this.canW / );
this.score = ;
}
Score.prototype.draw = function(){
var aScore = ('' + this.score).split('');
var len = aScore.length;
this.canX = * (viewSize.width - (this.canW + ) * len + );
for(var i = ; i < len; i++){
var num = parseInt(aScore[i]);
if(num < ){
var imgX = this.imgX + num * ;
var imgY = ;
}else{
var imgX = this.imgX + (num - ) * ;
var imgY = ;
}
var canX = this.canX + i * (this.canW + );
ctx.drawImage(img, imgX, imgY, this.imgW, this.imgH, canX, this.canY, this.canW, this.canH);
}
};
/**
* 小鳥類
*/
function Bird(){
this.imgX = [, , ];
this.imgY = [, , ];
this.imgW = [, , ];
this.imgH = [, , ];
this.index = ;
this.count = ;
this.step = ;
var canX = Math.ceil( / * viewSize.width);
this.canX = [canX, canX, canX];
var canY = Math.ceil( / * viewSize.height);
this.canY = [canY, canY, canY];
var canW = Math.ceil( * k);
this.canW = [canW, canW, canW];
var canH = Math.ceil( * k);
this.canH = [canH, canH, canH];
this.t = ;
this.y = [canY, canY, canY];
}
Bird.prototype.draw = function(){
var index = this.index;
//翅膀拍動
this.count++;
if(this.count == ){
this.index += this.step;
this.count = ;
}
if((this.index == && this.step == ) || this.index == && this.step == -) this.step = - this.step;
//計算垂直位移,使用公式 y = a * t * (t - c)
var c = * ;
var minY = - * viewSize.height / ;
var a = -minY * / (c * c);
var dy = a * this.t * (this.t - c);
if(this.y[] + dy < ){
canClick = false;
}else{
canClick = true;
}
for(var i = ; i < ; 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 = ;
this.imgW = ;
this.imgH = ;
this.canX = viewSize.width;
this.canW = Math.ceil( / * viewSize.width);
this.canH = Math.ceil(this.canW * / );
}
/**
* 上水管類
*/
function UpPie(top){
Pie.call(this);
this.imgX = ;
this.canY = top - this.canH;
this.draw = drawPie;
};
UpPie.prototype = new Pie();
/**
* 下水管類
*/
function DownPie(top){
Pie.call(this);
this.imgX = ;
this.canY = top + Math.ceil( / * viewSize.height);
this.draw = drawPie;
}
DownPie.prototype = new Pie();
function drawPie(){
var speed = * 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( / * viewSize.height),
maxTop = Math.ceil( / * viewSize.height),
top = minTop + Math.ceil(Math.random() * (maxTop - minTop));
Pies.push(new UpPie(top));
Pies.push(new DownPie(top));
};
/**
* 地闆類
*/
function Ground(){
this.imgX = ;
this.imgY = ;
this.imgH = ;
this.imgW = ;
this.canH = Math.ceil( * k);
this.canW = Math.ceil(k * );
this.canX = ;
this.canY = viewSize.height - this.canH;
}
Ground.prototype.draw = function(){
if(this.imgX > ) this.imgX = ;
ctx.drawImage(img, this.imgX, this.imgY, this.imgW, this.imgH, this.canX, this.canY, this.canW, this.canH);
this.imgX += ;
};