JavaScript实现Fly Bird小游戏

向右看齐 2022-07-10 05:28 312阅读 0赞

转载自:http://www.jianshu.com/p/45d994d04a25

1.分析页面结构,理清需求和功能

游戏有三个界面,分别是开始界面,游戏界面和游戏结束界面。

1.1 开始界面

strip

start.gif

  • 游戏的大背景
  • 上下移动的游戏标题和翅膀摆动的小鸟
  • start 按钮,点击进入游戏界面
  • 一直移动的地面

    1.2 游戏界面

strip 1

play.gif

  • 显示越过障碍数量的计分器
  • 移动的障碍物,分别是上管道和下管道
  • 点击游戏界面,小鸟向上飞起,然后在重力作用下下坠,
  • 当小鸟和管道碰撞后,结束界面弹出,同时小鸟落到地面

    1.3 结束界面

  • Game over 提示面板
  • OK 按钮

2. 开发“开始界面”

考虑到草地的移动效果,我们在页面中加入两个草地

2.1 HTML

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8" />
  5. <title>Fly Bird</title>
  6. <link rel="stylesheet" type="text/css" href="css/index.css"/>
  7. </head>
  8. <body>
  9. <div id="wrapBg"> <!--游戏背景-->
  10. <div id="headTitle"> <!--开始标题-->
  11. <img id="headBird" src="img/bird0.png" alt="小鸟" /> <!--标题中的小鸟-->
  12. </div>
  13. <button id="startBtn" ></button> <!--开始按钮-->
  14. <div id="grassLand1"></div> <!--草地1-->
  15. <div id="grassLand2"></div> <!--草地2-->
  16. </div>
  17. </body>
  18. </html>

2.2 CSS

  1. #wrapBg{ /*游戏背景*/ width: 343px;height: 480px; margin: 0 auto; background-image:url(../img/bg.jpg); position: relative; top: 100px; overflow: hidden; } #headTitle{ /*开始标题*/ width: 236px;height: 77px; background-image: url(../img/head.jpg); position: absolute; left: 53px; top: 100px; } #headBird{ /*开始标题中的小鸟*/ float:right; margin-top: 25px; } #startBtn{ /*开始按钮*/ width: 85px;height: 29px; padding: 0;margin: 0; background-image: url(../img/start.jpg); position: absolute;left: 129px;top: 250px; } #grassLand1{ /*草地1*/ height: 14px;width: 343px; background-image: url(../img/banner.jpg); position: absolute;top: 423px; } #grassLand2{ /*草地2*/ height: 14px;width: 343px; background-image: url(../img/banner.jpg); position: absolute;top: 423px;left: 343px; }

将wrapBg中的overflow:hidden 注释掉的页面效果

1240

开始界面.jpg

2.3 JS

小鸟煽动翅膀的效果需要用到逐帧动画的原理

逐帧动画是一种常见的动画形式(Frame By Frame),其原理是在“连续的关键帧”中分解动画动作,也就是在时间轴的每帧上逐帧绘制不同的内容,使其连续播放而成动画。

1240 1

bird1.png

1240 2

bird0.png

2.3.1 开始标题的摆动
  1. var jsHeadTitle = document.getElementById("headTitle");// 获取标题 var jsHeadBird = document.getElementById("headBird"); // 获取标题中小鸟 var Y = 3;//标题的摆动幅度 var index = 0; var imgArr = ["img/bird0.png","img/bird1.png"] //将小鸟图片路径放入一个数组,利用逐帧动画的原理做出小鸟翅膀摆动的样子 var headWaveTimer = setInterval(headWave,200); //设置标题上下摆动的定时器 function headWave() { Y *= -1; jsHeadTitle.style.top = jsHeadTitle.offsetTop + Y + "px"; jsHeadBird.src = imgArr[index++]; if (index == 2) { index = 0; } }
2.3.2 移动的草地
  1. var jsGrassLand1 = document.getElementById("grassLand1"); //获取草地1
  2. var jsGrassLand2 = document.getElementById("grassLand2"); //获取草地2
  3. var landTimer = setInterval(landRun,30); //让草地动起来的定时器
  4. function landRun() {
  5. if (jsGrassLand1.offsetLeft <= -343) {
  6. jsGrassLand1.style.left = "343px";
  7. }
  8. if (jsGrassLand2.offsetLeft <= -343) {
  9. jsGrassLand2.style.left = "343px";
  10. }
  11. jsGrassLand1.style.left = jsGrassLand1.offsetLeft - 3 + "px";
  12. jsGrassLand2.style.left = jsGrassLand2.offsetLeft - 3 + "px";
  13. }

