微信飛機(jī)大戰(zhàn)

您好疲憋,本篇文章主要描述如何用面向?qū)ο缶幊趟枷敕聦?xiě)微信飛機(jī)大戰(zhàn)。
采用JS腳本來(lái)控制HTML5新標(biāo)簽canvas畫(huà)布,因?yàn)橹徊僮饕粋€(gè)DOM茎用,畫(huà)面會(huì)更加流暢,很多基于網(wǎng)頁(yè)開(kāi)發(fā)的小游戲都是用canvas畫(huà)布實(shí)現(xiàn)的睬罗。

0 準(zhǔn)備工作

首先轨功,我們要準(zhǔn)備好各種圖片,如飛機(jī)的雪碧(精靈)圖容达、子彈圖古涧、背景圖。

圖片素材

然后花盐,對(duì)整個(gè)游戲流程進(jìn)行構(gòu)思:

①游戲開(kāi)始前的圖片預(yù)加載
②讓背景圖片動(dòng)起來(lái)
③繪制英雄機(jī)羡滑,英雄機(jī)跟隨鼠標(biāo)移動(dòng)
④英雄機(jī)能發(fā)射子彈
⑤繪制3種敵機(jī),賦予它們不同的血量與下落速度
⑥子彈與敵機(jī)的碰撞檢測(cè)
⑦英雄機(jī)與敵機(jī)的碰撞檢測(cè)
⑧顯示分?jǐn)?shù)算芯,與結(jié)束游戲

1 HTML+CSS

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>微信飛機(jī)大戰(zhàn)</title>
    </head>
    <body>
        //
        <canvas id="canvas" width="320" height="528" style="border: 2px solid gray"></canvas>
    //引入腳本文件
    <script src=""airplane></script>
    </body>
</html>

2 JS

2.1 所有圖片預(yù)加載

            //獲取畫(huà)布與畫(huà)布上下文
            var canvas = document.getElementById('canvas');
            var ctx = canvas.getContext('2d');
            //分?jǐn)?shù)變量  游戲是否開(kāi)始變量 英雄機(jī)對(duì)象
            var score = 0;
            var gameStart = false;
            var theHero = null;

            //  創(chuàng)建對(duì)象來(lái)接收對(duì)應(yīng)的圖片對(duì)象
            var bgImg = '';
            var heroImg = '';
            var enemy1Img = '';
            var enemy2Img = '';
            var enemy3Img = '';
            var bullet1Img = '';

            //繪制進(jìn)度條構(gòu)造函數(shù)
            function Loading(){
                this.width = 220;
                this.height = 12;
                this.x = 50;
                this.y = 258;
            }
    
            //創(chuàng)建進(jìn)度條原型
            Loading.prototype = {
                //畫(huà)進(jìn)度條的外框
                drawStroke : function(){
                    ctx.beginPath();
                    ctx.strokeRect(this.x,this.y,this.width,this.height);
                    ctx.stroke();
                    ctx.closePath();
                },
                //畫(huà)已加載的填充圖
                drawFill : function(){
                    ctx.beginPath();
                    ctx.fillStyle = 'orangered';
                    ctx.fillRect(this.x,this.y,this.width,this.height);
                    ctx.fill();
                    ctx.closePath();
                }
            }
            
            // 預(yù)加載函數(shù)
            function load(){
                var loadS = new Loading();
                var loadSW = loadS.width;
                loadS.drawStroke();
                var loadF = new Loading();
                loadF.width = 0;
                loadF.drawFill();
                //用一個(gè)數(shù)組把圖片地址保存起來(lái)
                var arr = ['img/bg.png','img/hero.png',
                'img/enemy1.png','img/enemy2.png',
                'img/enemy3.png','img/bullet1.png'
                ];
                
                var index = 0;
                //利用循環(huán)柒昏,創(chuàng)建圖片對(duì)象
                for(var i = 0;i<arr.length;++i){
                    var img = new Image();
                    img.src = arr[i];
                    //利用正則表達(dá)式判斷圖片對(duì)象是否為背景圖,如果是就用一個(gè)變量保存起來(lái)也祠,其他圖片也是類似
                    if(/bg/.test(arr[i])){
                        bgImg = img;
                    }
                    else if(/hero/.test(arr[i])){
                        heroImg = img;
                    }
                    else if(/enemy1/.test(arr[i])){
                        enemy1Img = img;
                    }
                    else if(/enemy2/.test(arr[i])){
                        enemy2Img = img;
                    }
                    else if(/enemy3/.test(arr[i])){
                        enemy3Img = img;
                    }
                    else if(/bullet1/.test(arr[i])){
                        bullet1Img = img;
                    }
                    //當(dāng)本次循環(huán)的圖片加載完畢昙楚,運(yùn)行該函數(shù)
                    img.onload = function(){
                        //加載增量
                        index++;
                        //進(jìn)度條的長(zhǎng)度隨著圖片加載個(gè)數(shù)的增加而變長(zhǎng)
                        loadF.width = (index/arr.length)*loadSW;
                        ctx.clearRect(0,0,canvas.width,canvas.height);
                        loadF.drawFill();
                        
                        if(index >=arr.length){
                            //當(dāng)圖片全部加載完畢,繪畫(huà)初始界面
                            theHero = new Hero(heroImg,6);
                            
                            ctx.fillStyle = 'black';
                            ctx.font = '30px Consoles';
                            ctx.fillText('飛機(jī)大戰(zhàn)',canvas.width/3.5,canvas.height/3);
                            ctx.fillStyle = 'orange';
                            ctx.fillRect(canvas.width/3.5,canvas.height-200,130,50);
                            ctx.fillStyle = 'black';
                            ctx.fillText('開(kāi)始游戲',canvas.width/3.3,canvas.height-163);
                        }
                    }
                }
            }
            load();
