[canvas]空戰遊戲1.18

空戰遊戲到今天能夠玩了,玩法仍是方向鍵(或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分再修訂

相關文章
相關標籤/搜索