用js写飞机大战的小游戏

飞机大战思路整理

飞机大战分为5个阶段:
1.游戏开始阶段
1.0定义整个游戏的全局变量,并进行初始化
1.1.0创建游戏背景图片
1.1.1初始化背景图片的数据
1.1.2创建游戏开始阶段的构造函数
*绘制方法
*运动方法
1.1.3创建游戏开始的对象
1.1.4绘制游戏名
2.游戏加载阶段
2.0绘制游戏加载
2.1.0创建游戏加载时的图片,并放在一个数组中
2.1.1初始化游戏加载时的数据
2.1.2创建游戏加载时的构造函数
*绘制方法
*运动方法
2.1.3创建游戏加载时的对象
2.1.4增加鼠标点击事件(点击第一阶段会跳转到第二阶段)
3.游戏运行阶段
3.1 绘制我方飞机
3.1.0创建我方飞机的图片,并把它们放进数组中;
3.1.1初始化我方飞机的数据;
3.1.2创建我方飞机的构造函数;
*绘制方法
*运动方法
*碰撞方法(在3.3.8中调用)
*射击方法(我方飞机射击时,要调用子弹产生的对象实例)
3.1.3创建我方飞机的对象
3.1.4增加鼠标移动事件(当鼠标移动的时候我方飞机要跟着移动)
3.2 绘制子弹
3.2.0创建子弹的图片
3.2.1初始化子弹的数据
3.2.2创建子弹的构造函数
*绘制方法
*运动方法
*碰撞方法(在3.3.8中调用)
3.2.3定义一个子弹的数组。将新的子弹的对象实例放进数组中(方便后面遍历、增加、删除)
3.2.4创建绘制子弹的函数:遍历数组调用子弹对象的构造函数中的绘制子弹的方法
3.2.5创建子弹移动函数:遍历数组调用子弹对象的构造函数中子弹运动的方法
3.2.6创建删除子弹的函数:
删除子弹满足的条件:1.碰撞的时候删除子弹
2.子弹的纵坐标小于负的子弹的高度的时候删除子弹
3.3 绘制敌方飞机
3.3.0创建敌方飞机的图片(分别用3个数组存放:区别小飞机、中飞机、大飞机)
3.3.1分别初始化三个类型飞机的数据
3.3.2创建敌方飞机的构造函数
*绘制方法
*运动方法
*敌方飞机碰撞的方法(在3.3.8中调用)
*检测敌方飞机是否碰撞的方法(有可能是我方飞机碰撞、也有可能事子弹碰撞)
3.3.3定义一个存放敌方飞机的数组(方便后面函数遍历、增加子弹、删除、改变)
3.3.4创建一个函数,往数组中添加数据(小飞机、中飞机、大飞机)————较难
它们的产生是随机的,将new出来的飞机对象实例添加进入数组中。
3.3.5创建函数,遍历画出敌方飞机(调用数组中对象的方法)
3.3.6创建函数,遍历出敌方飞机的运动(调用数组对象中的方法)
3.3.7创建函数,删除敌方飞机
删除敌方飞机满足的条件:
1.敌方飞机的高度大于整个画布的高度时
2.敌方飞机爆炸完成时
3.3.8创建函数,敌方飞机碰撞以后的函数(循环遍历敌方飞机的数组)
判断:1.碰撞为我方飞机的时候
调用数组元素中的检测是否碰撞的方法(传入的实参就是我方飞机)
分别调用敌方飞机和我方飞机的撞击以后的方法
2.碰撞为子弹的时候
调用数组元素中的检测是否碰撞的方法(传入的参数就是子弹数组的元素)
分别调用敌方飞机和子弹数组元素中的撞击以后的方法
3.4绘制分数值和生命值
4.游戏暂停阶段
用画布的鼠标移出事件暂停游戏状态
用画布的鼠标移入事件开始游戏状态
5.游戏结束阶段
绘制游戏结束
写一个定时器:调用所有的对象方法以及函数调用
通过判断游戏状态调用所对应的对象方法和函数
注意:在后面的暂停阶段和游戏结束阶段要把前面第三阶段的我方飞机、子弹、敌方飞机都显示出来