加載完成效果圖

2.2 為開(kāi)始按鈕添加點(diǎn)擊事件

因?yàn)樵诋?huà)布內(nèi)诈嘿,所以只能根據(jù)位置判斷是否點(diǎn)擊到按鈕

canvas.onmousedown = function(e){
                var e = e||window.event;
                var x = e.clientX - canvas.offsetLeft;
                var y = e.clientY - canvas.offsetTop;
                // 開(kāi)始游戲的框大小內(nèi) canvas.width/3.5,canvas.height-200,130,50
                if(x>=canvas.width/3.5&&(x<=canvas.width/3.5+130)&&y>=canvas.height-200&&y<canvas.height-200+50){
                    gameStart = true;
                    //動(dòng)畫(huà)主函數(shù)
                    move();
                    //點(diǎn)擊完就把畫(huà)布點(diǎn)擊事件置為空
                    canvas.onmousedown = null;
                }
            }

2.3 背景移動(dòng)函數(shù)

利用ctx.drawImage()繪制兩張背景圖堪旧,同時(shí)向下移動(dòng)削葱,當(dāng)?shù)谝粡垐D片移動(dòng)到底部后就改變圖片位置。兩張圖一直輪播淳梦。

            // 設(shè)置背景移動(dòng)量與背景位置變量           
            var bgChange = 1;
            var bgPos = 0;
            // 背景移動(dòng)函數(shù)
            function bgMove(){
                bgPos += bgChange;
                if(bgPos==canvas.height){
                    bgPos = 0;
                    ctx.drawImage(bgImg,0,0);
                }
                ctx.drawImage(bgImg,0,-canvas.height+bgPos);
                ctx.drawImage(bgImg,0,bgPos);               
            }

2.4 各種對(duì)象的構(gòu)造函數(shù)

2.4.1 英雄機(jī)構(gòu)造函數(shù)

            //傳入對(duì)象為英雄機(jī)圖片
            function Hero(obj,health){
                this.obj = obj;     
                this.width = obj.width/health;
                this.height = obj.height;
                //英雄機(jī)血量
                this.health = health;
                this.boom = 0;
                //英雄機(jī)初始位置
                this.x = canvas.width/2 -this.width/2;
                this.y = canvas.height - this.height;
            }
            Hero.prototype.draw = function(){
                ctx.drawImage(this.obj,this.width*this.boom,0,this.width,this.height,this.x,this.y,this.width,this.height);
            }

