HTML+CSS+JS像素鸟小游戏

1.游戏展示

9最终成果展示

2.实现步骤

像素鸟游戏实现的思路

1. 绘制页面

未开始
暂停
游戏结束

<div id="land">
    <div id="sky">
        <div id="bird"></div>
        <div id="start">
            <img src="../bird-game/ready.png" alt="" id="ready">
            <img src="../bird-game/tap.png" alt="" id="tap">
            <p>键盘上下键切换颜色</p>
        </div>
        <div id="end">
            <img src="../bird-game/gameover.png" alt="" id="over">
        </div>
        <div id="pause">
            <img src="../bird-game/button_play.png" alt="">
        </div>
        <div id="score"></div>
    </div>
</div>

2. land不断向左移动

land.js
    // 获取land元素
    var land=document.getElementById('land');
    // 设置背景移动的速度
    let landSpeed=5;
    // 定义landbac对象,添加属性x,x相当于land元素的背景图片的横坐标
    var landbac={
        x:0
    }

    // 设置循环定时器使land背景循环移动
    var landMove=setInterval(landMoving(),30)

    function landMoving(){
        // land背景向左移动 
        landbac.x-=landSpeed;
        if(landbac.x<-200) landbac.x=0;
        // 实现真正的背景图片的移动
        land.style.backgroundPositionX=landbac.x+'px';
    }

背景移动

3. 小鸟煽动翅膀

bird.js
	// 获取bird元素
	var birdElem=document.getElementById('bird');
	// 小鸟振翅飞翔
	var j = 0;
	let  birdFly=setInterval(()=>{
	    birdElem.style.backgroundImage="url(../bird-game/image/bird0_" + j + ".png)"
	    j++;
	    if( j == 3){
	        j=0;
	    }
	} , 100);

小鸟煽动翅膀

4. 小鸟改变颜色
在这里插入图片描述
根据小鸟图片命名可以得出规律:只需要改变路径中bird0,1,2的数值就可以实现小鸟颜色的切换,此时需要用到键盘监听,按下键盘的次数对应小鸟图片名称,从而实现颜色切换,又因为只有三种颜色,所以还需数值的循环,也就是0到1到2之后,再回到0。

event.js
//添加键盘监听事件,当按下上下键时切换颜色
var choice=0;
document.onkeydown=function(event){     
    // 小鸟改变颜色
    if((event.keyCode==38||event.keyCode==40)){
        choice++;
        if(choice==3) choice=0; 
    }
}
bird.js
// 小鸟振翅飞翔+切换颜色
var j = 0;
let  birdFly=setInterval(()=>{
    birdElem.style.backgroundImage="url(../bird-game/image/bird"+choice+"_" + j + ".png)"
    j++;
    if( j == 3){
        j=0;
    }
} , 100);

4小鸟改变颜色

5. 小鸟下落
小鸟的下落是通过定时器,对小鸟纵坐标不断增加来实现。为了让小鸟的下落更贴近现实,给添加添加重力加速度,下落时小鸟速度越来越快,而上升时小鸟速度越来越慢。

// 小鸟添加重力
bird.js
let hasEnergy=false;
function  changeSpeed(){
    if (!hasEnergy) {
        bird.ySpeed++;
        bird.y += bird.ySpeed * 0.4;  
    } 
    else {
        bird.ySpeed--;  
        bird.y -= bird.ySpeed * 0.1;   
        if (bird.ySpeed <= 0) {
            hasEnergy = false;   
            }
    }
}

// 定时器实现小鸟不断下降
game.js
setInterval(()=>{
    changeSpeed();
    birdElem.style.top=bird.y+'px'; 
},30)

小鸟下落

6. 鼠标监听,实现小鸟弹跳

event.js
var land=document.getElementById('land');
// 鼠标点击事件
land.addEventListener('click',function(){

        bird.y-=20;                   
        hasEnergy = true;
        bird.ySpeed = 2;

});

7. 添加状态(游戏未开始,开始,暂停,结尾)

游戏未开始,小鸟在固定位置振翅飞行,land不动,可以切换颜色。
鼠标点击后开始,游戏开始,鼠标才能点击控制小鸟向上移动,land移动,管子循环出现。
空格控制暂停,小鸟在固定位置振翅飞行,land不动,管子不动。
游戏结束,小鸟不振翅飞行,land不动,管子不动,再次点击切换到游戏未开始状态。