<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
            #canvas{
                display: block;
                margin: 100px auto;
                /*border: solid 1px #000;*/
            }
        </style>
    </head>
    <body>
        <canvas id="canvas" width="480" height="650"></canvas>
        <script>
            var canvas=document.getElementById("canvas");
            var context=canvas.getContext("2d");
//          0.游戏初始化
//          0.1将游戏分为几个阶段
            var START=0;//   第一个阶段   游戏开始阶段
            var STARTING=1;//第二个阶段   游戏游戏开始的加载阶段
            var RUNNING=2;// 第三个阶段   游戏运行阶段
            var PAUSE=3;//   第四个阶段   游戏暂停阶段
            var GAMEOVER=4;//第五个阶段   游戏结束阶段
//          0.2定义一个自己的状态,去和上面的状态作比较
            var state=START;
//          0.3定义画布的宽和高
            var WIDTH=canvas.width;
            var HEIGHT=canvas.height;
//          0.4定义游戏的得分
            var score=0;
//          0.5定义我方飞机的生命值为3
            var life=3;
            //初始化阶段完成

            //1.游戏刚开始的阶段
            //1.1加载背景图片
            //1.1.1创建加载背景图片的对象
            var bg=new Image();
            bg.src="img/background.png";
            //1.1.2初始化背景图片的数据
            var BG={
                imgs:bg,
                width:480,
                height:852
            }
            //1.1.3创建背景图片的构造函数(构造出一个对象)  
            //这里使用了创建对象的第二种方法:先使用函数来定义对象,然后创建新的对象实例
            //构造函数的特点: 
//          ①构造函数的首字母必须要大写,用来区分与普通函数 
//          ②内部使用的this对象,来指向直接要生成的实例对象 
//          ③使用new来生成实例对象
            function Bg(config){//这里的config是个形参,而BG就是后面要传进来的实参;
                this.imgs=config.imgs;
                this.width=config.width;
                this.height=config.height;
                //在这里定义了两张背景图,是为了第一张背景图移动完后第二张背景图接着移动
                this.x1=0;
                this.y1=0;
                this.x2=0;
                //第二张背景图的初始高度放在背景高度(固定)的上面
                this.y2=-this.height;

                //定义背景图片绘制的方法
                this.paint=function(){
                    //分别绘制了两张背景图
                    context.drawImage(this.imgs,this.x1,this.y1);
                    context.drawImage(this.imgs,this.x2,this.y2);
                }
                //定义背景图片运动的方法
                this.step=function(){
                    //背景图片位置向下移动一个,然后利用后面的定时器让背景图动起来
                    this.y1++;
                    this.y2++;
                    //判断图片高度的临界点,只要图片一和图片二的高度等于设置的背景高度时,就让他们的纵向位置变成背景高度的上面
                    if(this.y1 == this.height){
                        this.y1=-this.height;
                    }
                    if(this.y2 == this.height){
                        this.y2=-this.height;
                    }
                }
            }
//          1.1.4创建背景图片的对象
            var sky=new Bg(BG);
//          1.2绘制游戏名
            var logo=new Image();
            logo.src="img/start.png";

//          2第二阶段游戏加载的过程
//          2.1游戏加载时的四个图片
            var loadings = [];
            loadings[0] = new Image();
            loadings[0].src = "img/game_loading1.png";
            loadings[1] = new Image();
            loadings[1].src = "img/game_loading2.png";
            loadings[2] = new Image();
            loadings[2].src = "img/game_loading3.png";
            loadings[3] = new Image();
            loadings[3].src = "img/game_loading4.png";
//          2.2初始化图片数据
            var LOADING={
                imges:loadings,
                length:loadings.length,
                width:186,
                height:38
            }
