好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

VUE实现一个Flappy Bird游戏的示例代码

Flappy Bird是一个非常简单的小游戏,在app上大家都玩过。这里就用VUE来实现一个简单的PC版Flappy Bird,娱乐一下~~~~~

要实现这个游戏,首先来分析一下游戏界面里哪几块东西需要动起来:

1、第一当然就是上下移动的小鸟;

2、横向移动的背景图,让小鸟看起来在横向飞行;

3、从画面右端进入的一排排管道。

这样很明确了,我们让上面3块内容按照规律运动起来,然后再加上规则边界判断和计分,就可以得到一个完整的游戏。所以就一块块来解决。

先来定义一些常量和变量:

?

let rafId = null ; // requestAnimationFrame的ID

let startSpeed = 1;

const SPEED = 0.04; // 加速度

const UP = 5.0; // 速度累加上限

const UP_SUM = 30; // 按一次跳跃的高度

const BIRD_WIDTH = 50; // 小鸟图片宽50px

const PIPE_DISTANCE = 350; // 管道之间的横向距离

let id = 0; // 管道唯一id,从0开始计数

 

...

 

data() {

     return {

       start: false ,

       clientWidth: 0,

       clientHeight: 0,

       spaceHeight: [240, 200, 160], // 上管道与下管道之间的距离

       pipeArr: [], // 管道数组

       score: 0, // 得分

       jumpSum: 0, // 当前跳跃相对高度

       jumpFlag: false , // true-按下空格键跳跃上升阶段;false-自由落体阶段

       dropBirdImg: require( "@/assets/img/bird/bird0_0.png" ),

       flyBirdImg: require( "@/assets/img/bird/bird0_2.png" ),

       gameOver: false , // 游戏失败的flag,用于停止动画帧

     };

},

1、上下移动的小鸟

为了分别控制小鸟和管道的位置,元素定位均采用position: absolute

小鸟本身就是个div+背景图,然后定义一下在界面里的初始位置:

?

< div class = "bird" id = "bird" ref = "bird" ></ div >

 

  #bird {

       height: 50px;

       width: 50px;

       border-radius: 50%;

       background: url("~assets/img/bird/bird0_1.png") no-repeat center/contain;

       // 小鸟初始位置

       position: absolute;

       left: 300px;

       top: 300px;

}

然后,在什么都不操作的情况下,小鸟从初始位置开始"坠落",小鸟的坠落是一个越落越快的过程,在这里我没有用物理的重力加速度公式,只是简单模拟了一个曲线加速过程。这是一个持续的动画,所以把这个动作放在动画帧里,即requestAnimationFrame,每一帧的函数定义为loop()。

所以在loop函数中,根据offsetTop和父元素的clientHeight来判断小鸟是否触碰到了画面的上下边界,是则游戏结束;否,则增加style.top让小鸟坠落。

?

loop() {

       let _this = this ;

       if (_this.jumpFlag) {

         // 小鸟跳跃

         _this.jump();

       }

       let top = _this.$refs.bird.offsetTop;

       if (top > _this.clientHeight - BIRD_WIDTH || top <= 0) {

         // 碰到边界,游戏结束

         _this.resetGame();

       } else if (!_this.jumpFlag) {

         _this.$refs.bird.style.background = `url( '${_this.dropBirdImg}' ) no-repeat center/contain`;

         _this.$refs.bird.style.top = top + startSpeed * startSpeed + "px" ; // 模拟加速坠落

         if (startSpeed < UP) {

           startSpeed += SPEED;

         }

       }

       _this.pipesMove(); // 管道移动

}

游戏中,玩家按下空格键,小鸟会向上跳跃一段距离,用this.jumpFlag[true/false]来记录这一状态,当按下时,置为true,loop函数中小鸟jump(),在jump到一定距离后,jumpFlag置为false,小鸟开始坠落。

所以,jump函数很容易实现:

?

jump() {

       let _this = this ;

       _this.$refs.bird.style.background = `url( '${_this.flyBirdImg}' ) no-repeat center/contain`;

       if (_this.jumpSum > UP_SUM) {

         // 到顶部就落下

         _this.jumpFlag = false ;

         _this.jumpSum = 0;

         startSpeed = 1;

       } else {

         _this.$refs.bird.style.top = _this.$refs.bird.offsetTop - 8 + "px" ;

         _this.jumpSum += 8;

       }

}

2、横向移动的背景图

这个比较简单,就是background-position无限循环切换就行了,位置根据自己下载的背景图素材宽度决定。

?

animation: bgMove 8 s linear infinite;

       @keyframes bgMove {

         0% {

           background-position : 805px 0 ;

         }

         100% {

           background-position : 0 0 ;

         }

}

经过这两步,我们就可以得到一个正在飞行的小鸟了,用document.onkeydown监听空格键来切换jumpFlag,如下图:

