首页 > 编程语言 >JavaScript实现Fly Bird小游戏

JavaScript实现Fly Bird小游戏

时间:2023-06-30 13:35:23浏览次数:50  
标签:Fly style img url JavaScript 小鸟 小游戏 var bird

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

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

1.1 开始界面

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

1.2 游戏界面

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

    1.3 结束界面

  • GAMEOVER 提示面板OK 按钮
  • OK按钮

    2. 开发“开始界面”

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

  • 2.1 HTML

    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8" />
            <title>Fly Bird</title>
            <link rel="stylesheet" type="text/css" href="css/index.css"/>
        </head>
        <body>
            <div id="wrapBg">  <!--游戏背景-->
                <div id="headTitle"> <!--开始标题-->
                    <img id="headBird" src="img/bird0.png" alt="小鸟" /> <!--标题中的小鸟-->
                </div>
                <button id="startBtn" ></button> <!--开始按钮-->
                <div id="grassLand1"></div> <!--草地1-->
                <div id="grassLand2"></div> <!--草地2-->
            </div>
        </body>
    </html>

    2.2 CSS

    #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 注释掉的页面效果:

    2.3 JS

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

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

    2.3.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 移动的草地
    var jsGrassLand1 = document.getElementById("grassLand1"); //获取草地1
            var jsGrassLand2 = document.getElementById("grassLand2"); //获取草地2
     
            var landTimer = setInterval(landRun,30); //让草地动起来的定时器
            function landRun() {
                if (jsGrassLand1.offsetLeft <= -343) {
                    jsGrassLand1.style.left = "343px";
                }
                if (jsGrassLand2.offsetLeft <= -343) {
                    jsGrassLand2.style.left = "343px";
                }
                jsGrassLand1.style.left = jsGrassLand1.offsetLeft - 3 + "px";
                jsGrassLand2.style.left = jsGrassLand2.offsetLeft - 3 + "px";
            }

    2.3.3 Start按键

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

    3. “游戏界面”的开发

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

    3.1 小鸟

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

    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按钮时,加载小鸟。(在之前的代码基础上添加)

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

    添加小鸟后的效果:

3.2 障碍(上方水管和下方水管)

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

block.js:

function Block() {
    this.upDivWrap = null;
    this.downDivWrap = null;
    this.downHeight = baseObj.randomNum(0,150);
    this.gapHeight = baseObj.randomNum(150,160);
    this.upHeight = 312 - this.downHeight - this.gapHeight;
 
    // 用来生成Div的方法
    this.createDiv = function(url, height, positionType, left, top) {
        var newDiv = document.createElement("div");
        newDiv.style.width = "62px";
        newDiv.style.height = height;
        newDiv.style.position = positionType;
        newDiv.style.left = left;
        newDiv.style.top = top;
        newDiv.style.backgroundImage = url;  //"url(/img/0.jpg)"
        return newDiv;
    };
 
    this.createBlock = function() {
        var upDiv1 = this.createDiv("url(img/up_mod.png)", this.upHeight + "px");
        var upDiv2 = this.createDiv("url(img/up_pipe.png)", "60px");
        this.upDivWrap = this.createDiv(null, null, "absolute", "450px");
        this.upDivWrap.appendChild(upDiv1);
        this.upDivWrap.appendChild(upDiv2);//生成上方管道
 
        var downDiv1 = this.createDiv("url(img/down_pipe.png)", "60px");
        var downDiv2 = this.createDiv("url(img/down_mod.png)", this.downHeight +"px");
        this.downDivWrap = this.createDiv(null, null, "absolute", "450px", 363 - this.downHeight + "px");
        this.downDivWrap.appendChild(downDiv1);
        this.downDivWrap.appendChild(downDiv2); //生成下方的管道
 
        jsWrapBg.appendChild(this.upDivWrap);
        jsWrapBg.appendChild(this.downDivWrap);
    };
 
    this.moveBlock = function() { //控制管道移动的方法
        this.upDivWrap.style.left = this.upDivWrap.offsetLeft - 3 + "px";
        this.downDivWrap.style.left = this.downDivWrap.offsetLeft - 3 + "px";
    };    
}

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

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,同时在数组中删除。

        var landTimer = setInterval(landRun,30); //让草地动起来的定时器
        function landRun() {
            if (jsGrassLand1.offsetLeft <= -343) {
                jsGrassLand1.style.left = "343px";
            }
            if (jsGrassLand2.offsetLeft <= -343) {
                jsGrassLand2.style.left = "343px";
            }
            jsGrassLand1.style.left = jsGrassLand1.offsetLeft - 3 + "px";
            jsGrassLand2.style.left = jsGrassLand2.offsetLeft - 3 + "px";
 
            if (blocksArr.length) {
                for (var i = 0; i < blocksArr.length; i++) {
                    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;
                    if (x || y || z) {
                        window.clearInterval(landTimer);//清除landTimer定时器
                        bird.fallSpeed = 0; //小鸟下落
                        jsWrapBg.onclick = null; //消除点击事件
 
                    }
                }
                if (blocksArr[blocksArr.length - 1].downDivWrap.offsetLeft < (450 - blockDistance)) {
                        blockDistance = baseObj.randomNum(130,250);
                        var newBlock = new Block();
                        newBlock.createBlock();
                        blocksArr.push(newBlock);
                }
 
                if (blocksArr[0].downDivWrap.offsetLeft < -50) {
                        jsWrapBg.removeChild(blocksArr[0].downDivWrap);
                        jsWrapBg.removeChild(blocksArr[0].upDivWrap);
                        blocksArr.shift(blocksArr[0]);
                }
            }
        }

