• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    利用HTML5 Canvas制作一个简单的打飞机游戏

    之前在当耐特的DEMO里看到个打飞机的游戏,然后就把他的图片和音频扒了了下来。。。。自己凭着玩的心情重新写了一个。仅供娱乐哈。。。。。。我没有用框架,所有js都是自己写的。。。。。。所以就可以来当个简单的教程,对那些刚玩canvas的,或许能有些帮助,楼主玩canvas也不是很久,技术不是很好,请见谅哈。

      闲话不多说,先上DEMO撒:飞机游戏   楼主写这个人纯碎娱乐,没想着写成多正式的游戏哈。

      步入主题啦:打飞机游戏文件有index.html入口文件,allSprite.js精灵的逻辑处理文件,loading.js加载处理文件以及data.js(初始化的一些数据)。

      首先,正常的游戏基本上都需要一个loading,loading页面就是用来预加载数据的,包括精灵表图片,音频等,因为这是个小游戏,要加载的就只有一些音频和图片。里面的加载代码主要就下面这些,其他是制作loading动画的,那个比较简单,就不贴了,如果有兴趣的直接在DEMO里看控制台就行了:

    XML/HTML Code复制内容到剪贴板
    1. loadImg:function(datas){   
    2.             var _this = this;   
    3.             var dataIndex = 0;   
    4.             li();   
    5.             function li(){   
    6.                 if(datas[dataIndex].indexOf("mp3")>=0){   
    7.                     var audio = document.createElement("audio");   
    8.                     document.body.appendChild(audio);   
    9.                     audio.preload = "auto";   
    10.                     audio.src = datas[dataIndex];   
    11.                     audio.oncanplaythrough = function(){   
    12.                         this.oncanplaythrough = null;   
    13.                         dataIndex++;   
    14.                         if(dataIndex===datas.length){   
    15.                             _this.percent = 100;   
    16.                         }else {   
    17.                             _this.percent = parseInt(dataIndex/datas.length*100);   
    18.                             li.call(_this);   
    19.                         }   
    20.                     }   
    21.                 }else {   
    22.                     preLoadImg(datas[dataIndex] , function(){   
    23.                         dataIndex++;   
    24.                         if(dataIndex===datas.length){   
    25.                             _this.percent = 100;   
    26.                         } else {   
    27.                             _this.percent = parseInt(dataIndex/datas.length*100);   
    28.                             li.call(_this);   
    29.                         }   
    30.                     })   
    31.                 }   
    32.             }   
    33.         },   
    34.   
    35. //再贴出preLoadImg的方法   
    36. function preLoadImg(src , callback){   
    37.     var img = new Image();   
    38.     img.src = src;   
    39.     if(img.complete){   
    40.         callback.call(img);   
    41.     }else {   
    42.         img.onload = function(){   
    43.             callback.call(img);   
    44.         }   
    45.     }   
    46. }     


    我先在data.js里面用一个数组保存文件的链接,然后判断这些链接是图片还是音频,如果是图片就用preLoadImg加载,预加载图片的代码很简单,就是new一个图片对象,然后把链接赋给它,加载完后再回调。音频的加载则是通过生成一个HTML5的audio dom对象,把链接赋给它,audio有一个事件“canplaythrough”,浏览器预计能够在不停下来进行缓冲的情况下持续播放指定的音频/视频时,会发生 canplaythrough 事件,也就是说当canplaythrough被调用时,音频就已经被加载的差不多了,可以进行下一个音频的加载了。就这样当把所有东西都加载完后,再进行回调,开始游戏。

     

      游戏开始了,一个游戏,会需要很多的对象,所以我就统一写成了一个精灵对象,不同对象之间的每一帧的运动情况直接用behavior来分别编写就行了。

    XML/HTML Code复制内容到剪贴板
    1. W.Sprite = function(name , painter , behaviors , args){   
    2.     if(name !== undefined) this.name = name;   
    3.     if(painter !== undefined) this.painter = painter;   
    4.     this.top = 0;   
    5.     this.left = 0;   
    6.     this.width = 0;   
    7.     this.height = 0;   
    8.     this.velocityX = 3;   
    9.     this.velocityY = 2;   
    10.     this.visible = true;   
    11.     this.animating = false;   
    12.     this.behaviors = behaviors;   
    13.     this.rotateAngle = 0;   
    14.     this.blood = 50;   
    15.     this.fullBlood = 50;   
    16.     if(name==="plan"){   
    17.         this.rotateSpeed = 0.05;   
    18.         this.rotateLeft = false;   
    19.         this.rotateRight = false;   
    20.         this.fire = false;   
    21.         this.firePerFrame = 10;   
    22.         this.fireLevel = 1;   
    23.     }else if(name==="star"){   
    24.         this.width = Math.random()*2;   
    25.         this.speed = 1*this.width/2;   
    26.         this.lightLength = 5;   
    27.         this.cacheCanvas = document.createElement("canvas");   
    28.         thisthis.cacheCtx = this.cacheCanvas.getContext('2d');   
    29.         thisthis.cacheCanvas.width = this.width+this.lightLength*2;   
    30.         thisthis.cacheCanvas.height = this.width+this.lightLength*2;   
    31.         this.painter.cache(this);   
    32.     }else if(name==="badPlan"){   
    33.         this.badKind = 1;   
    34.         this.speed = 2;   
    35.         this.rotateAngle = Math.PI;   
    36.     }else if(name==="missle"){   
    37.         this.width = missleWidth;   
    38.     }else if(name==="boom"){   
    39.         this.width = boomWidth;   
    40.     }else if(name==="food"){   
    41.         this.width = 40;   
    42.         this.speed = 3;   
    43.         this.kind = "LevelUP"  
    44.     }   
    45.     this.toLeft = false;   
    46.     this.toTop = false;   
    47.     this.toRight = false;   
    48.     this.toBottom = false;   
    49.   
    50.     this.outArcRadius = Math.sqrt((this.width/2*this.width/2)*2);   
    51.   
    52.     if(args){   
    53.         for(var arg in args){   
    54.             this[arg] = args[arg];   
    55.         }   
    56.     }   
    57. }   
    58. Sprite.prototype = {   
    59.     constructor:Sprite,   
    60.     paint:function(){   
    61.         if(this.name==="badPlan"){this.update();}   
    62.   
    63.         if(this.painter !== undefined && this.visible){   
    64.             if(this.name!=="badPlan") {   
    65.                 this.update();   
    66.             }   
    67.             if(this.name==="plan"||this.name==="missle"||this.name==="badPlan"){   
    68.                 ctx.save();   
    69.                 ctx.translate(this.left , this.top);   
    70.                 ctx.rotate(this.rotateAngle);   
    71.                 this.painter.paint(this);   
    72.                 ctx.restore();   
    73.             }else {   
    74.                 this.painter.paint(this);   
    75.             }   
    76.         }   
    77.     },   
    78.     update:function(time){   
    79.         if(this.behaviors){   
    80.             for(var i=0;i<this.behaviors.length;i++){   
    81.                 this.behaviors[i].execute(this,time);   
    82.             }   
    83.         }   
    84.     }   
    85. }   


    写出精灵类后,就可以通过编写每个的painter以及behavior来生成不同的对象了。接下来就是写painter了,painter分成两种,一种是普通的painter,一种就是精灵表painter,因为像爆炸动画,飞机开枪动画,都不是一张图片就能搞定的,所以就需要用到精灵表了:

    而绘制这些就要为他们定制一个精灵表绘制器,下面这个是最简单的精灵表绘制器,针对游戏的复杂性可以相对的修改精灵表写法,直到合适,不过原理都大同小异,就是小修小改而已:

    XML/HTML Code复制内容到剪贴板
    1. var SpriteSheetPainter = function(cells){   
    2.             this.cells = cells || [];   
    3.             this.cellIndex = 0;   
    4.         }   
    5.         SpriteSheetPainter.prototype = {   
    6.             advance:function(){   
    7.                 if(this.cellIndex === this.cells.length-1){   
    8.                     this.cellIndex = 0;   
    9.                 }   
    10.                 else this.cellIndex++;   
    11.             },   
    12.             paint:function(sprite){   
    13.                 var cell = this.cells[this.cellIndex];   
    14.                 context.drawImage(spritesheet , cell.x , cell.y , cell.w , cell.h , sprite.left , sprite.top , cell.w , cell.h);   
    15.             }   
    16.         }     

    而普通的绘制器就更简单了,直接写一个painter,把要画的什么东西都写进去就行了。

    有了精灵类和精灵表绘制器后,我们就可以把星星,飞机,子弹,爆炸对象都写出来了:下面是整个allSprite.js的代码:

    JavaScript Code复制内容到剪贴板
    1. (function(W){   
    2.     "use strict"  
    3.     var planWidth = 24,   
    4.         planHeight = 24,   
    5.         missleWidth = 70,   
    6.         missleHeight = 70,   
    7.         boomWidth = 60;   
    8.     //精灵类   
    9.     W.Sprite = function(name , painter , behaviors , args){   
    10.         if(name !== undefined) this.name = name;   
    11.         if(painter !== undefined) this.painter = painter;   
    12.         this.top = 0;   
    13.         this.left = 0;   
    14.         this.width = 0;   
    15.         this.height = 0;   
    16.         this.velocityX = 3;   
    17.         this.velocityY = 2;   
    18.         this.visible = true;   
    19.         this.animating = false;   
    20.         this.behaviors = behaviors;   
    21.         this.rotateAngle = 0;   
    22.         this.blood = 50;   
    23.         this.fullBlood = 50;   
    24.         if(name==="plan"){   
    25.             this.rotateSpeed = 0.05;   
    26.             this.rotateLeft = false;   
    27.             this.rotateRight = false;   
    28.             this.fire = false;   
    29.             this.firePerFrame = 10;   
    30.             this.fireLevel = 1;   
    31.         }else if(name==="star"){   
    32.             this.width = Math.random()*2;   
    33.             this.speed = 1*this.width/2;   
    34.             this.lightLength = 5;   
    35.             this.cacheCanvas = document.createElement("canvas");   
    36.             this.cacheCtx = this.cacheCanvas.getContext('2d');   
    37.             this.cacheCanvas.width = this.width+this.lightLength*2;   
    38.             this.cacheCanvas.height = this.width+this.lightLength*2;   
    39.             this.painter.cache(this);   
    40.         }else if(name==="badPlan"){   
    41.             this.badKind = 1;   
    42.             this.speed = 2;   
    43.             this.rotateAngle = Math.PI;   
    44.         }else if(name==="missle"){   
    45.             this.width = missleWidth;   
    46.         }else if(name==="boom"){   
    47.             this.width = boomWidth;   
    48.         }else if(name==="food"){   
    49.             this.width = 40;   
    50.             this.speed = 3;   
    51.             this.kind = "LevelUP"  
    52.         }   
    53.         this.toLeft = false;   
    54.         this.toTop = false;   
    55.         this.toRight = false;   
    56.         this.toBottom = false;   
    57.   
    58.         this.outArcRadius = Math.sqrt((this.width/2*this.width/2)*2);   
    59.   
    60.         if(args){   
    61.             for(var arg in args){   
    62.                 this[arg] = args[arg];   
    63.             }   
    64.         }   
    65.     }   
    66.     Sprite.prototype = {   
    67.         constructor:Sprite,   
    68.         paint:function(){   
    69.             if(this.name==="badPlan"){this.update();}   
    70.   
    71.             if(this.painter !== undefined && this.visible){   
    72.                 if(this.name!=="badPlan") {   
    73.                     this.update();   
    74.                 }   
    75.                 if(this.name==="plan"||this.name==="missle"||this.name==="badPlan"){   
    76.                     ctx.save();   
    77.                     ctx.translate(this.left , this.top);   
    78.                     ctx.rotate(this.rotateAngle);   
    79.                     this.painter.paint(this);   
    80.                     ctx.restore();   
    81.                 }else {   
    82.                     this.painter.paint(this);   
    83.                 }   
    84.             }   
    85.         },   
    86.         update:function(time){   
    87.             if(this.behaviors){   
    88.                 for(var i=0;i<this.behaviors.length;i++){   
    89.                     this.behaviors[i].execute(this,time);   
    90.                 }   
    91.             }   
    92.         }   
    93.     }   
    94.   
    95.     // 精灵表绘制器   
    96.     W.SpriteSheetPainter = function(cells , isloop , endCallback , spritesheet){   
    97.         this.cells = cells || [];   
    98.         this.cellIndex = 0;   
    99.         this.dateCount = null;   
    100.         this.isloop = isloop;   
    101.         this.endCallback = endCallback;   
    102.         this.spritesheet = spritesheet;   
    103.     }   
    104.     SpriteSheetPainter.prototype = {   
    105.         advance:function(){   
    106.             this.cellIndex = this.isloop?(this.cellIndex===this.cells.length-1?0:this.cellIndex+1):(this.cellIndex+1);   
    107.         },   
    108.         paint:function(sprite){   
    109.             if(this.dateCount===null){   
    110.                 this.dateCount = new Date();   
    111.             }else {   
    112.                 var newd = new Date();   
    113.                 var tc = newd-this.dateCount;   
    114.                 if(tc>40){   
    115.                     this.advance();   
    116.                     this.dateCount = newd;   
    117.                 }   
    118.             }   
    119.             if(this.cellIndex<this.cells.length || this.isloop){   
    120.                 var cell = this.cells[this.cellIndex];   
    121.                 ctx.drawImage(this.spritesheet , cell.x , cell.y , cell.w , cell.h , sprite.left-sprite.width/2 , sprite.top-sprite.width/2 , cell.w , cell.h);   
    122.             } else if(this.endCallback){   
    123.                 this.endCallback.call(sprite);   
    124.                 this.cellIndex = 0;   
    125.             }   
    126.         }   
    127.     }   
    128.   
    129.     //特制飞机精灵表绘制器   
    130.     W.controllSpriteSheetPainter = function(cells , spritesheet){   
    131.         this.cells = cells || [];   
    132.         this.cellIndex = 0;   
    133.         this.dateCount = null;   
    134.         this.isActive = false;   
    135.         this.derection = true;   
    136.         this.spritesheet = spritesheet;   
    137.     }   
    138.     controllSpriteSheetPainter.prototype = {   
    139.         advance:function(){   
    140.             if(this.isActive){   
    141.                 this.cellIndex++;   
    142.                 if(this.cellIndex === this.cells.length){   
    143.                     this.cellIndex = 0;   
    144.                     this.isActive = false;   
    145.                 }   
    146.             }   
    147.         },   
    148.         paint:function(sprite){   
    149.             if(this.dateCount===null){   
    150.                 this.dateCount = new Date();   
    151.             }else {   
    152.                 var newd = new Date();   
    153.                 var tc = newd-this.dateCount;   
    154.                 if(tc>sprite.firePerFrame){   
    155.                     this.advance();   
    156.                     this.dateCount = newd;   
    157.                 }   
    158.             }   
    159.             var cell = this.cells[this.cellIndex];   
    160.             ctx.drawImage(this.spritesheet , cell.x , cell.y , cell.w , cell.h , -planWidth/2 , -planHeight/2 , cell.w , cell.h);   
    161.         }   
    162.     }   
    163.   
    164.     W.planBehavior = [   
    165.         {execute:function(sprite,time){   
    166.             if(sprite.toTop){   
    167.                 sprite.top = sprite.top<planHeight/2? sprite.top : sprite.top-sprite.velocityY;   
    168.             }   
    169.             if(sprite.toLeft){   
    170.                 sprite.left = sprite.left<planWidth/2? sprite.left : sprite.left-sprite.velocityX;   
    171.             }   
    172.             if(sprite.toRight){   
    173.                 sprite.left = sprite.left>canvas.width-planWidth/2? sprite.left : sprite.left+sprite.velocityX;   
    174.             }   
    175.             if(sprite.toBottom){   
    176.                 sprite.top = sprite.top>canvas.height-planHeight/2? sprite.top : sprite.top+sprite.velocityY;   
    177.             }   
    178.             if(sprite.rotateLeft){   
    179.                 sprite.rotateAngle -= sprite.rotateSpeed;   
    180.             }   
    181.             if(sprite.rotateRight){   
    182.                 sprite.rotateAngle += sprite.rotateSpeed;   
    183.             }   
    184.             if(sprite.fire&&!sprite.painter.isActive){   
    185.                 sprite.painter.isActive = true;   
    186.                 this.shot(sprite);   
    187.   
    188.             }   
    189.         },   
    190.         shot:function(sprite){   
    191.             this.addMissle(sprite , sprite.rotateAngle);   
    192.             var missleAngle = 0.1   
    193.             for(var i=1;i<sprite.fireLevel;i++){   
    194.                 this.addMissle(sprite , sprite.rotateAngle-i*missleAngle);   
    195.                 this.addMissle(sprite , sprite.rotateAngle+i*missleAngle);   
    196.             }   
    197.   
    198.             var audio = document.getElementsByTagName("audio");   
    199.             for(var i=0;i<audio.length;i++){   
    200.                 console.log(audio[i].paused)   
    201.                 if(audio[i].src.indexOf("shot")>=0&&audio[i].paused){   
    202.                     audio[i].play();   
    203.                     break;   
    204.                 }   
    205.             }   
    206.         },   
    207.         addMissle:function(sprite , angle){   
    208.                 for(var j=0;j<missles.length;j++){   
    209.                     if(!missles[j].visible){   
    210.                         missles[j].left = sprite.left;   
    211.                         missles[j].top = sprite.top;   
    212.                         missles[j].rotateAngle = angle;   
    213.                         var missleSpeed = 20;   
    214.                         missles[j].velocityX = missleSpeed*Math.sin(-missles[j].rotateAngle);   
    215.                         missles[j].velocityY = missleSpeed*Math.cos(-missles[j].rotateAngle);   
    216.                         missles[j].visible = true;   
    217.                         break;   
    218.                     }   
    219.                 }   
    220.             }   
    221.         }   
    222.     ]   
    223.   
    224.     W.starBehavior = [   
    225.         {execute:function(sprite,time){   
    226.             if(sprite.top > canvas.height){   
    227.                 sprite.left = Math.random()*canvas.width;   
    228.                 sprite.top = Math.random()*canvas.height - canvas.height;   
    229.             }   
    230.             sprite.top += sprite.speed;   
    231.         }}   
    232.     ]   
    233.   
    234.     W.starPainter = {   
    235.         paint:function(sprite){   
    236.             ctx.drawImage(sprite.cacheCanvas , sprite.left-sprite.width/2-sprite.lightLength , sprite.top-sprite.width/2-sprite.lightLength)   
    237.         },   
    238.   
    239.         cache:function(sprite){   
    240.             sprite.cacheCtx.save();   
    241.             var opacity = 0.5,addopa = 1/sprite.lightLength;   
    242.             sprite.cacheCtx.fillStyle = "rgba(255,255,255,0.8)";   
    243.             sprite.cacheCtx.beginPath();   
    244.             sprite.cacheCtx.arc(sprite.width/2+sprite.lightLength , sprite.width/2+sprite.lightLength , sprite.width/2 , 0 , 2*Math.PI);   
    245.             sprite.cacheCtx.fill();   
    246.             for(var i=1;i<=sprite.lightLength;i+=2){   
    247.                 opacity-=addopa;   
    248.                 sprite.cacheCtx.fillStyle = "rgba(255,255,255,"+opacity+")";   
    249.                 sprite.cacheCtx.beginPath();   
    250.                 sprite.cacheCtx.arc(sprite.width/2+sprite.lightLength , sprite.width/2+sprite.lightLength , sprite.width/2+i , 0 , 2*Math.PI);   
    251.                 sprite.cacheCtx.fill();   
    252.             }   
    253.         }   
    254.     }   
    255.   
    256.     W.foodBehavior = [   
    257.         {execute:function(sprite,time){   
    258.             sprite.top += sprite.speed;   
    259.             if(sprite.top > canvas.height+sprite.width){   
    260.                 sprite.visible = false;   
    261.             }   
    262.         }}   
    263.     ]   
    264.   
    265.     W.foodPainter = {   
    266.         paint:function(sprite){   
    267.             ctx.fillStyle = "rgba("+parseInt(Math.random()*255)+","+parseInt(Math.random()*255)+","+parseInt(Math.random()*255)+",1)"  
    268.             ctx.font="15px 微软雅黑"  
    269.             ctx.textAlign = "center";   
    270.             ctx.textBaseline = "middle";   
    271.             ctx.fillText(sprite.kind , sprite.left , sprite.top);   
    272.         }   
    273.     }   
    274.   
    275.   
    276.   
    277.     W.missleBehavior = [{   
    278.         execute:function(sprite,time){   
    279.             sprite.left -= sprite.velocityX;   
    280.             sprite.top -= sprite.velocityY;   
    281.             if(sprite.left<-missleWidth/2||sprite.top<-missleHeight/2||sprite.left>canvas.width+missleWidth/2||sprite.top<-missleHeight/2){   
    282.                 sprite.visible = false;   
    283.             }   
    284.         }   
    285.     }];   
    286.   
    287.     W.misslePainter = {   
    288.         paint:function(sprite){   
    289.             var img = new Image();   
    290.             img.src="../planGame/image/plasma.png"  
    291.             ctx.drawImage(img , -missleWidth/2+1 , -missleHeight/2+1 , missleWidth , missleHeight);   
    292.         }   
    293.     }   
    294.   
    295.     W.badPlanBehavior = [{   
    296.         execute:function(sprite,time){   
    297.             if(sprite.top > canvas.height || !sprite.visible){   
    298.                 var random = Math.random();   
    299.   
    300.                 if(point>=200&&point<400){   
    301.                     sprite.fullBlood = 150;   
    302.                     if(random<0.1){   
    303.                         sprite.badKind = 2;   
    304.                         sprite.fullBlood = 250;   
    305.                     }   
    306.                 }else if(point>=400&&point<600){   
    307.                     sprite.fullBlood = 250;   
    308.                     if(random<0.2){   
    309.                         sprite.badKind = 2;   
    310.                         sprite.fullBlood = 400;   
    311.                     }   
    312.                     if(random<0.1){   
    313.                         sprite.badKind = 3;   
    314.                         sprite.fullBlood = 600;   
    315.                     }   
    316.                 }else if(point>=600){   
    317.                     sprite.fullBlood = 500;   
    318.                     if(random<0.4){   
    319.                         sprite.badKind = 2;   
    320.                         sprite.fullBlood = 700;   
    321.                     }   
    322.                     if(random<0.2){   
    323.                         sprite.badKind = 3;   
    324.                         sprite.fullBlood = 1000;   
    325.                     }   
    326.                 }   
    327.   
    328.                 sprite.visible = true;   
    329.                 sprite.blood = sprite.fullBlood;   
    330.                 sprite.left = Math.random()*(canvas.width-2*planWidth)+planWidth;   
    331.                 sprite.top = Math.random()*canvas.height - canvas.height;   
    332.             }   
    333.             sprite.top += sprite.speed;   
    334.         },   
    335.         shot:function(sprite){   
    336.             this.addMissle(sprite , sprite.rotateAngle);   
    337.             var missleAngle = 0.1   
    338.             for(var i=1;i<sprite.fireLevel;i++){   
    339.                 this.addMissle(sprite , sprite.rotateAngle-i*missleAngle);   
    340.                 this.addMissle(sprite , sprite.rotateAngle+i*missleAngle);   
    341.             }   
    342.         },   
    343.         addMissle:function(sprite , angle){   
    344.             for(var j=0;j<missles.length;j++){   
    345.                 if(!missles[j].visible){   
    346.                     missles[j].left = sprite.left;   
    347.                     missles[j].top = sprite.top;   
    348.                     missles[j].rotateAngle = angle;   
    349.                     var missleSpeed = 20;   
    350.                     missles[j].velocityX = missleSpeed*Math.sin(-missles[j].rotateAngle);   
    351.                     missles[j].velocityY = missleSpeed*Math.cos(-missles[j].rotateAngle);   
    352.                     missles[j].visible = true;   
    353.                     break;   
    354.                 }   
    355.             }   
    356.         }   
    357.     }];   
    358.   
    359.     W.badPlanPainter = {   
    360.         paint:function(sprite){   
    361.             var img = new Image();   
    362.             img.src="../planGame/image/ship.png"  
    363.             switch(sprite.badKind){   
    364.                 case 1:ctx.drawImage(img , 96 , 0 , planWidth , planWidth , -planWidth/2 , -planHeight/2 , planWidth , planWidth);   
    365.                 break;   
    366.   
    367.                 case 2:ctx.drawImage(img , 120 , 0 , planWidth , planWidth , -planWidth/2 , -planHeight/2 , planWidth , planWidth);   
    368.                 break;   
    369.   
    370.                 case 3:ctx.drawImage(img , 144 , 0 , planWidth , planWidth , -planWidth/2 , -planHeight/2 , planWidth , planWidth);   
    371.                 break;   
    372.             }   
    373.   
    374.             ctx.strokeStyle = "#FFF";   
    375.             ctx.fillStyle = "#F00";   
    376.             var bloodHeight = 1;   
    377.             ctx.strokeRect(-planWidth/2-1 , planHeight+bloodHeight+3 , planWidth+2 , bloodHeight+2);   
    378.             ctx.fillRect(planWidth/2-planWidth*sprite.blood/sprite.fullBlood , planHeight+bloodHeight+3 , planWidth*sprite.blood/sprite.fullBlood , bloodHeight);   
    379.         }   
    380.     }   
    381.   
    382.     W.planSize = function(){   
    383.         return {   
    384.             w:planWidth,   
    385.             h:planHeight   
    386.         }       
    387.     }   
    388. })(window);   

    这些绘制方法之类的都相对比较简单。

      主要说一下飞机的运动以及对象数量的控制,飞机怎么运动?毫无疑问,通过键盘控制它运动,可能很多人就会想到通过keydown这个方法按下的时候通过判断keyCode来让飞机持续运动。但是有个问题,keydown事件不支持多键按下,也就是说,当你按下X键时,keyCode是88,与此同时你按下方向键后,keyCode会瞬间变成37,也就是说,如果你单纯的想靠keydown来控制飞机运动,飞机就只能做一件事,要么只可以往某个方向移动,要么只会开枪。

      所以,我们要通过keydown和keyup来实现飞机的运动,原理很容易理解:当我们按下往左的方向键时,我们给飞机一个往左的状态,也就是让飞机的toLeft属性为true,而在动画循环中,判断飞机的状态,如果toLeft为true则飞机的x值不停地减少,飞机也就会不停地往左移动,然后当我们抬起手指时触发keyup事件,我们就再keyup事件中解除飞机往左的状态。飞机也就停止往左移动了。其他状态也一样的原理,这样写的话,就能够让飞机多种状态于一生了。可以同时开枪同时到处跑了。

    实现的代码如下:

    XML/HTML Code复制内容到剪贴板
    1. //keydown/keyup事件的绑定     
    2.   window.onkeydown = function(event){   
    3.             switch(event.keyCode){   
    4.                 case 88:myplan.fire = true;   
    5.                 break;   
    6.                 case 90:myplan.rotateLeft=true;   
    7.                 break;   
    8.                 case 67:myplan.rotateRight=true;   
    9.                 break;   
    10.                 case 37:myplan.toLeft = true;   
    11.                 break;   
    12.                 case 38:myplan.toTop = true;   
    13.                 break;   
    14.                 case 39:myplan.toRight = true;   
    15.                 break;   
    16.                 case 40:myplan.toBottom = true;   
    17.                 break;   
    18.             }   
    19.         }   
    20.   
    21.         window.onkeyup = function(event){   
    22.             switch(event.keyCode){   
    23.                 case 88:myplan.fire = false;   
    24.                 break;   
    25.                 case 90:myplan.rotateLeft=false;   
    26.                 break;   
    27.                 case 67:myplan.rotateRight=false;   
    28.                 break;   
    29.                 case 37:myplan.toLeft = false;   
    30.                 break;   
    31.                 case 38:myplan.toTop = false;   
    32.                 break;   
    33.                 case 39:myplan.toRight = false;   
    34.                 break;   
    35.                 case 40:myplan.toBottom = false;   
    36.                 break;   
    37.             }   
    38.         }       
    39.   
    40.   
    41. //飞机每一帧的状态更新处理代码   
    42. execute:function(sprite,time){   
    43.             if(sprite.toTop){   
    44.                 spritesprite.top = sprite.top<planHeight/2? sprite.top : sprite.top-sprite.velocityY;   
    45.             }   
    46.             if(sprite.toLeft){   
    47.                 spritesprite.left = sprite.left<planWidth/2? sprite.left : sprite.left-sprite.velocityX;   
    48.             }   
    49.             if(sprite.toRight){   
    50.                 spritesprite.left = sprite.left>canvas.width-planWidth/2? sprite.left : sprite.left+sprite.velocityX;   
    51.             }   
    52.             if(sprite.toBottom){   
    53.                 spritesprite.top = sprite.top>canvas.height-planHeight/2? sprite.top : sprite.top+sprite.velocityY;   
    54.             }   
    55.             if(sprite.rotateLeft){   
    56.                 sprite.rotateAngle -sprite.rotateSpeed;   
    57.             }   
    58.             if(sprite.rotateRight){   
    59.                 sprite.rotateAngle += sprite.rotateSpeed;   
    60.             }   
    61.             if(sprite.fire&&!sprite.painter.isActive){   
    62.                 sprite.painter.isActive = true;   
    63.                 this.shot(sprite);   
    64.   
    65.             }     

    就是如此简单。

      然后说下对象控制,打飞机游戏,会发射大量子弹,产生大量对象,包括爆炸啊,飞机啊,子弹等,如果不停地进行对象的生成和销毁,会让浏览器的负荷变得很大,运行了一段时间后就会卡出翔了。所以,我们要用可以循环利用的对象来解决这个问题,不进行对象的销毁,对所有对象进行保存,循环利用。

      我的做法就是,在游戏初始化的时候,直接生成一定数量的对象,存放在数组里面。当我们需要一个对象的时候,就从里面取,当用完后,再放回数组里面。数组里的所有对象都有一个属性,visible,代表对象当前是否可用。

      举个例子,当我的飞机发射一发炮弹,我需要一发炮弹,所以我就到炮弹数组里遍历,如果遍历到的炮弹visible为true,也就说明该对象正在使用着,不能拿来用,所以继续遍历,直到遍历到visible为false的炮弹对象,说明这个对象暂时没人用。然后就可以拿过来重新设置属性,投入使用了。当炮弹击中敌人或者打出画布外的时候,把炮弹的visible设成false,又成了一个没人用的炮弹在数组里存放起来等待下一次调用。

      所以,我们要预算算好页面大概要用到多少个对象,然后就预先准备好对象,这样,在游戏进行中,不会有对象进行生成和销毁,对游戏性能方面就有了提升了。

      

      最后再说下音频,游戏里面要用到多个同样的audio才能保证音效的不间断性:
    复制代码

    XML/HTML Code复制内容到剪贴板
    1. var audio = document.getElementsByTagName("audio");   
    2.                                             for(var i=0;i<audio.length;i++){   
    3.                                                 console.log(audio[i].paused)   
    4.                                                 if(audio[i].src.indexOf("boom")>=0&&audio[i].paused){   
    5.                                                     audio[i].play();   
    6.                                                     break;   
    7.                                                 }   
    8.                                             }  

     

    好吧,基本上就这样了。技术或许还不够好,纯碎做个记录,如果代码有不当正处,欢迎指出,共同学习。

    源码地址:https://github.com/whxaxes/canvas-test/tree/gh-pages/src/Game-demo/planGame

    上一篇:用HTML5制作数字时钟的教程
    下一篇:浅谈利用缓存来优化HTML5 Canvas程序的性能
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯

    时间:9:00-21:00 (节假日不休)

    地址:江苏信息产业基地11号楼四层

    《增值电信业务经营许可证》 苏B2-20120278

    利用HTML5 Canvas制作一个简单的打飞机游戏 利用,HTML5,Canvas,制作,一个,