zoukankan      html  css  js  c++  java
  • 用html5实现的flappy-bird

    可能网上早就有几个flappy-bird的html5版本啦,到这个时候flappy-bird可能也没有之前那么火了,但是作为一个新手,自己思考,自己动手写一个flappy-bird的demo还是很有成就感的。

    flappy-bird的html5版无非是通过canvas来画的,可能网上也有webgl版本的,但是我貌似没见过,如果你发现了,希望告诉我一声,咱们一起讨论讨论。之前在微博上看到有大神用60几行就写出了一个demo,这让我写完之后发现自己的demo有将近200多行的代码,瞬间让我对大神们膜拜的五体投地,当然我的代码也可以精简到几十行,不过那样写出来,不便于维护,对于新人也很难看懂。

    html代码我就不写了,大家也都知道,如果你连html代码也需要的话,那你接下来也就没必要看了,还不如直接跳转到w3school.com.cn。

    接下来就是重点的js了,至于css吗?你知道的css对于canvas是无效的,那我干嘛还写css呢,这不是浪费生命吗

    一开始先是定义bird对象,建议用构造函数的方法,当然你也可以用工厂函数,这没什么关系的 

    1. var Bird = function (param) {  
    2.                 this.x = param.x || 0;  
    3.                 this.y = param.y || 0;  
    4.                 this.w = param.w;  
    5.                 this.h = param.h;  
    6.                 this.yDir = param.yDir || 1;  
    7.                 this.img = param.img;  
    8.   
    9.                 return this;  
    10.             }  


    创建一个bird的构造函数,传入参数param 并返回this,参数param是相关的配置参数。

    接下来是bird的draw属性,这个属性主要是将bird给画出来 

    1. Bird.prototype.draw = function () {  
    2.   
    3.                 ctx.drawImage(this.img, 0, 0, this.img.width, this.img.height, this.x, this.y, this.w, this.h);  
    4.   
    5.                 return this;  
    6.             };  


    ok,就这么简单,只是简单的调用canvas的drawImage方法

    接下来就是bird的jump属性,这个属性主要是控制bird的下滑,模仿小鸟的下降 

    1. Bird.prototype.jump = function () {  
    2.                 this.y += this.yDir;  
    3.                 this.draw();  
    4.   
    5.                 return this;  
    6.             }  


    没错,还是这么简单,就是修改y参数,然后调用draw方法,额,其实jump方法和draw方法是可以合并的,不过为了以后的扩展或者,修改方便,我还是选择分开了。当然若是合并了,我的代码又可以少几行了。

    上面就完成了bird对象的定义,没错,已经完成了,就一点代码而已。没有太多

    接下来是水管对象的定义,不过我人太懒,实在是不想找水管的那个图片,所以我就偷工减料,用了几个盒子来代替水管,原理是一样的,但是视觉效果,你懂的,就比如我身边的一个女性同学说的,程序猿能有什么美感!大家将就着吧

    定义盒子对象 

    1. var Box = function (x, y) {  
    2.                 this.x = x || boxOption.x;  
    3.                 this.y = y || boxOption.y;  
    4.                 this.w = boxOption.w;  
    5.                 this.h = boxOption.h;  
    6.                 this.img = boxOption.img;  
    7.                 this.visible = true;  
    8.   
    9.                 return this;  
    10.             };  


    是不是觉得和bird很像,不过就是多了visible属性,这个属性是控制盒子的看见与否,在游戏中的小鸟通过的的水管之间的空隙就是靠它了,

    还是要定义它几个方法,不对,它只有一个方法 

    1. Box.prototype.draw = function () {  
    2.   
    3.                 // console.log([this.img, this.img.width, this.img.height, this.x, this.y, this.w, this.h]);  
    4.                 ctx.drawImage(this.img, 0, 0, this.img.width, this.img.height, this.x, this.y,  
    5.                     this.w, this.h);  
    6.   
    7.             };  


    有没有觉得这个方法和bird的draw方法一样,没错是一样的,其实我应该让box继承bird对象,这样我的代码有可以少几行了

    好了,不谈代码行数的问题了,伤心

    接下来是pipe的对象,它不过是box的一个集合 

    1. var pipe = function (posX, xDir, maxNum) {  
    2.                 this.x = posX;  
    3.                 this.xDir = xDir;  
    4.                 var boxList = [];  
    5.                 var box = new Box(0, 0);  
    6.                 var boxW = box.w,  
    7.                     boxH = box.h;  
    8.                 var boxTmp;  
    9.                 var maxNum = maxNum || Math.ceil(canvas.height / boxW);  
    10.   
    11.                 for (var i = 0; i < maxNum; i++) {  
    12.                     boxTmp = new Box(posX, i * boxH);  
    13.                     boxList.push(boxTmp);  
    14.                 }  
    15.   
    16.                 this.obj = boxList;  
    17.                 this.boxW = boxW;  
    18.                 return this;  
    19.             };  


    this.obj这个属性就是box数组

    和前面一样,pipe也有个draw属性 

    1. pipe.prototype.draw = function () {  
    2.                 var box;  
    3.                 for (var i = 0; i < this.obj.length; i++) {  
    4.                     box = this.obj[i];  
    5.                     box.x = this.x;  
    6.                     if (box.visible) {  
    7.                         box.draw();  
    8.                     }  
    9.                 }  
    10.                 return this;  
    11.             };  


    就是将this.obj中的所有box来一次遍历输出,当然box的visible属性必须是可见的

    下面的这个方法是随机隐藏两个连续的box,以便给可伶的小鸟通过,我们是仁慈的,给了两个box的高度,当然如果你要是想虐人的话,建议你只给一个高度 

    1. // 随机隐藏两个连续的箱子  
    2.             pipe.prototype.rand = function () {  
    3.                 for (var i = 0; i < this.obj.length; i++) {  
    4.                     this.obj[i].visible = true;  
    5.                 }  
    6.   
    7.                 var rand = Math.floor(Math.random() *  5) + 1;  
    8.                 // console.log(rand);  
    9.                 this.obj[rand].visible = false;  
    10.                 this.obj[rand + 1].visible = false;  
    11.   
    12.                 return this;  
    13.             };  

    最后一个属性是移动方法,这是让水管进行左右移动的 

    1. pipe.prototype.move = function () {  
    2.                 this.x += this.xDir;  
    3.   
    4.                 // console.log(this.x, this.xDir, this.boxW);  
    5.                 if (this.x < -this.boxW) {  
    6.                     this.x = canvas.width;  
    7.                     this.rand();  
    8.                 }  
    9.                 this.draw();  
    10.                 return this;  
    11.             };  


    ok 基本这样ok了,但是我们是不是忘了什么东西啊,想起来了,我们还没有进行碰撞检测呢,如果不进行碰撞检测,那岂不是开挂了,这当然是不行的

     

    1. // 碰撞函数  
    2.   
    3.             function collision (bird, pipe1) {  
    4.                 var birdx = bird.x,  
    5.                     birdy = bird.y,  
    6.                     birdw = bird.w,  
    7.                     birdh = bird.h;  
    8.   
    9.                 var boxes = pipe1.obj;  
    10.                 var box1, box2, num;  
    11.                 for (var i = 0; i < boxes.length - 1; i++) {  
    12.                     // 找到被隐藏的两个盒子  
    13.                     if (!boxes[i].visible) {  
    14.                         box1 = boxes[i];  
    15.                         box2 = boxes[i + 1];  
    16.                         break;  
    17.                     }  
    18.                 }  
    19.                 var emptyx = box1.x;  
    20.                 var emptyy = box1.y;  
    21.                 var emptyw = box1.w;  
    22.                 var emptyh = box1.h + box2.h;  
    23.   
    24.                 // 检测是否与上半部水管碰撞  
    25.                 console.log([birdx, birdy, birdw, birdh, emptyx, 0, emptyw, box1.y, boxes[0].y]);  
    26.                 var collUp = calculate(birdx, birdy, birdw, birdh, emptyx, 0, emptyw, box1.y);  
    27.                 // 检测是否与下半部水管碰撞  
    28.                 var collDown = calculate(birdx, birdy, birdw, birdh, emptyx, box2.y + box2.h, emptyw, canvas.height - box2.y - box2.h);  
    29.                 // console.log(collUp, collDown);  
    30.                 if (collUp || collDown) {  
    31.                     // alert('game over');  
    32.                     console.log('game over 1111');  
    33.                     console.log(myReq);  
    34.                     stop();  
    35.                 }  
    36.   
    37.                 if (birdy > canvas.height - birdh) {  
    38.                     console.log('game over   222');  
    39.                     console.log(myReq);  
    40.                     stop();    
    41.                 }  
    42.             }  
    43.   
    44.             // 计算碰撞函数,默认矩形碰撞  
    45.             function calculate (x1, y1, w1, h1, x2, y2, w2, h2) {  
    46.                 var ax = x1 + w1 / 2,  
    47.                     ay = y1 + h1 / 2,  
    48.                     bx = x2 + w2 / 2,  
    49.                     by = y2 + h2 / 2;  
    50.                 var collX = false, collY = false;  
    51.   
    52.                 (Math.abs(bx - ax) < (w1 + w2) / 2) && (collX = true);  
    53.                 (Math.abs(by - ay) < (h1 + h2) / 2) && (collY = true);  
    54.   
    55.                 return collX && collY;  
    56.             }  

    这样就基本ok了,接下来也只是一些,初始化而已,这个直接上代码吧

      

    1. var count = 0, timeout, myReq = 0, stopped, requestId = 0;  
    2.             function render() {  
    3.                 if (!stopped) {  
    4.                     ctx.fillStyle = '#ccc';  
    5.                     ctx.fillRect(0, 0, canvas.width, canvas.height);  
    6.                     bird.jump();  
    7.                     pipe1.move();  
    8.                     // 检测碰撞  
    9.                     collision(bird, pipe1);  
    10.                     requestId = window.requestAnimationFrame(render);  
    11.                     console.log(requestId);  
    12.                 }  
    13.             }  
    14.   
    15.             // 绑定鼠标事件  
    16.             document.onclick = function () {  
    17.                 bird.y -= 25;  
    18.             }  
    19.             function start() {  
    20.                 requestId = window.requestAnimationFrame(render);  
    21.                 stopped = false;  
    22.                 // console.log(requestId);  
    23.   
    24.             }  
    25.   
    26.             function stop() {  
    27.                 if (requestId) {  
    28.                     window.cancelAnimationFrame(requestId);  
    29.                 }  
    30.                 stopped = true;  
    31.                 // console.log(requestId);  
    32.             }  
    33.   
    34.             start();  


    效果如下图所示

    完整代码请访问我的github

    https://github.com/chenkehxx/mytest/blob/master/flappy-bird/flappy-bird.html

  • 相关阅读:
    互评
    201671010438 王奕晗英文文本词频统计
    201671010438王奕晗 实验二词频统计
    201671010438王奕晗 实验三 作业互评与改进
    通读《构建之法》所提出的问题
    个人学习总结博客(201671010440 王雨竹)
    互评
    201671010440王雨竹+《英文文本统计分析》
    201671010440 王雨竹 词频统计软件项目报告
    201671010440 王雨竹 实验三 作业互评与改进
  • 原文地址:https://www.cnblogs.com/web100/p/html5-flappy-bird.html
Copyright © 2011-2022 走看看