game.js
  // 设置游戏的状态 0:未开始 1:进行中  2:失败
  let status=0;
  // 判断游戏进行还是暂停
  let running=false
  setInterval(()=>{  
    if(status==0&&running==false){ 
        // land不动
        clearInterval(landMove);
      }
    if(status==1&&running==true){ 
        // 小鸟不断下降
        changeSpeed();
        birdElem.style.top=bird.y+'px'; 
        // land移动
        landMove=setInterval(landMoving(),30);

    }
    if(status==2&&running==false){
        end.style.display='block';
        // 小鸟不振翅
        clearInterval(birdFly);
        // land不动
        clearInterval(landMove);
    }  
},30)

event.js
var land=document.getElementById('land');
 // 鼠标点击事件
 land.addEventListener('click',function(){
    if(status==0&&running==false){
        status=1;
        running=true;
        start.style.display='none';
    }
    if(status==1&&running==true){ 
        bird.y-=20;                
        hasEnergy = true;
        bird.ySpeed = 2;
    }
    if(status==2&&running==false){
        setTimeout(function(){
            location.reload();
            status=0;
            bird.y=beginy;          
        },500);             
    }     
});

event.js
//设置空格按下的次数
let count=0;
document.onkeydown=function(event){    
 // 空格键实现暂停
    if(event.keyCode==32&&status==1){
        if(count%2==0){
            running=false;
            pause.style.display='block';
            clearInterval(landMove);
        }
        else{
            running=true;
            pause.style.display='none';
        }
        count++;
    }
}

6添加状态

8. 创建水管+计分

pipe.js
// 设置初始分数为0
let scoreDemo=0;
// 设置水管移动速度
let pipeSpeed=3;
// 创建水管
function createPipe(position){
    var pipe={};
    pipe.x=position;
    // 设置上管道的高度为150—250
    pipe.upHeight=parseInt(Math.random()*100)+150;
    // 设置管道之间的距离为100
    // 得出下管道的高度
    pipe.downHeight=500-pipe.upHeight-100;
    // 得出下管道的x坐标
    pipe.downposition=100+pipe.upHeight;

    // 创建上管道
    var upPipe=document.createElement('div');
    // 设置上管道的宽高和绝对定位的位置
    upPipe.style.width='52px';
    upPipe.style.height=pipe.upHeight+'px';
    upPipe.style.position='absolute';
    upPipe.style.background='url(../bird-game/pipe2.png) no-repeat center  bottom'
    upPipe.style.left=pipe.x+'px';
    upPipe.style.top='0px';
    sky.appendChild(upPipe);

    // 创建下管道
    var downPipe=document.createElement('div');
     // 设置下管道的宽高和绝对定位的位置
    downPipe.style.width='52px';
    downPipe.style.height=pipe.downHeight+'px';
    downPipe.style.position='absolute';
    downPipe.style.background='url(../bird-game/pipe1.png) no-repeat center  top'
    downPipe.style.left=pipe.x+'px';
    downPipe.style.top=pipe.downposition+'px';
    sky.appendChild(downPipe);
    let scorePosition=position;
    let  pipeMove=setInterval(function(){
        if(status==1&&running==true){

            pipe.x-=pipeSpeed;
            scorePosition-=pipeSpeed;
            upPipe.style.left=pipe.x+'px';
            downPipe.style.left=pipe.x+'px';
            // 当管道恰好完全超出窗口时,改变管道位置,实现管道循环出现
            if(pipe.x<-52){    
                pipe.x=800;
            }
            // 当管道恰好越过小鸟,分数加1
            if(scorePosition+upPipe.offsetWidth<bird.x){
                scoreDemo++;
                scorePosition=800;
            }
            score.innerHTML='分数:'+scoreDemo;
        }
    },20);
    
}

// 控制水管出现时间
setTimeout(function(){
    createPipe(600);
    createPipe(800);
    createPipe(1000);
    createPipe(1200);
},100);

8创建水管

9. 碰撞检测
碰撞检测有两种情况:

1.小鸟与窗口顶部和land碰撞
game.js 定时器里
 if(bird.y<0||(bird.y+birdElem.offsetHeight>500)){            
        status=2;
        running=false;
        bird.y=beginy;
        clearInterval(birdFly);
        clearInterval(landMove);   
    }