//          2.3写构造函数
            function Loading(config){
                this.imges=config.imges;
                this.length=config.length;
                this.width=config.width;
                this.height=config.height;
//              定义索引,判断要显示的图片是哪个
                this.startIndex=0;
//              定义绘制方法
                this.paint=function(){
                    context.drawImage(this.imges[this.startIndex],0,HEIGHT-this.height)
                }
                //定义游戏加载时的图片切换速度初始值
                this.time=0;
                //定义游戏加载时图片动态效果(其实是图片切换)的方法
                this.step=function(){
                    this.time++;
                    //设置4步切换一张图
                    if(this.time % 4 == 0){
                        this.startIndex++;
                    }
                    if(this.startIndex == this.length){
                        state = RUNNING;
                    }
                }
            }
            //创建游戏加载(第二阶段)的对象
            var loading=new Loading(LOADING);
            //获取鼠标点击事件
            canvas.onclick=function(){
//              鼠标点击第一阶段(游戏开始)时,要切换到第二状态(游戏加载)
                if(state == START){
                    state = STARTING;
                }
            }

//          3.1.0写我方飞机
//          3.1.1加载我方飞机的图片
            var heros=[];
            heros[0]=new Image();
            heros[0].src="img/hero1.png";
            heros[1]=new Image();
            heros[1].src="img/hero2.png";
            heros[2]=new Image();
            heros[2].src="img/hero_blowup_n1.png";
            heros[3]=new Image();
            heros[3].src="img/hero_blowup_n2.png";
            heros[4]=new Image();
            heros[4].src="img/hero_blowup_n3.png";
            heros[5]=new Image();
            heros[5].src="img/hero_blowup_n4.png";
//          3.2初始化图片数据
            var HEROS={
                imgs:heros,
                length:heros.length,
                width:99,
                height:124,
                frame:2             
            }
//          3.1.3构造我方飞机的函数
            function Hero(config){
                this.imgs=config.imgs;
                this.length=config.length;
                this.width=config.width;
                this.height=config.height;
                this.frame=config.frame;
//              定义索引值
                this.startIndex=0;
//              定义我方飞机的位置
                this.x=WIDTH / 2 - this.width / 2;
                this.y=HEIGHT - this.height;
//              定义飞机撞击的标志,表示飞机没有被撞击
                this.down=false;
//              定义飞机是否爆破完成,表示飞机还没有完全爆炸
                this.candel=false;

//              定义绘制的方法
                this.paint=function(){
                    context.drawImage(this.imgs[this.startIndex],this.x,this.y)
                }
//              定义我方飞机运动的方法
                this.step=function(){
//                  我方飞机的运动状态:(所以在此处要判断状态)
//                  1.正常状态
//                  2.爆破状态
                    if(!this.down){//表示飞机正常状态,此时只有两张图进行切换
                        if(this.startIndex == 0){
                            this.startIndex=1;
                        }
                        else{
                            this.startIndex=0;
                        }
                    }
                    else{//爆炸状态
                        this.startIndex++;//爆炸状态的图片持续增加
                        if(this.startIndex == this.length){//判断图片是最后一张的时候
                            life--;//我方飞机爆炸完后,生命值减一
                            //判断生命值为0时,游戏结束
                            if(life == 0){
                                state=GAMEOVER;
                                //虽然游戏结束了但是还停留在最后一张图的状态
                                this.startIndex=this.length-1;
                            }
//                          想要开始新的生命时,直接给出创建的新的对象
                            else{
                                hero=new Hero(HEROS);

                            }
                        }
                    }
                }
//              定义我方飞机碰撞的方法
                this.bang=function(){
                    this.down=true;
                }


//              增加我方飞机射击的方法
                //定义射击速度初始值为0
                this.time=0;
                //我方飞机射击的时候,就会出现子弹,所以要调用子弹的数组中产生的子弹对象
                this.shoot=function(){
                    this.time++;
                    //每3步射击一次
                    if(this.time % 3 == 0){
                        //这里把子弹每次创建好的新的实例增加到数组的末尾
                        bullets.push(new Bullet(BULLETS))
                    }
                }
            }
            //3.1.4创建我方飞机的对象实例
            var hero=new Hero(HEROS);
            //3.1.5获取鼠标移动事件
            canvas.onmousemove=function(event){
                var event=event || window.event;
                if(state == RUNNING){//判断当前游戏状态
                    //把获取到的页面中的鼠标横坐标的值赋给飞机的横坐标(位置)
                    hero.x = event.offsetX-hero.width/2;
                    //把获取到的页面中的鼠标纵坐标的值赋给飞机的纵坐标(位置)
                    hero.y = event.offsetY-hero.height/2;
                }
            }