2.3.3 Start按键

  1. var jsStartBtn = document.getElementById("startBtn"); jsStartBtn.onclick = function() { //为start按键添加点击事件处理程序 jsHeadTitle.style.display = "none"; //隐藏标题 clearInterval(headWaveTimer); //关闭让标题摆动的定时器 jsStartBtn.style.display = "none"; //隐藏按键 //待添加功能 //点击开始按键进入游戏界面 }

完成后的效果(注释掉了wrapBg中的overflow:hidden )

strip 2

start01.gif

接下来我们开发“游戏界面”

3. “游戏界面”的开发

游戏界面中有三样元素,分别是“小鸟”,“障碍”,和“计分器”,我们依次来创建相应的对象。

3.1 小鸟

首先,创建小鸟的对象, bird.js 文件。

  1. var bird = { flyTimer:null,//小鸟飞翔定时器 wingTimer:null,//小鸟翅膀摆动定时器 div:document.createElement("div"), showBird:function(parentObj) { this.div.style.width = "40px"; this.div.style.height = "28px"; this.div.style.backgroundImage = "url(img/bird0.png)"; this.div.style.backgroundRepeat = "no-repeat"; this.div.style.position = "absolute"; this.div.style.left = "50px"; this.div.style.top = "200px"; this.div.style.zIndex = "1"; parentObj.appendChild(this.div); //将小鸟DIV插入游戏界面中 }, fallSpeed: 0, //小鸟下落速度 flyBird: function(){ //控制小鸟飞翔下落的函数 bird.flyTimer = setInterval(fly,40); function fly() { bird.div.style.top = bird.div.offsetTop + bird.fallSpeed++ + "px"; if (bird.div.offsetTop < 0) { bird.fallSpeed = 2; //这里用于控制小鸟不要飞出界面 } if (bird.div.offsetTop >= 395) { bird.fallSpeed = 0; clearInterval(bird.flyTimer); //一旦飞到地面,清除定时器 clearInterval(bird.wingTimer); //清除翅膀摆动定时器 } if (bird.fallSpeed > 12) { bird.fallSpeed = 12; //鸟的最大下落速度控制在12 } } }, wingWave: function() { //控制小鸟煽动翅膀的函数 var up = ["url(img/up_bird0.png)", "url(img/up_bird1.png)"]; var down = ["url(img/down_bird0.png)", "url(img/down_bird1.png)"]; var i = 0, j = 0; bird.wingTimer = setInterval(wing,120);//逐帧动画,小鸟煽动翅膀 function wing() { if (bird.fallSpeed > 0) { bird.div.style.backgroundImage = down[i++]; if (i==2) {i = 0} }if (bird.fallSpeed < 0) { bird.div.style.backgroundImage = up[j++]; if (j==2) {j = 0} } } }, };

下面,实现点击start按钮时,加载小鸟。(在之前的代码基础上添加)

  1. jsStartBtn.onclick = function() { //为start按键添加点击事件处理程序
  2. jsHeadTitle.style.display = "none"; //隐藏标题
  3. clearInterval(headWaveTimer); //关闭让标题摆动的定时器
  4. jsStartBtn.style.display = "none"; //隐藏按键
  5. bird.showBird(jsWrapBg); //插入小鸟到界面中
  6. bird.flyBird(); //控制小鸟飞翔下落
  7. bird.wingWave(); //逐帧动画,小鸟煽动翅膀
  8. jsWrapBg.onclick = function(){
  9. bird.fallSpeed = -8;
  10. };
  11. //待添加功能
  12. //点击开始按键进入游戏界面
  13. }

添加小鸟后的效果

strip 3

play01.gif

3.2 障碍(上管道和下管道)

1240 3

block示意图.png

障碍分为上管道和下管道,如示意图所示结构嵌套,这样就可以通过随机设置DownDiv2的高度和gapHeight的高度,来改变生成障碍的形态
block.js

  1. function Block() {
  2. this.upDivWrap = null;
  3. this.downDivWrap = null;
  4. this.downHeight = baseObj.randomNum(0,150);//随机生成0-150之间的数,用于控制下管道的高度
  5. this.gapHeight = baseObj.randomNum(150,160);// 管道中间间隙宽度,通过调节大小,可以的控制游戏难度
  6. this.upHeight = 312 - this.downHeight - this.gapHeight;
  7. // 用来生成Div的方法
  8. this.createDiv = function(url, height, positionType, left, top) {
  9. var newDiv = document.createElement("div");
  10. newDiv.style.width = "62px";
  11. newDiv.style.height = height;
  12. newDiv.style.position = positionType;
  13. newDiv.style.left = left;
  14. newDiv.style.top = top;
  15. newDiv.style.backgroundImage = url; //"url(/img/0.jpg)"
  16. return newDiv;
  17. };
  18. this.createBlock = function() {
  19. var upDiv1 = this.createDiv("url(img/up_mod.png)", this.upHeight + "px");
  20. var upDiv2 = this.createDiv("url(img/up_pipe.png)", "60px");
  21. this.upDivWrap = this.createDiv(null, null, "absolute", "450px");
  22. this.upDivWrap.appendChild(upDiv1);
  23. this.upDivWrap.appendChild(upDiv2);//生成上方管道
  24. var downDiv1 = this.createDiv("url(img/down_pipe.png)", "60px");
  25. var downDiv2 = this.createDiv("url(img/down_mod.png)", this.downHeight +"px");
  26. this.downDivWrap = this.createDiv(null, null, "absolute", "450px", 363 - this.downHeight + "px");
  27. this.downDivWrap.appendChild(downDiv1);
  28. this.downDivWrap.appendChild(downDiv2); //生成下方的管道
  29. jsWrapBg.appendChild(this.upDivWrap);
  30. jsWrapBg.appendChild(this.downDivWrap);
  31. };
  32. this.moveBlock = function() { //控制管道移动的方法
  33. this.upDivWrap.style.left = this.upDivWrap.offsetLeft - 3 + "px";
  34. this.downDivWrap.style.left = this.downDivWrap.offsetLeft - 3 + "px";
  35. };
  36. }

公共对象文件 baseObj.js ,用来提供随机数,和两个矩形div的碰撞检测

  1. var baseObj = { //随机数 randomNum: function(min, max) { return parseInt(Math.random() * (max - min + 1) + min); }, //两个矩形元素之间的碰撞检测 rectangleCrashExamine: function (obj1, obj2) { var obj1Left = obj1.offsetLeft; var obj1Width = obj1.offsetLeft + obj1.offsetWidth; var obj1Top = obj1.offsetTop; var obj1Height = obj1.offsetTop + obj1.offsetHeight; var obj2Left = obj2.offsetLeft; var obj2Width = obj2.offsetLeft + obj2.offsetWidth; var obj2Top = obj2.offsetTop; var obj2Height = obj2.offsetTop + obj2.offsetHeight; if (!(obj1Left > obj2Width || obj1Width < obj2Left || obj1Top > obj2Height || obj1Height < obj2Top)) { return true; } return false; }, };

下面我的想法是在start按钮点击的时候创建一个block,把这个block存储到数组 blocksArr 中,在 landTimer 定时器的方法 landRun 中检查此数组的长度,如果数组不为空数组,那么就让数组中所有的block移动。

检查数组中最后一个block离开的距离,达到一定距离,就重新new 一个block,添加到数组。

检查数组中第一个block,一旦达到一定位置,就在结构中移除downDivWrap 和 upDivWrap,同时在数组中删除block。

  1. var blocksArr = [];
  2. var blockDistance = baseObj.randomNum(130,250); var landTimer = setInterval(landRun,30); //让草地动起来的定时器
  3. function landRun() {
  4. if (jsGrassLand1.offsetLeft <= -343) {
  5. jsGrassLand1.style.left = "343px";
  6. }
  7. if (jsGrassLand2.offsetLeft <= -343) {
  8. jsGrassLand2.style.left = "343px";
  9. }
  10. jsGrassLand1.style.left = jsGrassLand1.offsetLeft - 3 + "px";
  11. jsGrassLand2.style.left = jsGrassLand2.offsetLeft - 3 + "px";
  12. if (blocksArr.length) {
  13. for (var i = 0; i < blocksArr.length; i++) {
  14. blocksArr[i].moveBlock(); var x =baseObj.rectangleCrashExamine(blocksArr[i].downDivWrap, bird.div); var y = baseObj.rectangleCrashExamine(blocksArr[i].upDivWrap, bird.div); var z = bird.div.offsetTop >= 390;
  15. if (x || y || z) {
  16. window.clearInterval(landTimer);//清除landTimer定时器
  17. bird.fallSpeed = 0; //小鸟下落
  18. jsWrapBg.onclick = null; //消除点击事件
  19. }
  20. }
  21. if (blocksArr[blocksArr.length - 1].downDivWrap.offsetLeft < (450 - blockDistance)) {
  22. blockDistance = baseObj.randomNum(130,250); var newBlock = new Block(); newBlock.createBlock();
  23. blocksArr.push(newBlock); }
  24. if (blocksArr[0].downDivWrap.offsetLeft < -50) {
  25. jsWrapBg.removeChild(blocksArr[0].downDivWrap); jsWrapBg.removeChild(blocksArr[0].upDivWrap); blocksArr.shift(blocksArr[0]); }
  26. }
  27. }

当前的游戏效果

strip 4

play02.gif

3.3 计分器

游戏中的计分器相对较好实现,我们就实现最大为三位数的计分器吧。
html

  1. <div id="score">
  2. <div id="num1"></div>
  3. <div id="num2"></div>
  4. <div id="num3"></div>
  5. </div>

css样式

  1. #score{ position:absolute; left: 130px; top:50px; z-index: 1; } #score div{ height: 39px; width: 28px; float: left; background-image: url(../img/0.jpg); display: none; }

js

  1. var jsScore = document.getElementById("score");
  2. var jsNum1 = document.getElementById("num1");
  3. var jsNum2 = document.getElementById("num2");
  4. var jsNum3 = document.getElementById("num3");
  5. var score = 0;

实现计数器功能,最重要的是如何判断走过水管的数量,我们以水管的位置来判断。bird的定位left为50px,水管的宽度是62px,当水管越过小鸟的时候,水管距离它父级的定位 offsetLeft 是 -12px。每当有一个水管到达此位置,score++;
在start按钮的事件处理程序中加入

  1. jsNum1.style.display = "block";// 在点击开始之后,让计数器显示出来。
  2. if (blocksArr[0].downDivWrap.offsetLeft == -12) {
  3. score++;//积分面板 if (score < 10) {
  4. jsNum1.style.backgroundImage = "url(img/" + score + ".jpg)";
  5. } else if (score < 100) {
  6. jsNum2.style.display = "block";
  7. jsNum1.style.backgroundImage = "url(img/" + parseInt(score/10) + ".jpg)";
  8. jsNum2.style.backgroundImage = "url(img/" + score%10 + ".jpg)";
  9. } else if (score < 1000) {
  10. jsNum3.style.display = "block";
  11. jsNum1.style.backgroundImage = "url(img/" + parseInt(score/100) + ".jpg)";
  12. jsNum2.style.backgroundImage = "url(img/" + parseInt(score/10)%10 + ".jpg)";
  13. jsNum3.style.backgroundImage = "url(img/" + score%10 + ".jpg)";
  14. }
  15. console.log(score); }

目前效果 ,计数器功能完成。

strip 5

play03.gif

4.“结束界面”的开发

当小鸟和管道碰撞或者和地面碰撞时候,隐藏计分器,弹出结束面板。
结束界面主要有“结束面板”和“ok”按钮,这里需要为“ok”按钮添加点击事件。

  1. <div id="gameOver">
  2. <img src="img/game_over.jpg" alt="game over" />
  3. <img src="img/message.jpg" alt="message" />
  4. <img id="ok" src="img/ok.jpg" alt="ok" />
  5. </div>
  6. #gameOver{ position: absolute; top: 100px; text-align: center; display: none; z-index: 1; }

为“OK”按钮添加事件

  1. jsOkBtn.onclick = function() { window.location.href = "index.html"; //刷新页面 }

最终效果

strip 6

play04.gif

有兴趣的朋友,可以下载代码,然后加上音效。OK,结束。

GitHub代码下载传送门

好了,下面是广告时间。前端学习群,前200人免费入群。现在还不满,从速哦!入群加 笑笑微信:iamaixiaoxiao

Center

发表评论

表情:
评论列表 (有 0 条评论,312人围观)

还没有评论,来说两句吧...

相关阅读