当前的游戏效果:

 

3.3 计分器

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

            <div id="score">
                <div id="num1"></div>
                <div id="num2"></div>
                <div id="num3"></div>
            </div>
        var jsScore = document.getElementById("score");
        var jsNum1 = document.getElementById("num1");
        var jsNum2 = document.getElementById("num2");
        var jsNum3 = document.getElementById("num3");
        var score = 0;

 

#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;

}

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

在start按钮的事件处理程序中加入

jsNum1.style.display = "block";// 在点击开始之后,让计数器显示出来。

if (blocksArr[0].downDivWrap.offsetLeft == -12) {

score++;//积分面板

if (score < 10) {

jsNum1.style.backgroundImage = "url(img/" + score + ".jpg)";

} else if (score < 100) {

jsNum2.style.display = "block";

jsNum1.style.backgroundImage = "url(img/" + parseInt(score/10) + ".jpg)";

jsNum2.style.backgroundImage = "url(img/" + score%10 + ".jpg)";

} else if (score < 1000) {

jsNum3.style.display = "block";

jsNum1.style.backgroundImage = "url(img/" + parseInt(score/100) + ".jpg)";

jsNum2.style.backgroundImage = "url(img/" + parseInt(score/10)%10 + ".jpg)";

jsNum3.style.backgroundImage = "url(img/" + score%10 + ".jpg)";

}

console.log(score);

}

计数器功能完成的效果:

4.“结束界面”的开发

当小鸟和管道碰撞或者和地面碰撞时候,隐藏计分器,弹出结束面板。

结束界面主要有“结束面板”和“ok”按钮,这里需要为“ok”按钮添加点击事件。

#gameOver{

position: absolute;

top: 100px;

text-align: center;

display: none;

z-index: 1;

}

 

为“OK”按钮添加事件

jsOkBtn.onclick = function() {

window.location.href = "index.html"; //刷新页面

}

最终效果:

 

 

 

标签:Fly,style,img,url,JavaScript,小鸟,小游戏,var,bird
From: https://www.cnblogs.com/cloudnine/p/17516472.html

相关文章

  • JavaScript中数组常用方法汇总!
    数组是一个复杂数据类型,我们在操作它的时候就不能再想基本数据类型一样操作了。比如我们想改变一个数组//创建一个数组vararr=[1,2,3]//我们想把数组变成只有1和2arr=[1,2]这样肯定是不合理,因为这样不是在改变之前的数组。相当于重新制作了一个数组给到arr......
  • 简单的贪吃蛇小游戏
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title></head><body><h1>画布canvas</h1><canvasid="gameCanvas"width="600"......
  • 小游戏——贪吃蛇案例
    一、案例目的 1、实现贪吃蛇游戏基本功能,屏幕上随机出现一个“食物”,称为豆子,上下左右控制“蛇”的移动,吃到“豆子”以后“蛇”的身体加长一点。2、“蛇”碰到边界或蛇头与蛇身相撞,蛇死亡,游戏结束。3、为游戏设计友好的交互界面;例如欢迎界面,游戏界面,游戏结束界面。要有开始键......
  • 我制作的一个简单的跳棋小游戏
    我用虚幻5制作的简单的3D跳棋小游戏,可以本地多人对战以及和AI对战,已经上传到了itch.io。目前跳棋功能已经完善,其他棋类游戏留待以后加入。支持中英文。支持两种棋盘,支持经典玻璃弹珠和其他不透明弹珠。游戏网页链接......
  • Vue3 vite:is a JavaScript file. Did you mean to enable the 'allowJs' option?
    描述今天在vue3+vite下进行打包时,突然vscode报了一个error。 大概的意识是询问是否启用“allowJS”选项,因为该文件在程序内是指定用于编译的根文件。提示信息已经很明确了,下面从网上摘抄了下什么是allowJS选项。allowJs是1.8中新提供的选项。TypeScrip......
  • JavaScript高级学习
    JavaScript进阶学习作用域、变量提升、闭包等语言特征,加深对JavaScript的理解,掌握变量赋值、函数声明的简洁语法,降低代码的冗余度。理解作用域对程序执行的影响能够分析程序执行的作用域范围理解闭包本质,利用闭包创建隔离作用域了解什么变量提升及函数提升掌握箭头函......
  • JavaScript 教程
    JavaScript是Web的编程语言。所有现代的HTML页面都可以使用JavaScript。JavaScript非常容易学。本教程将教你学习从初级到高级JavaScript知识。 为什么学习JavaScript?JavaScript是web开发人员必须学习的3门语言中的一门:HTML 定义了网页的内容CSS 描述......
  • JavaScript 格式化时间
    functionformatDate(date){/***格式化日期*@param{Date|String}date日期或日期字符串*/function_isString(val){returnObject.prototype.toString.call(val)==='[objectString]';}/***精确判断数据是否是Date类型*@param......
  • JavaScript 链表的增删改查
       //节点对象classNode{constructor(data){this.data=data;//存储节点数据this.next=null;//存储下一个节点的引用,默认为null}}//链表对象classLinkedList{constructor(){this.head=null;//链表头节点,默认为null}......
  • JavaScript学习 -- 内置函数(Math和Date)
    一、Date函数letdate=newDate()console.log("当前日期和时间:"+date)console.log("当前日期和时间:"+date.toLocaleString())console.log("年份:"+date.getFullYear())console.log("月份:"+(parseInt(date.getMonth())+1))console.log("日:"......