//          3.2绘制子弹
//          3.2.0创建子弹的图片
            var bullet=new Image();
            bullet.src="img/bullet1.png";
//          3.2.1初始化数据
            var BULLETS={
                imgs:bullet,//注意这里的“ :”
                width:9,
                height:21
            }
//          3.2.2创建子弹的构造函数
            function Bullet(config){//用形参config去接收
                this.imgs=config.imgs;
                this.width=config.width;
                this.height=config.height;
//              定义子弹的坐标
                //子弹的横坐标
                this.x=hero.x + hero.width / 2-this.width / 2 ;
                //子弹纵坐标
                this.y=hero.y - this.height;
//              定义绘制方法
                this.paint=function(){
                    context.drawImage(this.imgs,this.x,this.y)
                };
//              定义运动方法
                this.step=function (){
                    this.y-=10;
                };
//              定义子弹碰撞属性    ,false表示没有碰撞            
                this.candel=false;
//              定义子弹碰撞方法,碰撞之后他的碰撞属性为true
                this.bang=function(){
                    this.candel=true;
                }
            }
//          3.2.3让所有new的子弹对象放到一个数组中(方便后面遍历,增加,删除)
            var bullets=[];
//          3.2.4通过遍历绘制子弹;最后在定时器中调用该函数即可
            function bulletdPaint(){
                for(var i=0;i<bullets.length;i++){
                    //调用数组对象中的方法paint()
                    bullets[i].paint();
                }
            }
//          3.2.5通过遍历调用子弹的运动;
            function bulletdStep(){
                for(var i=0;i<bullets.length;i++){
                    bullets[i].step();
                }
            }
//          3.2.6增加子弹的删除函数
            function bulletDel(){
                //删除子弹所满足的条件:
//              1.碰撞的时候删除子弹
//              2.超出画布的高度,其实就是负的子弹的高度
                for(var i=0;i<bullets.length;i++){
                    if(bullets[i].candel  || bullets[i].y < -bullets[i].height){
                        //调用数组中的方法
                        bullets.splice(i,1)
                    }
                }
            }

//          3.3敌方飞机的绘制
//          3.3.0创建敌方飞机图片
            var enemy1=[];//小飞机
            enemy1[0]=new Image();
            enemy1[0].src="img/enemy1.png";
            enemy1[1]=new Image();
            enemy1[1].src='img/enemy1_down1.png';
            enemy1[2]=new Image();
            enemy1[2].src='img/enemy1_down2.png';
            enemy1[3]=new Image();
            enemy1[3].src='img/enemy1_down3.png';
            enemy1[4]=new Image();
            enemy1[4].src='img/enemy1_down4.png';
            var enemy2=[];//中飞机
            enemy2[0]=new Image();
            enemy2[0].src="img/enemy2.png";
            enemy2[1]=new Image();
            enemy2[1].src="img/enemy2_down1.png";
            enemy2[2]=new Image();
            enemy2[2].src="img/enemy2_down2.png";
            enemy2[3]=new Image();
            enemy2[3].src="img/enemy2_down3.png";
            enemy2[4]=new Image();
            enemy2[4].src="img/enemy2_down4.png";
            var enemy3 = [];    //大飞机
            enemy3[0] = new Image();
            enemy3[0].src = "img/enemy3_n1.png"
            enemy3[1] = new Image();
            enemy3[1].src = "img/enemy3_n2.png"
            enemy3[2] = new Image();
            enemy3[2].src = "img/enemy3_down1.png"
            enemy3[3] = new Image();
            enemy3[3].src = "img/enemy3_down2.png"
            enemy3[4] = new Image();
            enemy3[4].src = "img/enemy3_down3.png"
            enemy3[5] = new Image();
            enemy3[5].src = "img/enemy3_down4.png"
            enemy3[6] = new Image();
            enemy3[6].src = "img/enemy3_down5.png"
            enemy3[7] = new Image();
            enemy3[7].src = "img/enemy3_down6.png"
