zoukankan      html  css  js  c++  java
  • 用javascript写星际飞机大战游戏

    在github里看到了个不错的脚本游戏,决定亲自动手来写,效果如下

     

    下面是代码的思路分享

    把整个代码理解消化确实不容易,但是如果你坚持看完相信你一定会有收获

    如果没兴趣可以直接点击下面的链接 复制代码 开玩:

    http://www.cnblogs.com/demonxian3/p/6241755.html

     

    1丶首先准备好素材

    游戏的元素有:飞机 敌机 子弹 背景 暂停

     

    素材的大小可以通过drawImage()来改变其大小,因此不需要太纠结素材多少像素

     

    创建一个html文件 和一个js文件

    然后开始编辑该html文件

    2.写一个画布:

    <canvas id="canvas" width=500 height=500 style="border:1px solid #c3c3c3"></canvas>

     

     

    3.获取画布对象

    canvas = document.getElementById('canvas') 
    
    cxt = canvas.getContext('2d');

     

     

    4.设置画布属性

    var boxx=0
    
    var boxy=0
    
    var boxwidth=500
    
    var boxheight=500

     

     **************************画飞机****************************

    5.声明飞机相关的变量

    飞机横坐标 纵坐标 宽度 高度 这些都是为下面的drawImage做准备的

    var planex;
    
    var planey;
    
    var planewidth=60;
    
    var planeheight=60;
    
    var planeImage = new Image();
    
    planeImage.src="images/hero.jpg";  //引入图片

     

    6.布置飞机初始位置

    planex = (boxwidth - planewidth) / 2   //画布的中央
    
    planey = boxheight - planeheight     //画布的底端

    因为这里的坐标(x,y)指的是飞机左上角的坐标,因此需要把飞机的长度算上

     

    7.画出飞机

    有了坐标和宽高就可以把飞机画出来了

    cxt.drawImage(planeImage,planex,planey,planewidth,planeheight);

     

    现在可以打开网页测试下,看看飞机是否画出来了,

    如果没有画出来,那么请别灰心,耐心的查一下有什么错误的地方

    如果成功显示出飞机了那么go on!

     

    飞机画出来了但是飞机不会动 

    那么怎么能让飞机动呢? 

    改变其坐标!

    那么什么时候动呢?

    按下键盘上的 ↑ ↓ ← → 键的时候

    我们知道键盘有个事件驱动 onkeydown

    而这个事件驱动有个属性keycode 

    用这个属性可以绑定上下左右键对应的函数

    如下可以看到对应的keycode码

    下面是四个我们需要的按键的对应码

    左 : 37

    上 : 38

    右 : 39

    下 : 40

     

    光有事件驱动onkeydown还不够,我们还需要实时监听事件的发生

    说的通俗点就是 你在任意时刻去按键盘 程序都能够及时响应

    下面这个函数就可以帮我们实时监听事件

    addEventListener()函数

    语法

    element.addEventListener(event, function, useCapture)
     
    参数解释:
    element    表示需要使用该方法的对象 
    event        表示监听什么事件  
    function    表示监听到事件后做出什么响应 
    useCapture 表示在捕获或冒泡执行 默认为false 我们就按照默认来
     
     
    声明一个运动跨度变量 这个变量的作用你马上就能看到了
    var sp=0;

     

     
    8.监听body元素 响应事件
     
    下面四个if语句用来判断飞机是否出界
     
    body.addEventListener('keydown',function (event){
                switch(event.keyCode){
                    case 37 : if(planex>boxx){sp=8}else{sp=0}planex-=sp;break;
                    case 38 : if(planey>boxy){sp=8}else{sp=0}planey-=sp;break;
                    case 39 : if((planex+planewidth)<boxwidth){sp=8}else{sp=0}planex+=sp;break;
                    case 40 : if((planey+planeheight)<boxheight){sp=8}else{sp=0}planey+=sp;break;
                    default:break;
                }
            },false)
    
     

     

    到这里 你可以测试一下飞机是否可以动起来了

    当然我会直接告诉你结果----不能动!!

    因为上面的事件监听改变的只是飞机的坐标,但是图片还在原地

    为了让图片跟着坐标动起来,我们需要写一个函数

    9.更新飞机图片位置函数

    function drawplane(){
    
      cxt.clearRect(boxx,boxy,boxwidth,boxheight);                      //清除所有就元素
    
      cxt.drawImage(planeImage,planex,planey,planewidth,planeheight);  //重新画出飞机
    
    }
     
    
    var fps                               //其实只是用来调飞机流畅度
    
    var gameTimmer = setInterval (run,1000/fps);      //gameTimer是游戏的总驱动计时器
    
     
    
    function run(){                           //drawplane函数自动运行
    
     drawplane();                      
    
    }
    

     

     **************************画子弹****************************

     10.设置子弹的相关变量 也是为drawImage函数的五个参数所做准备

    var bulletx;
    
    var bullety;
    
    var bulletwidth=10;
    
    var bulletheight=10;
    
    var bulletImage=new Image();  
    
    bulletImage.src="images/bullet.png"

     

     

    11.存放子弹的弹夹-----数组

    用数组来存放子弹方便我们访问每个子弹对象

    var herobullet;                    //用来表示单个子弹
    
    var allbullets = new Array();      //用来表示所有子弹
    

     

    12.设置子弹初始位置

    我们希望 每颗子弹的起始位置是随着飞机而改变的 因此把飞机的变量引进来

    bulletx=planex+planewidth/2
    
    bullety=planey+bulletheight

     

    13.声明子弹的构造函数、

    这部分有点难理解 也是本代码的难点

    下面声明的构造函数是用来制作子弹的

    function bullet(x,y){
    
    this.x=x;                //子弹横坐标
    
    this.y=y;          //子弹纵坐标
    
    this.islive=true;       //子弹存活属性
    
    this.timmer=null;     //子弹计时器 用来使得子弹自己运动
    
        this.run = function run(){    //子弹的运动方法
    
           if(this.islive==false||this.y<-10){  //判断子弹是否存活或出界
    
           clearInterval(this.timmer);  //停止运动
    
        this.islive=false;
    
            }else{
    
            this.y-=20           //让子弹往上飞
    
        }
    
        }
    
    }

     

     14.生产子弹

     有了做子弹的秘方 那么就可以开始制造子弹了

     在这里可以使用到我们之前定义的数组存放每颗子弹

    function producebullet(){
    
      herobullet = new bullet(bulletx,bullety);  //生产子弹
     
      allbullets.push(herobullet);  //存放子弹
    
      var timmer = setInterval("allbullets[" + (allbullets.length-1) +"].run()" , 50); //设置子弹子弹运行 这里用到eval可以将字符变为有效方法 
    
      allbullets[allbullets.length-1].timmer=timmer;  //将自动运行的子弹方法赋予到子弹的属性
    
    }

     

    15.画出子弹

    同飞机的道理相同:光改变坐标是看不出效果,还需要把图片位置也更新了

    function  drawbullet(){
    
      for (var i=0;i<allbullets.length;i++){ //遍历每颗子弹
    
      if(allbullets[i].islive){  //如果子弹挂了 就不需要再画出来了
    
      cxt.drawImage(bulletImage,allbullets.x,allbullets.y,bulletwidth,bulletheight);
    
      }
    
      }
    
    }

     

     16.让drawbullet函数自动运行

    然后把drawbullet函数 放到之前定义的run函数里 不是子弹的方法

    function run(){
    
      drawplane();
    
      drawbullet();
    
    }

     

    17.让子弹自动生产

    给生产子弹的函数加个计时器

    btimmer = setInterval(producebullet,500)

     

     **************************画敌机****************************

    我们来回顾下画子弹的步骤

    设置相关属性 ->  确定初始坐标 -> 子弹构造函数 ->  生产子弹 -> 画出子弹 -> 自动生产和绘画

    画敌机的步骤和上面的步骤是一样的

     

    但是有个不同的点 那就是敌机初始的横坐标应该是随机的  而纵坐标都是0 (顶部位置)

     

    说到随机 Math.random()函数 是不可以缺少的

    但这个只能产生0~1的随机数

    因此 Math.random*500   范围就是0~500范围

    最后四拾伍入   Math.ceil(Math.random*500);

     

    18.设置敌机的相关属性

    var enemyx;
    var enemyy;
    var enemywidth=30
    var enemyheight=30
    var allenemys = new Arr(); //和子弹一样 数组用来存放所有敌机
    var heroenemy; //用来表示其中单个敌机
    var enemyImage= new Image();
    enemyImage.src
    ="images/enemy.png"

    19.敌机构造函数

    //构造函数
    //下面基本上和子弹差不多 就不解释了
    function enemy(x,y){
    
      this.x=x;
    
      this.y=y;
    
      this.islive=true;
    
      this.timmer=null;
    
      this.run = function run(){
    
       if (this.islive==false||this.y>boxheight){
    
       clearInterval(this.timmer);
    
       this.islive=false;
    
       }else{
    
       this.y+=2.5;   
    
       }
    
      }
    
    }
    
    

    20生产敌机

    //生产随机位置的敌机
    
    function produceenemy(){
    
      enemyx=Math.ceil(Math.random()*500);  //产生随机初始位
    
      enemyy=33;                            //在画布顶端产生敌机
    
      heroenemy = new enemy(enemyx,enemyy);  //生产敌机
    
      allenemys.push(heroenemy);            //加入数组
    
      var timmer = setInterval("allenemys[" + (allenemys.length-1) + "].run()"); //启动run函数并使其自动运行
    
      allenemys[allenemys.length-1].timmer=timmer;   //run函数赋予在enemy属性
    
    }
    
     

    21画出敌机

    //画出敌机
    
    function drawenemy(){
    
      for (var i=0;i<allenemys.length;i++){
    
      if(allenemys[i].islive){
    
       cxt.drawImage(enemyImage,allenemys[i].x,allenemys[i].y,enemywidth,enemyheight)
    
      }
    
     }
    
    }

    22自动生产敌机 更新敌机

    最后把 drawenemy 加入run函数里  给produeenemy 加个计时器

    function run(){
    
      drawplane();
    
      drawbullet();
    
      drawenemy();
    
    }
    
    

    etimmer = setInterval(produceenemy,800);

    
    

     

     **************************击中敌机****************************

    飞机 敌机  子弹 都画好了 而且都可以动起来了 , 现在来写子弹碰到敌机时敌机消失的规则

     

    上图可以清晰的看出子弹击中敌机的范围

    击中条件:

    bulletx + bulletwidth > enemyx

    bulletx < enemyx + enemywidth

    bullety < enemyy + enemyheight

    function checkbullet{
    
      for(var i=0;i<allenemys.length;i++){  //遍历敌机
    
        if (allenemys[i].islive){
    
        var e = allenemys[i];              //获取敌机对象
    
        }
    
        for(var j=0;j<allbullets.length;j++){  //遍历
    
          if(allbullets[j].islive){
    
            var b = allbullets[j];
    
            if(b.x+b.width>e.x&&b.x<e.x+e.width&&b.y<e.y+e.height){ //这里使用到上面的规则
    
              b.islive=false;  //在构造函数里只要islive的属性为假 就会停止run函数的计时器
    
              e.islive=false;  //因此子弹和敌机相关计时器都被clear掉了
    
              score+=100;  //加分
    
            }
    
          }
    
        }
    
      }
    
    }

     

    **************************击中我机****************************

    我机的死亡的规则:

     

     

    击中条件:

    enemyx+enemywidth > planex

    enemyx < planex + planewidth

    enemyy + enemyheight > planey

     

    function checkenemy(){
    
      for(var i=0;i<allenemys[i].length;i++){
    
            if(allenemys[i].islive){
    
          var e = allenemys[i];
    
          if(e.x+e.width > planex && e.x < planex + planewidth && e.y + e.height > planey){
    
            e.islive=false;
    
            stop();
    
          }
    
        }
    
         }
    
    }

     

     **************************停止一切****************************

    然后把stop函数写出来

    function stop(){
    
      clearInterval(btimmer);    //停止生产子弹
    
      clearInterval(etimmer);    //停止生产敌机
    
      clearInterval(gameTimmer);    //停止游戏
    
      allenemys.length=0;        //初始化
    
      allbullets.length=0;      //初始化
    
      show.innerHTML=score;       //统计总分
    
      score=0;
    
    }

     

     **************************启动一切****************************

    然后把checkbullet函数 和checkenemy函数 放到游戏驱动run 里头

    function run(){
    
      drawplane();  //画飞机
    
      drawenemy();  //画敌机
    
      drawbullet();  //画子弹
    
      checkbullet(); //检查子弹
    
      checkenemy();  //检查敌机
    
      drawscore();  //实时加分
    
    }

     

     

    最后设置一下加分相关的标签

    这个标签用来显示当前分数

    <div style="position: absolute;top: 90px;left: 30px;
    font-weight: bold;font-size: 40px;color:cornflowerblue"
    > <span id="show">0</span></div>

     

     

    获取标签

    show = document.getElementById('show');

     

     

    实时计分

    function drawscore(){
    
      show.innerHTML=score;
    
    }

     

     

    当然还可以加一些小细节,比如暂停按钮 弹出窗口统计得分

    还可以玩好玩的,比如飞机无敌 无限子弹 超级子弹 无敌并排子弹 总之你自己想怎么改都行

    源代码:http://www.cnblogs.com/demonxian3/p/6241755.html

     

  • 相关阅读:
    BZOJ2599: [IOI2011]Race(点分治)
    BZOJ4182: Shopping(点分治,树上背包)
    BZOJ3697: 采药人的路径(点分治)
    点分治
    BZOJ3091: 城市旅行(LCT,数学期望)
    BZOJ5020: [THUWC 2017]在美妙的数学王国中畅游(LCT,泰勒展开,二项式定理)
    BZOJ3514: Codechef MARCH14 GERALD07加强版(LCT,主席树)
    BZOJ4025: 二分图(LCT)
    BZOJ4817: [Sdoi2017]树点涂色(LCT)
    BZOJ2402: 陶陶的难题II(树链剖分,0/1分数规划,斜率优化Dp)
  • 原文地址:https://www.cnblogs.com/demonxian3/p/6238635.html
Copyright © 2011-2022 走看看