2.4.2 子彈構(gòu)造函數(shù)

            //創(chuàng)建一個(gè)數(shù)組存放子彈對(duì)象
            var arrBullet = [];
            function Bullet(x,y,speedY){
                //子彈初始位置
                this.x = theHero.x+31;
                this.y = theHero.y-15;
                //子彈向上運(yùn)動(dòng)速度
                this.speedY = speedY || -20;
                this.width = bullet1Img.width;
                this.height = bullet1Img.height
            }
            Bullet.prototype = {
                draw : function(){
                    ctx.drawImage(bullet1Img,this.x,this.y);
                },
                move : function(){
                    this.draw();
                    this.y += this.speedY;
                },
                //當(dāng)子彈到達(dá)頂部析砸,返回布爾值true
                clear : function(){
                    if(this.y<=0){
                        return true;
                    }
                    else{
                        return false;
                    }
                }
            }

2.4.3 敵機(jī)構(gòu)造函數(shù)

            //創(chuàng)建一個(gè)數(shù)組用來(lái)存放敵機(jī)對(duì)象
            var arrEn1 = [];
            //敵機(jī)構(gòu)造函數(shù)
            function Enemy(obj,speedY,health,type){
                //傳入的對(duì)象為 3種不同的敵機(jī)圖片
                this.obj = obj;
                this.width = obj.width/health;
                this.height = obj.height;
                //敵機(jī)出現(xiàn)位置設(shè)定為隨機(jī)
                this.x = rnd(canvas.width-this.width,0);
                this.y = rnd(-canvas.height,-obj.height);
                this.speedY = speedY || 1;
                //敵機(jī)血量
                this.health = health;
                //敵機(jī)是否被打中
                this.boom = 0;
                //敵機(jī)類型
                this.type = type;
            }
            Enemy.prototype = {
                //繪畫(huà)敵機(jī)
                draw : function(){
                    ctx.drawImage(this.obj,this.width*this.boom,0,this.width,this.height,this.x,this.y,this.width,this.height);
                },
                //敵機(jī)移動(dòng)
                move : function(){
                    this.draw();
                    this.y += this.speedY;
                },
                //如果敵機(jī)超越畫(huà)布底部 把清除敵機(jī)的布爾值置為true
                clear : function(){
                    if(this.y>=canvas.height){
                        return true;
                    }
                    else{
                        return false;
                    }
                }
            }
            //隨機(jī)函數(shù)
            function rnd(max,min){
                return Math.random()*(max-min+1)+min;
            }

2.5 英雄機(jī)跟隨鼠標(biāo)

            canvas.onmousemove = function(e){
                var e = e||window.event;
                var ex = e.clientX - canvas.offsetLeft - 33;
                var ey = e.clientY - canvas.offsetTop - 41;

                theHero.x = ex;
                theHero.y = ey; 
            }