2.小鸟与管子碰撞
pipe.js createPipe()方法中
   // birdElem.style.top=bird.y+'px';这条语句的作用:
   // 如果小鸟和管道碰撞,则游戏结束,
   // 此时小鸟的位置还是碰撞之前的位置,但是小鸟位置在数值上已经发生改变,出现显示上的错误,
   // 因此再次将小鸟纵坐标上的值赋给birdElem.style.top,实现刷新
   birdElem.style.top=bird.y+'px';
  
   // 小鸟的中心X,Y坐标
   let centerX1 = bird.x + birdElem.offsetWidth/2;  
   let centerY1 = bird.y + birdElem.offsetHeight/2; 
   // 上管道的中心X,Y坐标
   let centerX2 = upPipe.offsetLeft + upPipe.offsetWidth / 2;
   let centerY2 = upPipe.offsetTop + upPipe.offsetHeight / 2;
   // 下管道的中心X,Y坐标
   let centerX3 = downPipe.offsetLeft + downPipe.offsetWidth / 2;
   let centerY3 = downPipe.offsetTop + downPipe.offsetHeight / 2;

   let disX = Math.abs(centerX1 - centerX2); //小鸟和上管道中心点的横向距离
   let disY = Math.abs(centerY1 - centerY2);//小鸟和上管道中心点的纵向距离
   // 小鸟与上管道是否碰撞
   let upHit=(disX<(birdElem.offsetWidth + upPipe.offsetWidth)/2)&&(disY<(birdElem.offsetHeight + upPipe.offsetHeight)/2);

   let disX1 = Math.abs(centerX1 - centerX3);//小鸟和下管道中心点的横向距离
   let disY1 = Math.abs(centerY1 - centerY3);//小鸟和下管道中心点的纵向距离
   // 小鸟与上管道是否碰撞
   let downHit=disX1<(birdElem.offsetWidth + downPipe.offsetWidth)/2&&disY1 < (birdElem.offsetHeight + downPipe.offsetHeight)/2;

   if(upHit||downHit){
       status=2;
       running=false;
   } 

10.声音添加

html
<audio src="../bird-game/audio/begin.mp3" id="begin-audio"></audio>
<audio src="../bird-game/audio/change.mp3" id="change-audio"></audio>
<audio src="../bird-game/audio/fly.mp3" id="fly-audio"></audio>
<audio src="../bird-game/audio/hit.mp3" id="hit-audio"></audio>
<audio src="../bird-game/audio/score.mp3" id="score-audio"></audio>
<audio src="../bird-game/audio/over.mp3" id="over-audio"></audio>
js
var beginAudio=document.getElementById('begin-audio');
var changeAudio=document.getElementById('change-audio');
var flyAudio=document.getElementById('fly-audio');
var hitAudio=document.getElementById('hit-audio');
var scoreAudio=document.getElementById('score-audio');
var overAudio=document.getElementById('over-audio');
//分别添加到特定的需要音乐的位置
beginAudio.play();   
changeAudio.play(); 
flyAudio.play(); 
hitAudio.play(); 
scoreAudio.play(); 
overAudio.play(); 

3.遇到的问题以及解决办法

1. 问题:小鸟碰撞到水管,但页面显示没碰撞

原因:小鸟和管道碰撞,游戏结束,此时小鸟的位置还是碰撞之前的位置,但是小鸟位置在数值上已经发生改变,出现显示上的错误.
解决方法:再次将小鸟纵坐标上的值赋给birdElem.style.top,实现刷新.

2. 游戏最后页面卡顿

原因:setinterval不会清除定时器队列,每重复执行1次都会导致定时器叠加,最终卡死你的网页.
解决方法:setInterval放在外层,内层配合使用setTimeout.

参考:
setInterval影响性能导致卡死的解决方法
JS setInterval 定时器导致页面卡死

3. 小鸟落地碰撞超出land

原因:小鸟上下改变了纵坐标,与land的碰撞条件是超出500就死亡,但是小鸟的纵坐标数值上不一定会正好等于界限500.

4. 改变鸟头方向,碰撞检测不准确

原因:通过旋转div改变鸟头方向,导致div的宽高发生改变,而碰撞检测是根据小鸟的宽高和水管的宽高检测的,因此碰撞检测不准确
解决方法:
1.将图片换成已经旋转过的小鸟
2.更改碰撞检测方法

源码:
Gitee源码
GitHub源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值