//          3.3.2敌方飞机的初始化数据
            var ENEMY1={
                imgs:enemy1,
                length:enemy1.length,
                width:57,
                height:51,
                type:1,
                frame:1,
                life:1,
                score:1
            }
            var ENEMY2={
                imgs:enemy2,
                length:enemy2.length,
                width:69,
                height:95,
                type:2,
                frame:1,
                life:5,
                score:5
            }
            var ENEMY3={
                imgs:enemy3,
                length:enemy3.length,
                width:165,
                height:261,
                type:3,
                frame:2,
                life:15,
                score:20
            }
//          3.3.3敌方飞机的构造函数
            function Enemy(config){
                this.imgs=config.imgs;
                this.length=config.length;
                this.width=config.width;
                this.height=config.height;
                this.type=config.type;
                this.frame=config.frame;
                this.life=config.life;
                this.score=config.score;

//              定义敌方飞机的坐标
                this.x=Math.random()*(WIDTH-this.width);
                this.y=-this.height;
                //定义下标
                this.startIndex=0;
                //定义碰撞属性,没有碰撞为false
                this.down=false;
                //定义是否爆炸完成的属性
                this.candel=false;
                //定义绘制方法
                this.paint=function(){
                    context.drawImage(this.imgs[this.startIndex],this.x,this.y);
                };
                //定义运动方法
                this.step = function(){
                    if(!this.down){    //飞机处于正常状态
//                      小飞机,中飞机的下标始终都是0
//                      大飞机的下标是在0和1之间进行切换

                        this.startIndex ++;
                        this.startIndex = this.startIndex % this.frame;

//                      飞机向下的动画
                        this.y += 2;
                    }
                    else{          //飞机发生碰撞以后
                        this.startIndex ++;
                        if(this.startIndex == this.length){
                            this.candel = true;
                            this.startIndex = this.length - 1;
                        }
                    }
                }
                //是否被碰撞的方法
                this.checkHit=function(wo){//判断四个边
                    return wo.y + wo.height > this.y 
                           && wo.x + wo.width > this.x
                           && wo.y < this.y + this.height
                           && wo.x < this.x + this.width;       
                }
                //敌方飞机碰撞以后的方法
                this.bang=function(){
                    this.life--;
                    if(this.life == 0){
                        this.down=true;
                        score +=this.score;
                    }
                }
            }
//          3.3.4创建数组存放敌方飞机
            var enemise=[];
//          3.3.5创建函数,往数组中添加数据
            function enterEnemise(){
                //定义一个变量存放随机数
                var rand=Math.floor(Math.random()*100)
                if(rand < 10){
                    //添加小飞机
                    enemise.push(new Enemy(ENEMY1));
                }else if(rand < 55 && rand > 50){
                    //添加中飞机
                    enemise.push(new Enemy(ENEMY2));
                }else if(rand == 88){
                    //添加大飞机
                    //大飞机尤其只有一个
                    //并且把大飞机放在数组的第一位
                    if(enemise[0].type != 3  && enemise.length > 0){
                        enemise.splice(0,0,new Enemy(ENEMY3));
                    }
                }
            }
//          3.3.6创建函数绘制敌方飞机(因为敌方飞机在数组中,所以要循环遍历画出)
            function enemyPaint(){
                for(var i=0;i<enemise.length;i++){
                    enemise[i].paint();
                }
            }
//          3.3.7创建函数敌方飞机的运动
            function enemyStep(){
                for(var i=0;i<enemise.length;i++){
                    enemise[i].step();
                }
            }
//          3.3.8创建函数删除敌方飞机
            function delenemy(){
                for(var i=0;i<enemise.length;i++){
//                  console.log(enemise[i].candel)
                    if(enemise[i].y > HEIGHT || enemise[i].candel){
                        enemise.splice(i,1)
//                      arrayObject.splice(index,howmany,item1,.....,itemX)参数 描述 
//                      index 必需。整数,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置。 
//                      howmany 必需。要删除的项目数量。如果设置为 0,则不会删除项目。 
//                      item1, ..., itemX 可选。向数组添加的新项目。 
                    }
                }
            }