3 主動(dòng)畫(huà)函數(shù)

            //創(chuàng)造一個(gè)變量用來(lái)決定敵機(jī)出現(xiàn)概率
            var num = 0;
            function move(){
                num++;
                
                if(num==1000){
                    num = 0;
                }
                //每次開(kāi)始繪畫(huà)時(shí),先清除畫(huà)布
                ctx.clearRect(0,0,canvas.width,canvas.height);
                //調(diào)用背景移動(dòng)函數(shù)
                bgMove();
                //繪畫(huà)英雄機(jī)
                theHero.draw();
                
                // 創(chuàng)造子彈對(duì)象爆袍,并把該對(duì)象插入到子彈數(shù)組
                if(num%5==0){
                    var bullet = new Bullet();
                    arrBullet.push(bullet);
                }
                // 創(chuàng)造小敵機(jī)對(duì)象(出現(xiàn)幾率最大)首繁,并把該對(duì)象插入到敵機(jī)數(shù)組
                //(出現(xiàn)幾率最大,速度最快2.5陨囊,血量最低5)
                if(num%45==0){
                    var en1 = new Enemy(enemy1Img,2.5,5,'en1');
                    arrEn1.push(en1);
                }
                // 創(chuàng)造中敵機(jī)對(duì)象弦疮,并把該對(duì)象插入到敵機(jī)數(shù)組
                //(出現(xiàn)幾率最小,速度一般2蜘醋,血量一般7)
                if(num%160==0){
                    var en3 = new Enemy(enemy3Img,2,7,'en2');
                    arrEn1.push(en3);
                }
                // 創(chuàng)造大敵機(jī)對(duì)象胁塞,并把該對(duì)象插入到敵機(jī)數(shù)組
                //(出現(xiàn)幾率最小,速度最慢1.5压语,血量最高10)
                if(num%360==0){
                    var en2 = new Enemy(enemy2Img,1.5,10,'en3');
                    arrEn1.push(en2);
                }
                
                
                // 遍歷子彈數(shù)組畫(huà)子彈
                for(var i =0;i<arrBullet.length;++i){
                    //如果子彈到達(dá)底部啸罢,從數(shù)組中清除該對(duì)象
                    //清除后減少數(shù)組長(zhǎng)度并跳出本次循環(huán)
                    if(arrBullet[i].clear()){
                        arrBullet.splice(i,1);
                        i--;
                        continue;
                    }
                    //如沒(méi)有被清除則繪畫(huà)該子彈對(duì)象
                    arrBullet[i].move();
                }
                // 遍歷敵機(jī)數(shù)組畫(huà)敵機(jī)
                for(var j = 0;j<arrEn1.length;++j){
                    arrEn1[j].y += arrEn1[j].speedY;
                    //如果玩家得分超過(guò)15000就加快敵機(jī)速度
                    if(score>=15000){
                        arrEn1[j].y += arrEn1[j].speedY*1.3;
                    }
                    //如果敵機(jī)到達(dá)畫(huà)布底部,從數(shù)組中清除該對(duì)象
                    if(arrEn1[j].clear()){
                        arrEn1.splice(j,1);
                        j--;
                        continue;
                    }
                    //若沒(méi)有被清除則繪畫(huà)該敵機(jī)對(duì)象
                    arrEn1[j].draw();
                }
                
                // 英雄機(jī)與敵機(jī)碰撞檢測(cè) (矩形碰撞)
                for(var i = 0;i<arrEn1.length;++i){
                    //獲得英雄機(jī)上下左右位置
                    var heroL = theHero.x;
                    var heroT = theHero.y;
                    var heroR = theHero.x + theHero.width;
                    var heroB = theHero.y + theHero.health;
                    //獲取當(dāng)前循環(huán)中敵機(jī)上下左右位置
                    var enL = arrEn1[i].x;
                    var enT = arrEn1[i].y;
                    var enR = arrEn1[i].x + arrEn1[i].width;
                    var enB = arrEn1[i].y + arrEn1[i].height;
                    //矩形碰撞條件為:不碰撞的4種情況取反
                    if(!(enR<heroL||enB<heroT||enL>heroR||enT>heroB)){
                        //判斷敵機(jī)是否爆炸完成胎食,是則清除該敵機(jī)對(duì)象                      
                        if(arrEn1[i].boom==arrEn1[i].health-1){
                            arrEn1.splice(i,1);
                            i--;
                            continue;
                        }
                        //碰撞到時(shí)扰才,英雄機(jī)與敵機(jī)爆炸增量都自增1
                        else{
                            theHero.boom++;
                            arrEn1[i].boom++;
                        }
                        
                    }
                    //如果英雄機(jī)爆炸量與血量相同,則判斷游戲結(jié)束
                    if(theHero.boom>theHero.health-1){
                            theHero.boom = theHero.health;
                            theHero.draw();
                            //把游戲進(jìn)行布爾值置為false
                            gameStart = false;
                            //繪畫(huà)所得分?jǐn)?shù)
                            ctx.fillStyle = 'orangered';
                            ctx.font = '20px Consoles';
                            ctx.fillText('游戲結(jié)束厕怜,您獲得的分?jǐn)?shù):'+score,10,canvas.height/2);
                            break;
                        }
                }
                //繪畫(huà)英雄機(jī)血量條
                ctx.fillRect(10,canvas.height-20,350*((theHero.health-1-theHero.boom)/theHero.health),10);

                // 判斷子彈擊中敵機(jī)
                for(var i = 0;i<arrBullet.length;++i){
                    for(var j=0;j<arrEn1.length;++j){
                        //兩個(gè)循環(huán)分別遍歷子彈數(shù)組與敵機(jī)數(shù)組
                        //獲得每個(gè)子彈對(duì)象的位置與每個(gè)敵機(jī)的位置
                        var btL = arrBullet[i].x;
                        var btR = arrBullet[i].x + arrBullet[i].width;
                        var btT = arrBullet[i].y;
                        var btB = arrBullet[i].y + arrBullet[i].height;
                        
                        var enL = arrEn1[j].x;
                        var enR = arrEn1[j].x + arrEn1[j].width;
                        var enT = arrEn1[j].y;
                        var enB = arrEn1[j].y + arrEn1[j].height;
                        //對(duì)它們逐一進(jìn)行判斷是否碰撞
                        if(!(btL>enR||btR<enL||btT>enB||btB<enT)){
                            //碰撞到就在子彈數(shù)組中刪除該子彈對(duì)象
                            arrBullet.splice(i,1);
                            i--;
                            //打中一下衩匣,爆炸增量加1
                            arrEn1[j].boom++;
                            //爆炸增量等于血量時(shí),清除該敵機(jī)對(duì)象
                            if(arrEn1[j].boom==arrEn1[j].health-1){
                                //擊中對(duì)應(yīng)敵機(jī)加對(duì)應(yīng)分?jǐn)?shù)
                                if(arrEn1[j].type=='en1'){
                                    score += 300;
                                }
                                else if(arrEn1[j].type=='en2'){
                                    score += 500;
                                }
                                else if(arrEn1[j].type=='en3'){
                                    score += 1000;
                                }
                                //刪除對(duì)應(yīng)敵機(jī)對(duì)象
                                arrEn1.splice(j,1);
                                j--;
                                break;
                            }
                            break;
                        }
                    }
                }
                //在左上角繪畫(huà)分?jǐn)?shù)
                ctx.fillStyle = 'black';
                ctx.font = '20px Consoles';
                ctx.fillText('分?jǐn)?shù):'+score,10,30);
                ctx.fillStyle = 'orange';
                
                //如果游戲進(jìn)行布爾值為true酣倾,
                //則利用window.requestAnimationFrame重復(fù)調(diào)用move主動(dòng)畫(huà)函數(shù)
                //進(jìn)行不斷繪畫(huà)
                if(gameStart == true){
                window.requestAnimationFrame(move);
                }
            }

