空戰遊戲到今天能夠玩了,玩法仍是方向鍵(或AWSD)控制飛機位置,空格鍵開炮,吃五星升級,被敵機打中降級直到擊落,與敵機相撞則GG。javascript
點此下載程序1.16版,用CHrome打開index.html試玩。html
1.17版改變了敵機的出現位置。java
1.18版爲了防止居中火力全開模式,將讓敵機一次出現兩架,出現位置在四分之一和四分之三處,另外敵機子彈也加上了隨機的橫向速度。git
1.19版改變了對象的描繪順序,避免了子彈出如今機翼上的尷尬。github
Github url:https://github.com/horn19782016/PlaneCombat-WarIIshell
這個遊戲相比前做炸彈人,技術上的亮點在於Mng類中對以往對象的複用,避免了由於不斷的new對象致使越玩越卡的狀況發生。canvas
圖例:數組
近一千三百行源碼:瀏覽器
<!DOCTYPE html> <html lang="utf-8"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <head> <title>空戰遊戲1.19 19.3.18 18:42 by:逆火狂飆 horn19782016@163.com</title> <style> *{ margin:1px; padding:1px; } #canvas{ background:#ffffff; } #controls{ float:left; } </style> </head> <body onload="init()"> <table border="0px"> <tr> <td width="50px"valign="top"> <div id="controls"> <input id='animateBtn' type='button' value='開始'/> <input id='restartBtn' type='button' value='再來一盤'/> </div> </td> <td width="100%" align="center"> <canvas id="canvas" width="1200px" height="562px" > 出現文字表示你的瀏覽器不支持HTML5 </canvas> </td> </tr> </table> </body> </html> <script type="text/javascript"> <!-- var paused=true; animateBtn.onclick=function(e){ paused=! paused; if(paused){ animateBtn.value="開始"; }else{ animateBtn.value="暫停"; window.requestAnimationFrame(animate); } } restartBtn.onclick=function(e){ init(); } var ctx; // 繪圖環境 var bg; // 背景 var lastTime=0; // 上次時間,用以計算FPS var fps=0; // Frame per second var myPlane; // 己方飛機 var myShellMng; // 己方子彈管理類 var enemyPlaneMng; // 敵方飛機管理者 var explosionMng; // 爆炸管理者 var enemyShellMng; // 敵方子彈管理類 var bonusMng; // 獎勵管理類(只對本機有效) var shootDownCnt; // 擊落數量 var gameover=false; // 是否遊戲結束 // 初始化,被onload調用 function init(){ restartBtn.style.visibility="hidden"; shootDownCnt=0; gameover=false; // 建立背景對象 bg=new Background(); // 初始化CTX var canvas=document.getElementById('canvas'); canvas.width=bg.width*6; canvas.height=bg.height*4; ctx=canvas.getContext('2d'); // +new Date=new Date().getTime(); lastTime=+new Date; // 建立本機對象 myPlane=new MyPlane(ctx.canvas.width/2,canvas.height-100); // 本機子彈管理者 myShellMng=new MyShellMng(); // 敵機管理者 enemyPlaneMng=new EnemyPlaneMng(); // 爆炸管理者 explosionMng=new ExplosionMng(); // 敵方子彈管理者 enemyShellMng=new EnemyShellMng(); // 獎勵管理類 bonusMng=new BonusMng(); // 響應鍵盤按下事件 canvas.addEventListener('keydown', doKeyDown, true); window.addEventListener('keydown', doKeyDown, true); // 響應鍵盤彈起事件 canvas.addEventListener('keyup', doKeyUp, true); window.addEventListener('keyup', doKeyUp, true); canvas.focus(); }; //------------------------------------ // 響應鍵盤按下事件 //------------------------------------ function doKeyDown(e) { var keyID = e.keyCode ? e.keyCode :e.which; if(keyID === 38 || keyID === 87) { // up arrow and W myPlane.toUp=true; e.preventDefault(); } if(keyID === 40 || keyID === 83) { // down arrow and S myPlane.toDown=true; e.preventDefault(); } if(keyID === 39 || keyID === 68) { // right arrow and D myPlane.toRight=true; e.preventDefault(); } if(keyID === 37 || keyID === 65) { // left arrow and A myPlane.toLeft=true; e.preventDefault(); } if(keyID === 32 ) { // SpaceBar // 按下和彈起必須成對出現,不然畫面會僵 myPlane.shoot(); e.preventDefault(); } } //------------------------------------ // 響應鍵盤彈起事件 //------------------------------------ function doKeyUp(e) { var keyID = e.keyCode ? e.keyCode :e.which; if(keyID === 38 || keyID === 87) { // up arrow and W myPlane.toUp=false; e.preventDefault(); } if(keyID === 40 || keyID === 83) { // down arrow and S myPlane.toDown=false; e.preventDefault(); } if(keyID === 39 || keyID === 68) { // right arrow and D myPlane.toRight=false; e.preventDefault(); } if(keyID === 37 || keyID === 65) { // left arrow and A myPlane.toLeft=false; e.preventDefault(); } if(keyID === 32 ) { // SpaceBar // 按下和彈起必須成對出現,不然畫面會僵 e.preventDefault(); } } // 更新各對象狀態 function update(){ // 本機移動 myPlane.move(); // 本方炮彈移動 myShellMng.move(); // 判斷敵機和本機是否相撞 enemyPlaneMng.isCrashed(myPlane); // 敵機移動 enemyPlaneMng.move(); // 判斷本方子彈是否與敵機相撞 myShellMng.probeCrashedEnemyPlane(); // 補充敵機 enemyPlaneMng.reload(); // 移動敵方子彈 enemyShellMng.move(); // 判斷敵方子彈是否與本機相撞 enemyShellMng.probeCrashedMyPlane(); // 移動獎勵 bonusMng.move(); bonusMng.isCrashed(myPlane); } // 取得測試文本 function getTestString(){ var str=""; str+="本方炮彈:"+myShellMng.getCount()+" "; str+="敵機數:"+enemyPlaneMng.getCount()+" "; str+="敵方炮彈:"+enemyShellMng.getCount()+" "; str+="獎勵數:"+bonusMng.getCount()+" "; str+="爆炸數:"+explosionMng.getCount()+" "; return str; } // 在CTX畫出各個對象 function draw(){ // 清屏 ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height); // 畫背景 fps=calculateFps(new Date); bg.setOffset(fps); bg.paint(ctx); //var bgImg=bg.getImage(); //ctx.drawImage(bgImg,0,bg.height-bg.Offset,bg.width,bg.Offset,0,0,ctx.canvas.width,4*bg.Offset); //ctx.drawImage(bgImg,0,0,bg.width,bg.height-bg.Offset,0,4*bg.Offset,canvas.width,canvas.height-4*bg.Offset); // 畫己方子彈 myShellMng.paint(ctx); // 畫己方飛機 myPlane.paint(ctx); // 畫敵方子彈 enemyShellMng.paint(ctx); // 畫敵機 enemyPlaneMng.paint(ctx); // 畫爆炸 explosionMng.paint(ctx); // 畫獎勵 bonusMng.paint(ctx); // 顯示擊落數 ctx.font="bold 16px 宋體"; ctx.fillStyle='white'; ctx.fillText("ShootDown:"+shootDownCnt,20,30); // 顯示測試文本 //ctx.fillText(getTestString(),20,50); // 遊戲結束 if(gameover){ ctx.font="bold 64px 宋體"; ctx.strokeStyle='white'; ctx.strokeText("Game over",ctx.canvas.width/2-138,ctx.canvas.height/2); } } // 計算FPS function calculateFps(now){ var retval=1000/(now-lastTime); lastTime=now; return retval; } // 播放 function animate(){ if(!paused){ update(); draw(); window.requestAnimationFrame(animate); } } // 常規函數:角度獲得弧度 function getRad(degree){ return degree/180*Math.PI; } //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>點類定義開始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Point=function(x,y){ this.x=x; this.y=y; } Point.prototype={ x:0, // 橫座標 y:0, // 縱座標 } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<點類定義結束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>背景類定義開始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Background=function(){ this.width=104; this.height=156; this.files=['bgBlue.jpg','bgRiver.jpg','bgSky.jpg','bgVolcano.jpg']; this.Offset=0; this.velocity=40; } Background.prototype={ width:104, // 背景圖片原始寬度 height:156, // 背景圖片原始高度 files:[], // 圖片數組 Offset:0, // 偏移值 velocity:40, // 背景移動速度 loopValue:0, // 循環累加值,用來肯定時哪一張圖片 // 取得四張圖片裏面一張 getImage:function(){ this.loopValue++; if(this.loopValue>=3999){ this.loopValue=0; } var index=Math.floor(this.loopValue/1000); var img=new Image(); img.src=this.files[index]; return img; }, // 設置偏移值,爲移動背景作準備 setOffset:function(fps){ this.Offset=this.Offset<this.height?this.Offset+this.velocity/fps:0; }, // 畫背景 paint:function(ctx){ var bgImg=this.getImage(); ctx.drawImage(bgImg,0,this.height-this.Offset,this.width,this.Offset,0,0,ctx.canvas.width,4*this.Offset); ctx.drawImage(bgImg,0,0,this.width,this.height-this.Offset,0,4*this.Offset,canvas.width,canvas.height-4*this.Offset); }, } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<背景類定義結束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>我方戰機類定義開始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> MyPlane=function(x,y){ Point.apply(this,arguments); this.types=[ {width:56,height:41, file:'m0.png',shellCount:1,shellSpeed:4,}, {width:56,height:41, file:'m1.png',shellCount:2,shellSpeed:4.5,}, {width:80,height:54, file:'m2.png',shellCount:3,shellSpeed:5,}, {width:109,height:83,file:'m3.png',shellCount:4,shellSpeed:5.5,}, {width:109,height:81,file:'m4.png',shellCount:5,shellSpeed:6,}, {width:109,height:91,file:'m5.png',shellCount:6,shellSpeed:6.5,}, {width:117,height:80,file:'m6.png',shellCount:7,shellSpeed:7,}, {width:117,height:82,file:'m7.png',shellCount:8,shellSpeed:7.5,}, {width:117,height:99,file:'m8.png',shellCount:9,shellSpeed:8,}, ]; } MyPlane.prototype={ types:[], // 存儲圖片的數組 step:4, // 每次移動多遠 toLeft:false, // 是否向左移動 toRight:false, // 是否向右移動 toUp:false, // 是否向上移動 toDown:false, // 是否向下移動 level:0, // 等級 destroyed:false, // 是否被擊毀或撞毀 paint:function(ctx){ if(this.destroyed==false){ var index=this.level; if(index>this.types.length-1){ console.log("非法的本機level值="+this.level); index=this.types.length-1; } var img=new Image(); img.src=this.types[index].file; ctx.drawImage(img,this.x-this.types[index].width/2,this.y-this.types[index].height/2); // 標出this,x,this.y所在位置 //var img2=new Image(); //img2.src="shoot.png"; //ctx.drawImage(img2,this.x-5.5,this.y-5.5); } }, // 獲得飛機的左上角 getLeftUpPoint:function(){ var index=this.level; var p=new Point(this.x-this.types[index].width/2,this.y-this.types[index].height/2); return p; }, // 獲得飛機的右上角 getRightUpPoint:function(){ var index=this.level; var p=new Point(this.x+this.types[index].width/2,this.y-this.types[index].height/2); return p; }, // 獲得飛機的左下角 getLeftDownPoint:function(){ var index=this.level; var p=new Point(this.x-this.types[index].width/2,this.y+this.types[index].height/2); return p; }, // 獲得飛機的右下角 getRightDownPoint:function(){ var index=this.level; var p=new Point(this.x+this.types[index].width/2,this.y+this.types[index].height/2); return p; }, move:function(){ // 加入邊界判斷 2019年3月13日19點16分 var type=this.types[this.level].file; if(this.x<0){ this.x=0; this.toLeft=false; } if(this.x>ctx.canvas.width){ this.x=ctx.canvas.width; this.toRight=false; } if(this.y<0){ this.y=0; this.toUp=false; } if(this.y>ctx.canvas.height){ this.y=ctx.canvas.height; this.toDown=false; } // 運動 if(this.toLeft==true && this.destroyed==false){ this.x-=this.step; } if(this.toRight==true && this.destroyed==false){ this.x+=this.step; } if(this.toUp==true && this.destroyed==false){ this.y-=this.step; } if(this.toDown==true && this.destroyed==false){ this.y+=this.step; } }, // 本機開炮 shoot:function(){ if(this.destroyed==false){ var index=this.level; // 獲得炮彈 var shellCount=this.types[index].shellCount; var shells=myShellMng.fetch(shellCount); // 用來控制炮彈發射位置 var xLeft=this.x-this.types[index].width/2; var offset=this.types[index].width/(shellCount+1); for(var i=0;i<shellCount;i++){ var s=shells[i]; s.x=xLeft+(i+1)*offset; s.y=this.y; s.speed=this.types[index].shellSpeed; } } }, // 判斷以x,y爲座標的點是否進入本機機四角範圍內 isWithinLimits:function(x,y){ var index=this.level; var left=this.x-this.types[index].width/2; var top=this.y-this.types[index].height/2; var width=this.types[index].width; var height=this.types[index].height; if(left<x && x<left+width && top<y && y<top+height){ return true; }else{ return false; } }, // 看地方炮彈是否命中本機 isShooted:function(shell){ if(shell.destroyed==true){ return false; } // 若是炮彈打中本機範圍 if(this.isWithinLimits(shell.x,shell.y)){ shell.destroyed=true; // 被打中降一級,降到0如下爆炸 this.upgrade(-1); return true; } return false; }, // 本機升級 upgrade:function(value){ var upgraded=this.level+value; upgraded=Math.min(upgraded,this.types.length-1); if(upgraded<0){ // 遊戲結束 explosionMng.fire(this.x,this.y); this.destroyed=true; gameover=true; restartBtn.style.visibility="visible"; }else{ this.level=upgraded; } }, } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<我方戰機類定義結束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>我方炮彈類定義開始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> MyShell=function(x,y){ Point.apply(this,arguments); this.types=[ {width:11,height:11,file:'shell0.png'}, {width:11,height:11,file:'shell1.png'}, {width:11,height:11,file:'shell2.png'}, {width:11,height:11,file:'shell3.png'}, {width:11,height:11,file:'shell4.png'}, {width:11,height:11,file:'shell5.png'}, {width:11,height:11,file:'shell6.png'}, {width:18,height:18,file:'shell7.png'}, ]; } MyShell.prototype={ types:[], // 炮彈型號 destroyed:false,// 是否被敵機撞毀 visible:true, // 是否在CTX顯示範圍內 level:3, // 等級,用以決定炮彈型號 speed:4, // 炮彈速度(己方炮彈向上飛行) paint:function(ctx){ if(this.visible==false){ return; } if(this.destroyed==false){ // 沒被擊毀顯示炮彈型號 var img=new Image(); var index=this.level; img.src=this.types[index].file; ctx.drawImage(img,this.x-this.types[index].width/2,this.y-this.types[index].height/2); } }, move:function(){ // 設置越界不可見 if(this.x<0){ this.visible=false; } if(this.x>ctx.canvas.width){ this.visible=false; } if(this.y<0){ this.visible=false; } if(this.y>ctx.canvas.height){ this.visible=false; } if(this.visible==true && this.destroyed==false){ this.y-=this.speed; } }, } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<我方炮彈類定義結束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>我方炮彈管理者類定義開始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>19.3.16 MyShellMng=function(){ this.shells=new Array(); } MyShellMng.prototype={ shells:[], // 己方炮彈對象數組 // 取得己方炮彈數量 getCount:function(){ return this.shells.length; }, paint:function(ctx){ for(var i=0;i<this.shells.length;i++){ var s=this.shells[i]; s.paint(ctx); } }, // 取出子彈,count爲個數 fetch:function(count){ var retval=new Array(); //console.log("原有己方炮彈數量=",this.shells.length); // 先取原有的炮彈 for(var i=0;i<this.shells.length;i++){ var s=this.shells[i]; if(s.visible==false || s.destroyed==true){ s.destroyed=false; s.visible=true; if(retval.length<count){ retval.push(s); } } } // 不足再建立新的炮彈 while(retval.length<count){ var s=new MyShell(0,0); this.shells.push(s); retval.push(s); } //console.log("現有己方炮彈數量=",this.shells.length); return retval; }, // 移動炮彈 move:function(){ for(var i=0;i<this.shells.length;i++){ var s=this.shells[i]; s.move(); } }, // 判斷炮彈是否撞到敵機 probeCrashedEnemyPlane:function(){ for(var i=0;i<this.shells.length;i++){ var s=this.shells[i]; if(s.visible==true && s.destroyed==false){ if(enemyPlaneMng.isShooted(s)==true){ s.destroyed=true; return; } } } }, } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<我方炮彈管理者類定義結束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>敵方飛機類定義開始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> EnemyPlane=function(x,y,level){ Point.apply(this,arguments); this.level=level; this.types=[ {width:117,height:64,file:'e0.png',shellCount:1,shootInterval:300,shellSpeed:3 , reciprocatingRadius:12}, {width:117,height:64,file:'e1.png',shellCount:2,shootInterval:270,shellSpeed:4 , reciprocatingRadius:8.5}, {width:100,height:77,file:'e2.png',shellCount:3,shootInterval:240,shellSpeed:5 , reciprocatingRadius:6.5}, {width:117,height:85,file:'e3.png',shellCount:4,shootInterval:210,shellSpeed:6 , reciprocatingRadius:3}, {width:117,height:86,file:'e4.png',shellCount:5,shootInterval:180,shellSpeed:7 , reciprocatingRadius:4.5}, {width:117,height:93,file:'e5.png',shellCount:6,shootInterval:150,shellSpeed:8 , reciprocatingRadius:5.25}, {width:117,height:93,file:'e6.png',shellCount:6,shootInterval:120,shellSpeed:9 , reciprocatingRadius:6}, {width:117,height:96,file:'e7.png',shellCount:7,shootInterval:90, shellSpeed:11 , reciprocatingRadius:8.75}, {width:117,height:99,file:'e8.png',shellCount:8,shootInterval:60 ,shellSpeed:12, reciprocatingRadius:9.5}, ]; } EnemyPlane.prototype={ types:[], // 飛機型號數組 destroyed:false, // 是否被擊毀 level:7, // 等級,用此取飛機型號 visible:true, // 是否在ctx顯示範圍內 speed:2, // 敵機向下飛行的速度 paint:function(ctx){ // 不可見則不顯示 if(this.visible==false){ return; } if(this.destroyed==false){ // 沒被擊毀顯示飛機型號 var img=new Image(); var index=this.level; // 越界時報錯一下並取最後一張 if(index>this.types.length-1){ console.log("非法的敵機level值="+this.level); index=this.types.length-1; } img.src=this.types[index].file; ctx.drawImage(img,this.x-this.types[index].width/2,this.y-this.types[index].height/2); // 標出本機下x,y座標 //var img2=new Image(); //img2.src="shoot.png"; //ctx.drawImage(img2,this.x-5.5,this.y-5.5); } }, // 判斷以x,y爲座標的點是否進入敵機四角範圍內 isWithinLimits:function(x,y){ var index=this.level; var left=this.x-this.types[index].width/2; var top=this.y-this.types[index].height/2; var width=this.types[index].width; var height=this.types[index].height; if(left<x && x<left+width && top<y && y<top+height){ return true; }else{ return false; } }, // 敵機飛行 move:function(){ // 設置越界不可見 /*if(this.x<0){ this.visible=false; } if(this.x>ctx.canvas.width){ this.visible=false; } if(this.y<0){ this.visible=false; }*/ if(this.y>ctx.canvas.height){ this.visible=false; } if(this.visible){ var index=this.level; this.y+=this.speed; this.x+=this.types[index].reciprocatingRadius*Math.sin(getRad(this.y)); // 行動中隨機開槍 var rnd=this.getRndBetween(0,this.types[index].shootInterval);// 上限越大打得越慢,這個值能夠叫ShootInterval if(rnd<1){ this.shoot(); } } }, // 獲得隨機數 getRndBetween:function (lowerLimit,upperLimit){ return Math.floor(Math.random()*(upperLimit-lowerLimit+1))+lowerLimit; }, // 敵機開炮 shoot:function(){ if(this.destroyed==false){ var index=this.level; // 獲得炮彈 var shellCount=this.types[index].shellCount; var shells=enemyShellMng.fetch(shellCount); // 用來控制炮彈發射位置,從左翼尖到右翼尖均勻排列 var xLeft=this.x-this.types[index].width/2; var offset=this.types[index].width/(shellCount+1); for(var i=0;i<shellCount;i++){ var s=shells[i]; s.x=xLeft+(i+1)*offset; s.y=this.y; s.speedY=this.types[index].shellSpeed; s.speedX=this.getRndBetween(-4,4); } } }, } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<敵方飛機類定義結束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>敵方飛機管理類定義開始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> EnemyPlaneMng=function(x,y){ Point.apply(this,arguments); this.planes=new Array(); this.planes.push(new EnemyPlane(ctx.canvas.width/2,20,0)); this.planes.push(new EnemyPlane(ctx.canvas.width/2-80,20,1)); this.planes.push(new EnemyPlane(ctx.canvas.width/2+80,20,3)); } EnemyPlaneMng.prototype={ planes:[], // 取得敵機數量 getCount:function(){ return this.planes.length; }, // 獲得lowerLimit到upperlimit(包括端點值)的隨機整數 getRndBetween:function (lowerLimit,upperLimit){ return Math.floor(Math.random()*(upperLimit-lowerLimit+1))+lowerLimit; }, // 從新裝載飛機 reload:function(){ var count=this.getAlivePlaneCount(); if(count==0){ // 取得數組長度 var n=this.planes.length-1; // 一回搞三架敵機出來,控制數量就是控制數組個數 var arr2=[this.getRndBetween(0,n),this.getRndBetween(0,n),this.getRndBetween(0,n)]; for(var i=0;i<arr2.length;i++){ var index=arr2[i]; var plane=this.planes[index]; plane.visible=true; plane.destroyed=false; var arr=[ctx.canvas.width/4,ctx.canvas.width*3/4]; plane.x=arr[this.getRndBetween(0,1)]; plane.y=this.getRndBetween(10,40); plane.level=this.getRndBetween(0,plane.types.length-1); } } }, // 獲得屏幕上飛機活着的飛機數目 getAlivePlaneCount:function(){ var retval=0; for(var i=0;i<this.planes.length;i++){ var plane=this.planes[i]; if(plane.visible==true && plane.destroyed==false){ retval++; } } return retval; }, paint:function(ctx){ for(var i=0;i<this.planes.length;i++){ var plane=this.planes[i]; plane.paint(ctx); } }, move:function(){ for(var i=0;i<this.planes.length;i++){ var plane=this.planes[i]; plane.move(); } }, isShooted:function(shell){ if(shell.destroyed==true){ return false; } for(var i=0;i<this.planes.length;i++){ var plane=this.planes[i]; if(plane.visible==true && plane.destroyed==false){ if(plane.isWithinLimits(shell.x,shell.y)){ plane.destroyed=true; shell.destroyed=true; // 擊落數加一 shootDownCnt++; // 製造爆炸 explosionMng.fire(shell.x,shell.y); // 製造獎勵 var b=bonusMng.fetchOne(); b.x=shell.x; b.y=shell.y; // 敵機重載 enemyPlaneMng.reload(); return true; } } } return false; }, // 看主角飛機是否與敵機相撞 isCrashed:function(rolePlane){ if(rolePlane.destroyed==true){ return false; } // 獲得本機四角座標 var p1=rolePlane.getLeftUpPoint(); var p2=rolePlane.getRightUpPoint(); var p3=rolePlane.getLeftDownPoint(); var p4=rolePlane.getRightDownPoint(); for(var i=0;i<this.planes.length;i++){ var ep=this.planes[i]; if(ep.visible==true && ep.destroyed==false){ if(ep.isWithinLimits(p1.x,p1.y) || ep.isWithinLimits(p2.x,p2.y) || ep.isWithinLimits(p3.x,p3.y) || ep.isWithinLimits(p4.x,p4.y)){ ep.destroyed=true;// 敵機撞毀 explosionMng.fire(ep.x,ep.y);// 製造爆炸 rolePlane.destroyed=true;// 本機撞毀 explosionMng.fire(rolePlane.x,rolePlane.y);// 製造爆炸 // 遊戲結束 gameover=true; restartBtn.style.visibility="visible"; return true; } } } return false; }, } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<敵方飛機管理類定義結束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>爆炸類定義開始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Explosion=function(x,y){ Point.apply(this,arguments); this.types=[ {width:105,height:100,file:'explosion0.png'}, {width:105,height:100,file:'explosion1.png'}, {width:105,height:100,file:'explosion2.png'}, {width:105,height:100,file:'explosion3.png'}, {width:105,height:100,file:'explosion4.png'}, {width:105,height:100,file:'explosion5.png'}, ]; } Explosion.prototype={ types:[], // 爆炸圖片 destroyTime:0, // 被摧毀時間 paint:function(ctx){ var index=Math.floor(this.destroyTime); if(index<this.types.length){ this.destroyTime+=0.05; var img=new Image(); img.src=this.types[index].file; ctx.drawImage(img,this.x-this.types[index].width/2,this.y-this.types[index].height/2); } }, // 看這個爆炸對象是否使用過 isUsed:function(){ return this.destroyTime>=this.types.length; }, } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<爆炸類定義結束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>爆炸類2定義開始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Explosion2=function(x,y){ Point.apply(this,arguments); this.types=[ {width:98,height:100,file:'b0.png'}, {width:98,height:100,file:'b1.png'}, {width:98,height:100,file:'b2.png'}, {width:98,height:100,file:'b3.png'}, {width:98,height:100,file:'b4.png'}, {width:98,height:100,file:'b5.png'}, {width:98,height:100,file:'b6.png'}, {width:98,height:100,file:'b7.png'}, ]; } Explosion2.prototype={ types:[], // 爆炸圖片 destroyTime:0, // 被摧毀時間 paint:function(ctx){ var index=Math.floor(this.destroyTime); if(index<this.types.length){ this.destroyTime+=0.05; var img=new Image(); img.src=this.types[index].file; ctx.drawImage(img,this.x-this.types[index].width/2,this.y-this.types[index].height/2); } }, // 看這個爆炸對象是否使用過 isUsed:function(){ return this.destroyTime>=this.types.length; }, } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<爆炸類2定義結束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>爆炸管理類定義開始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ExplosionMng=function(x,y){ this.exps=new Array(); } ExplosionMng.prototype={ exps:[], // 爆炸數組 // 取得爆炸數量 getCount:function(){ return this.exps.length; }, paint:function(ctx){ for(var i=0;i<this.exps.length;i++){ var e=this.exps[i]; e.paint(ctx); } }, // 獲得隨機數 getRndBetween:function (lowerLimit,upperLimit){ return Math.floor(Math.random()*(upperLimit-lowerLimit+1))+lowerLimit; }, // 製做一次爆炸,使用這種方式,可最大程度利用現有對象,而不是建立一堆用不上的變量 fire:function(x,y){ var exp=null; for(var i=0;i<this.exps.length;i++){ var e=this.exps[i]; console.log('e.isUsed=',e.isUsed()) if(e.isUsed()==true){ exp=e; exp.x=x; exp.y=y; exp.destroyTime=0; console.log('使用一個原有對象',exp) break; } } if(exp==null){ var seed=this.getRndBetween(0,1); if(seed==0){ exp=new Explosion(x,y); }else{ exp=new Explosion2(x,y); } this.exps.push(exp); console.log('建立一個新對象',exp) } console.log('爆炸對象個數=',this.exps.length) }, } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<爆炸管理類定義結束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>敵方炮彈類定義開始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 19.3.16 EnemyShell=function(x,y){ Point.apply(this,arguments); this.types=[ {width:11,height:11,file:'shell0.png'}, {width:11,height:11,file:'shell1.png'}, {width:11,height:11,file:'shell2.png'}, {width:11,height:11,file:'shell3.png'}, {width:11,height:11,file:'shell4.png'}, {width:11,height:11,file:'shell5.png'}, {width:11,height:11,file:'shell6.png'}, {width:18,height:18,file:'shell7.png'}, ]; } EnemyShell.prototype={ types:[], // 炮彈型號 destroyed:false,// 是否被敵機撞毀 visible:true, // 是否在CTX顯示範圍內 level:0, // 等級,用以決定炮彈型號 speedY:4, // 炮彈縱向速度(敵方炮彈斜向下飛行) speedX:4, // 炮彈橫向速度(敵方炮彈斜向下飛行)增長此舉是爲了防止居中火力全開模式 paint:function(ctx){ if(this.visible==false){ return; } if(this.destroyed==false){ // 沒被擊毀顯示炮彈型號 var img=new Image(); var index=this.level; img.src=this.types[index].file; ctx.drawImage(img,this.x-this.types[index].width/2,this.y-this.types[index].height/2); } }, move:function(){ // 設置越界不可見 if(this.x<0){ this.visible=false; } if(this.x>ctx.canvas.width){ this.visible=false; } if(this.y<0){ this.visible=false; } if(this.y>ctx.canvas.height){ this.visible=false; } if(this.visible==true && this.destroyed==false){ this.y+=this.speedY; this.x+=this.speedX; } }, } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<敵方炮彈類定義結束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>敵方炮彈管理者類定義開始>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>19.3.16 EnemyShellMng=function(){ this.shells=new Array(); } EnemyShellMng.prototype={ shells:[], // 敵方炮彈對象數組 // 取得敵方炮彈數量 getCount:function(){ return this.shells.length; }, paint:function(ctx){ for(var i=0;i<this.shells.length;i++){ var s=this.shells[i]; s.paint(ctx); } }, // 取出子彈,count爲個數 fetch:function(count){ var retval=new Array(); //console.log("原有敵方炮彈數量=",this.shells.length); // 先取原有的炮彈 for(var i=0;i<this.shells.length;i++){ var s=this.shells[i]; if(s.visible==false || s.destroyed==true){ s.destroyed=false; s.visible=true; if(retval.length<count){ retval.push(s); } } } // 不足再建立新的炮彈 while(retval.length<count){ var s=new EnemyShell(0,0); this.shells.push(s); retval.push(s); } //console.log("現有敵方炮彈數量=",this.shells.length); return retval; }, // 移動炮彈 move:function(){ for(var i=0;i<this.shells.length;i++){ var s=this.shells[i]; s.move(); } }, // 判斷炮彈是否撞到本機 probeCrashedMyPlane:function(){ for(var i=0;i<this.shells.length;i++){ var s=this.shells[i]; if(s.visible==true && s.destroyed==false && myPlane.destroyed==false){ if(myPlane.isShooted(s)==true){ s.destroyed=true; return; } } } }, } //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<敵方炮彈管理者類定義結束<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< //---------------------------------------------------獎勵類定義開始------------------------------------------------------------------->> Bonus=function(x,y){ this.x=x; this.y=y; this.files=['bonus0.png','bonus1.png']; } Bonus.prototype={ x:0, // 橫座標 y:0, // 縱座標 files:[], // 圖片 used:false, // 是否使用過 index:0, // 做爲兩張圖片交替顯示的取捨 paint:function(ctx){ if(this.used==false){ var img=new Image(); img.src=this.files[0]; this.index++; if(this.index>199){ this.index=0; this.used=true; } if(this.index<100){ img.src=this.files[1]; }else{ img.src=this.files[0]; } ctx.drawImage(img,this.x,this.y); } }, // 獎勵得向下落,要不很差接 move:function(){ if(this.y<ctx.canvas.height){ this.y+=3; }else{ this.used=true; } }, } //---------------------------------------------------獎勵類定義結束-------------------------------------------------------------------<< //---------------------------------------------------獎勵管理類定義開始------------------------------------------------------------------->> BonusMng=function(x,y){ this.bonusArr=new Array(); } BonusMng.prototype={ bonusArr:[], // 獎勵數組 // 取得獎勵數量 getCount:function(){ return this.bonusArr.length; }, move:function(){ for(var i=0;i<this.bonusArr.length;i++){ var b=this.bonusArr[i]; b.move(); } }, paint:function(ctx){ for(var i=0;i<this.bonusArr.length;i++){ var b=this.bonusArr[i]; b.paint(ctx); } }, // 取出一個Bonus fetchOne:function(){ // 先取原有的 for(var i=0;i<this.bonusArr.length;i++){ var b=this.bonusArr[i]; if(b.used==true){ b.used=false; return b; } } // 原來的沒有就新建一個 var b=new Bonus(0,0); this.bonusArr.push(b); return b; }, // 看獎勵是否與本機相撞 isCrashed:function(rolePlane){ if(rolePlane.destroyed==true){ return false; } // 獲得本機四角座標 for(var i=0;i<this.bonusArr.length;i++){ var b=this.bonusArr[i]; if(b.used==false && rolePlane.isWithinLimits(b.x,b.y)==true){ //console.log("升級"); rolePlane.upgrade(1); b.used=true; return true; } } return false; }, } //---------------------------------------------------獎勵管理類定義結束-------------------------------------------------------------------<< //--> </script>
2019年3月17日13點31分初稿 app
2019年3月18日14點11分修訂
2019年3月18日18點46分再修訂