//          3.3.9绘制碰撞以后的函数
//          (在上面3.3.3敌方飞机的对象中定义了一个检测{我方飞机wo}是否碰撞的方法)
//          (那个wo作为形参)(那么下面的hero作为我方飞机的实参传进去)(子弹的数组中的元素对象{bullets[j]}作为实参传进去)
            function hitEnemise(){
                for(var i = 0;i< enemise.length;i++){
//                  如果我方飞机撞到了敌方飞机以后
                    if(enemise[i].checkHit(hero)){
//                      处理敌方飞机碰撞以后的逻辑
                        enemise[i].bang();
//                      处理我方飞机碰撞以后的逻辑
                        hero.bang()
                    }

//                  子弹如果碰到敌方飞机以后
                    for(var j = 0;j<bullets.length;j++){
                        if(enemise[i].checkHit(bullets[j])){
                            enemise[i].bang();
//                          子弹的碰撞逻辑
                            bullets[j].bang();
                        }
                    }
                }
            }
//          3.4绘制分数和生命值
            function scoreText(){
                context.font="30px bold"
                context.fillText("score:"+score,10,30)
                context.fillText("life:"+life,300,30)
            }
            //第三阶段的函数和对象方法写完了


//          4.绘制游戏暂停的阶段
            //调用画布的鼠标移出事件,改变状态
            canvas.onmouseout=function(){
                if(state == RUNNING){
                    state = PAUSE;
                }
            }
            //调用画布的鼠标移入事件
            canvas.onmouseover=function(){
                if(state == PAUSE){
                    state = RUNNING;
                }
            }
            //绘制暂停图片
            var pause=new Image()
            pause.src="img/game_pause_nor.png"
//          5.绘制游戏结束
            function gameover(){
                context.font="50px bold"
                context.fillText("GAME OVER !!!",80,300)
            }



            setInterval(function(){
                //背景图片无论在哪个状态都有背景图片以及它的动态效果
                sky.paint();
                sky.step();

                if(state==START){//第一阶段
                    context.drawImage(logo,35,0)
                }else if(state == STARTING){//第二阶段
                    loading.paint();
                    loading.step();
                }else if(state == RUNNING){//第三状态
                    //绘制我放飞机
                    hero.paint();
                    //我方飞机的运动
                    hero.step();
//                  我方飞机的射击方法
                    hero.shoot();
                    //子弹的绘制
                    bulletdPaint();
                    //子弹的运动
                    bulletdStep();
                    //子弹的删除
                    bulletDel();
                    //创建敌方飞机
                    enterEnemise()
                    //绘制敌方飞机
                    enemyPaint();
                    //绘制敌方飞机的运动
                    enemyStep();
                    //删除敌方飞机
                    delenemy();
                    //判断是否撞击
                    hitEnemise();
                    //绘制分数和生命值
                    scoreText()
                }else if(state == PAUSE){
                    sky.paint();
                    sky.step();
                    hero.paint();
                    bulletdPaint();
                    enemyPaint();
                    scoreText()
                    context.drawImage(pause,220,300)
                }else if(state == GAMEOVER){
                    sky.paint();
                    sky.step();
                    hero.paint();
                    bulletdPaint();
                    enemyPaint();
                    scoreText();
                    gameover();
                }
            },100)
        </script>
    </body>
</html>
  1. 可能还有一些bug(游戏体验)
  2. 代码没有优化到最佳。只是按照我最能理解的方式写的。
  3. 还没有形成那种很严密的逻辑思维,还需要多多努力。
  4. 感觉语法上的错误还好,逻辑上错误很不容易找出来,我还不能特别熟练通过自己调试出代码错误。
  5. 在应用熟练中学习了js中对象的创建,以及构造器的使用,函数调用等。

猜你喜欢

转载自blog.csdn.net/qq_40896410/article/details/82261074