4 最后效果圖

游戲進(jìn)行時(shí)
游戲結(jié)束時(shí)

5 總結(jié)

原來(lái)做一個(gè)游戲需要做好很多工作舵揭,這是我以前作為一個(gè)玩家所無(wú)法體會(huì)到的,在此感謝給予過(guò)我游戲樂(lè)趣的前輩們躁锡。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末午绳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子映之,更是在濱河造成了極大的恐慌拦焚,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件杠输,死亡現(xiàn)場(chǎng)離奇詭異赎败,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蠢甲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)僵刮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事搞糕∮碌酰” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵窍仰,是天一觀的道長(zhǎng)汉规。 經(jīng)常有香客問(wèn)我,道長(zhǎng)驹吮,這世上最難降的妖魔是什么针史? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮碟狞,結(jié)果婚禮上啄枕,老公的妹妹穿的比我還像新娘。我一直安慰自己族沃,他們只是感情好射亏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著竭业,像睡著了一般。 火紅的嫁衣襯著肌膚如雪及舍。 梳的紋絲不亂的頭發(fā)上未辆,一...
    開(kāi)封第一講書(shū)人閱讀 51,231評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音锯玛,去河邊找鬼咐柜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛攘残,可吹牛的內(nèi)容都是我干的拙友。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼歼郭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼遗契!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起病曾,我...
    開(kāi)封第一講書(shū)人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤牍蜂,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后泰涂,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體鲫竞,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年逼蒙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了从绘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖僵井,靈堂內(nèi)的尸體忽然破棺而出陕截,到底是詐尸還是另有隱情,我是刑警寧澤驹沿,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布艘策,位于F島的核電站,受9級(jí)特大地震影響渊季,放射性物質(zhì)發(fā)生泄漏朋蔫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一却汉、第九天 我趴在偏房一處隱蔽的房頂上張望驯妄。 院中可真熱鬧,春花似錦合砂、人聲如沸青扔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)微猖。三九已至,卻和暖如春缘屹,著一層夾襖步出監(jiān)牢的瞬間凛剥,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工轻姿, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留犁珠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓互亮,卻偏偏與公主長(zhǎng)得像犁享,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子豹休,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容