3、从右往左一移动进入管道

管道是由上下两个div组成,每个div通过不同的top: -xx和bottom: -yy实现中间有间隙。

首先实现生成一个随机间隙管道的函数,管道存放在pipeArr对象数组中:

?

addPipe(id) {

       let obj = {};

       let top_num = this .sum(10, 170);

       let height = this .spaceHeight[

         Math.floor(Math.random() * this .spaceHeight.length)

       ]; // 随机选取间隙值

       let bottom_num = height - top_num;

       obj.top = top_num;

       obj.id = id;

       obj.right = -(PIPE_DISTANCE / 2);

       obj.bottom = bottom_num;

       this .pipeArr.push(obj);

},

sum(m, n) {

       // 随机n-m之间的数字

       return Math.floor(Math.random() * (m - n) + n);

}

然后需要将管道移动起来,即loop()中管道移动函数pipesMove(),整个函数实现如下:

?

pipesMove() {

       let _this = this ;

       if (_this.pipeArr.length === 0) {

         return ;

       }

       let right0 = _this.pipeArr[0].right;

       if (right0 > this .clientWidth + 300) {

         this .pipeArr.shift();

       }

       let right_last = _this.pipeArr[_this.pipeArr.length - 1].right;

       if (right_last >= PIPE_DISTANCE / 2) {

         id++;

         this .addPipe(id);

       }

       for (let i = 0; i < _this.pipeArr.length; i++) {

         // 判断一下小鸟是否接触到了管道,小鸟50*50,left:300px;管道宽100px;管道进入范围right是width-450到width-300

         if (

           _this.pipeArr[i].right >= _this.clientWidth - 450 &&

           _this.pipeArr[i].right <= _this.clientWidth - 300

         ) {

           // 该管道进入了小鸟触碰范围

           let bird_top = _this.$refs.bird.offsetTop;

           // 12是小鸟图片素材上下有空白间隙

           if (

             bird_top <= _this.clientHeight / 2 - _this.pipeArr[i].top - 12 ||

             bird_top >=

               _this.clientHeight / 2 + _this.pipeArr[i].bottom - BIRD_WIDTH + 12

           ) {

             // 碰到了管道

             _this.resetGame();

             return ;

           }

         }

         if (_this.pipeArr[i].right === _this.clientWidth - 300 && _this.pipeArr[i].right === _this.clientWidth - 301) { // 当某个管道刚好在小鸟左边,证明小鸟通过该管道,根据管道id算出小鸟得分

           _this.score = _this.pipeArr[i].id + 1;

         }

         _this.pipeArr[i].right = _this.pipeArr[i].right + 2; // 管道每帧移动2px

       }

}

这里做了五件事:

(1)管道出了左边画面后shift()最左的管道;

(2)最右的管道离画面右侧一定距离后,加入新的一根管道;

(3)循环遍历中,判断小鸟是否进入了某一根管道的范围,判断小鸟top是否有触碰到上下管道,触碰则输;

(4)当某一个管道刚好位于小鸟左侧时,证明小鸟成功通过,分数+1;

(5)每个管道移动2px像素,数值记录在right属性里。

通过DOM里:style设置right就可以使得管道横向移动了

?

<section class= "pipes-wrap" ref= "pipes" >

           <div

             class= "pipe-item"

             v- for = "(item, index) in pipeArr"

             :key= "item.id"

             :id= "'pipe' + index"

             :style= "'right:' + item.right + 'px;'"

           >

             <div

               class= "pipe pipe-top"

               :style= "'top:-' + item.top + 'px;'"

             ></div>

             <div

               class= "pipe pipe-bottom"

               :style= "'bottom:-' + item.bottom + 'px;'"

             ></div>

           </div>

</section>

 

.pipes-wrap {

         position: relative;

         height: 100%;

         overflow: hidden;

         .pipe-item {

           position: absolute;

           height: 100%;

           width: 100px;

           .pipe {

             width: 100%;

             height: 50%;

             position: relative;

           }

           .pipe-top {

             background: url( '"~assets/img/bird/pipe_down.png' ) no-repeat;

             background-size: 100px;

             background-position: bottom;

           }

           .pipe-bottom {

             background: url( '"~assets/img/bird/pipe_up.png' ) no-repeat;

             background-size: 100px;

             background-position: top;

           }

         }

}

以上就是vue实现flappy bird的思路和核心代码了,总共也就两百多行代码。在我看来,难点主要集中在管道的移动、触碰判定以及分数计算上。当然代码里还有很多可以优化的不足点,共勉~~

原文链接:https://blog.csdn.net/denglouhen/article/details/114933374

dy("nrwz");

查看更多关于VUE实现一个Flappy Bird游戏的示例代码的详细内容...

  阅读:39次