1. 程式人生 > >用js寫飛機大戰的小遊戲

用js寫飛機大戰的小遊戲

飛機大戰思路整理

飛機大戰分為5個階段:
1.遊戲開始階段
1.0定義整個遊戲的全域性變數,並進行初始化
1.1.0建立遊戲背景圖片
1.1.1初始化背景圖片的資料
1.1.2建立遊戲開始階段的建構函式
*繪製方法
*運動方法
1.1.3建立遊戲開始的物件
1.1.4繪製遊戲名
2.遊戲載入階段
2.0繪製遊戲載入
2.1.0建立遊戲載入時的圖片,並放在一個數組中
2.1.1初始化遊戲載入時的資料
2.1.2建立遊戲載入時的建構函式
*繪製方法
*運動方法
2.1.3建立遊戲載入時的物件
2.1.4增加滑鼠點選事件(點選第一階段會跳轉到第二階段)
3.遊戲執行階段
3.1 繪製我方飛機
3.1.0建立我方飛機的圖片,並把它們放進陣列中;
3.1.1初始化我方飛機的資料;
3.1.2建立我方飛機的建構函式;
*繪製方法
*運動方法
*碰撞方法(在3.3.8中呼叫)
*射擊方法(我方飛機射擊時,要呼叫子彈產生的物件例項)
3.1.3建立我方飛機的物件
3.1.4增加滑鼠移動事件(當滑鼠移動的時候我方飛機要跟著移動)
3.2 繪製子彈
3.2.0建立子彈的圖片
3.2.1初始化子彈的資料
3.2.2建立子彈的建構函式
*繪製方法
*運動方法
*碰撞方法(在3.3.8中呼叫)
3.2.3定義一個子彈的陣列。將新的子彈的物件例項放進陣列中(方便後面遍歷、增加、刪除)
3.2.4建立繪製子彈的函式:遍歷陣列呼叫子彈物件的建構函式中的繪製子彈的方法
3.2.5建立子彈移動函式:遍歷陣列呼叫子彈物件的建構函式中子彈運動的方法
3.2.6建立刪除子彈的函式:
刪除子彈滿足的條件:1.碰撞的時候刪除子彈
2.子彈的縱座標小於負的子彈的高度的時候刪除子彈
3.3 繪製敵方飛機
3.3.0建立敵方飛機的圖片(分別用3個數組存放:區別小飛機、中飛機、大飛機)
3.3.1分別初始化三個型別飛機的資料
3.3.2建立敵方飛機的建構函式
*繪製方法
*運動方法
*敵方飛機碰撞的方法(在3.3.8中呼叫)
*檢測敵方飛機是否碰撞的方法(有可能是我方飛機碰撞、也有可能事子彈碰撞)
3.3.3定義一個存放敵方飛機的陣列(方便後面函式遍歷、增加子彈、刪除、改變)
3.3.4建立一個函式,往陣列中新增資料(小飛機、中飛機、大飛機)————較難
它們的產生是隨機的,將new出來的飛機物件例項新增進入陣列中。
3.3.5建立函式,遍歷畫出敵方飛機(呼叫陣列中物件的方法)
3.3.6建立函式,遍歷出敵方飛機的運動(呼叫陣列物件中的方法)
3.3.7建立函式,刪除敵方飛機
刪除敵方飛機滿足的條件:
1.敵方飛機的高度大於整個畫布的高度時
2.敵方飛機爆炸完成時
3.3.8建立函式,敵方飛機碰撞以後的函式(迴圈遍歷敵方飛機的陣列)
判斷:1.碰撞為我方飛機的時候
呼叫陣列元素中的檢測是否碰撞的方法(傳入的實參就是我方飛機)
分別呼叫敵方飛機和我方飛機的撞擊以後的方法
2.碰撞為子彈的時候
呼叫陣列元素中的檢測是否碰撞的方法(傳入的引數就是子彈陣列的元素)
分別呼叫敵方飛機和子彈陣列元素中的撞擊以後的方法
3.4繪製分數值和生命值
4.遊戲暫停階段
用畫布的滑鼠移出事件暫停遊戲狀態
用畫布的滑鼠移入事件開始遊戲狀態
5.遊戲結束階段
繪製遊戲結束
寫一個定時器:呼叫所有的物件方法以及函式呼叫
通過判斷遊戲狀態呼叫所對應的物件方法和函式
注意:在後面的暫停階段和遊戲結束階段要把前面第三階段的我方飛機、子彈、敵方飛機都顯示出來


<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
            #canvas{
                display: block;
                margin: 100px auto;
                /*border: solid 1px #000;*/
            }
        </style
>
</head> <body> <canvas id="canvas" width="480" height="650"></canvas> <script> var canvas=document.getElementById("canvas"); var context=canvas.getContext("2d"); // 0.遊戲初始化 // 0.1將遊戲分為幾個階段 var START=0
;// 第一個階段 遊戲開始階段 var STARTING=1;//第二個階段 遊戲遊戲開始的載入階段 var RUNNING=2;// 第三個階段 遊戲執行階段 var PAUSE=3;// 第四個階段 遊戲暫停階段 var GAMEOVER=4;//第五個階段 遊戲結束階段 // 0.2定義一個自己的狀態,去和上面的狀態作比較 var state=START; // 0.3定義畫布的寬和高 var WIDTH=canvas.width; var HEIGHT=canvas.height; // 0.4定義遊戲的得分 var score=0; // 0.5定義我方飛機的生命值為3 var life=3; //初始化階段完成 //1.遊戲剛開始的階段 //1.1載入背景圖片 //1.1.1建立載入背景圖片的物件 var bg=new Image(); bg.src="img/background.png"; //1.1.2初始化背景圖片的資料 var BG={ imgs:bg, width:480, height:852 } //1.1.3建立背景圖片的建構函式(構造出一個物件) //這裡使用了建立物件的第二種方法:先使用函式來定義物件,然後建立新的物件例項 //建構函式的特點: // ①建構函式的首字母必須要大寫,用來區分與普通函式 // ②內部使用的this物件,來指向直接要生成的例項物件 // ③使用new來生成例項物件 function Bg(config){//這裡的config是個形參,而BG就是後面要傳進來的實參; this.imgs=config.imgs; this.width=config.width; this.height=config.height; //在這裡定義了兩張背景圖,是為了第一張背景圖移動完後第二張背景圖接著移動 this.x1=0; this.y1=0; this.x2=0; //第二張背景圖的初始高度放在背景高度(固定)的上面 this.y2=-this.height; //定義背景圖片繪製的方法 this.paint=function(){ //分別繪製了兩張背景圖 context.drawImage(this.imgs,this.x1,this.y1); context.drawImage(this.imgs,this.x2,this.y2); } //定義背景圖片運動的方法 this.step=function(){ //背景圖片位置向下移動一個,然後利用後面的定時器讓背景圖動起來 this.y1++; this.y2++; //判斷圖片高度的臨界點,只要圖片一和圖片二的高度等於設定的背景高度時,就讓他們的縱向位置變成背景高度的上面 if(this.y1 == this.height){ this.y1=-this.height; } if(this.y2 == this.height){ this.y2=-this.height; } } } // 1.1.4建立背景圖片的物件 var sky=new Bg(BG); // 1.2繪製遊戲名 var logo=new Image(); logo.src="img/start.png"; // 2第二階段遊戲載入的過程 // 2.1遊戲載入時的四個圖片 var loadings = []; loadings[0] = new Image(); loadings[0].src = "img/game_loading1.png"; loadings[1] = new Image(); loadings[1].src = "img/game_loading2.png"; loadings[2] = new Image(); loadings[2].src = "img/game_loading3.png"; loadings[3] = new Image(); loadings[3].src = "img/game_loading4.png"; // 2.2初始化圖片資料 var LOADING={ imges:loadings, length:loadings.length, width:186, height:38 } // 2.3寫建構函式 function Loading(config){ this.imges=config.imges; this.length=config.length; this.width=config.width; this.height=config.height; // 定義索引,判斷要顯示的圖片是哪個 this.startIndex=0; // 定義繪製方法 this.paint=function(){ context.drawImage(this.imges[this.startIndex],0,HEIGHT-this.height) } //定義遊戲載入時的圖片切換速度初始值 this.time=0; //定義遊戲載入時圖片動態效果(其實是圖片切換)的方法 this.step=function(){ this.time++; //設定4步切換一張圖 if(this.time % 4 == 0){ this.startIndex++; } if(this.startIndex == this.length){ state = RUNNING; } } } //建立遊戲載入(第二階段)的物件 var loading=new Loading(LOADING); //獲取滑鼠點選事件 canvas.onclick=function(){ // 滑鼠點選第一階段(遊戲開始)時,要切換到第二狀態(遊戲載入) if(state == START){ state = STARTING; } } // 3.1.0寫我方飛機 // 3.1.1載入我方飛機的圖片 var heros=[]; heros[0]=new Image(); heros[0].src="img/hero1.png"; heros[1]=new Image(); heros[1].src="img/hero2.png"; heros[2]=new Image(); heros[2].src="img/hero_blowup_n1.png"; heros[3]=new Image(); heros[3].src="img/hero_blowup_n2.png"; heros[4]=new Image(); heros[4].src="img/hero_blowup_n3.png"; heros[5]=new Image(); heros[5].src="img/hero_blowup_n4.png"; // 3.2初始化圖片資料 var HEROS={ imgs:heros, length:heros.length, width:99, height:124, frame:2 } // 3.1.3構造我方飛機的函式 function Hero(config){ this.imgs=config.imgs; this.length=config.length; this.width=config.width; this.height=config.height; this.frame=config.frame; // 定義索引值 this.startIndex=0; // 定義我方飛機的位置 this.x=WIDTH / 2 - this.width / 2; this.y=HEIGHT - this.height; // 定義飛機撞擊的標誌,表示飛機沒有被撞擊 this.down=false; // 定義飛機是否爆破完成,表示飛機還沒有完全爆炸 this.candel=false; // 定義繪製的方法 this.paint=function(){ context.drawImage(this.imgs[this.startIndex],this.x,this.y) } // 定義我方飛機運動的方法 this.step=function(){ // 我方飛機的運動狀態:(所以在此處要判斷狀態) // 1.正常狀態 // 2.爆破狀態 if(!this.down){//表示飛機正常狀態,此時只有兩張圖進行切換 if(this.startIndex == 0){ this.startIndex=1; } else{ this.startIndex=0; } } else{//爆炸狀態 this.startIndex++;//爆炸狀態的圖片持續增加 if(this.startIndex == this.length){//判斷圖片是最後一張的時候 life--;//我方飛機爆炸完後,生命值減一 //判斷生命值為0時,遊戲結束 if(life == 0){ state=GAMEOVER; //雖然遊戲結束了但是還停留在最後一張圖的狀態 this.startIndex=this.length-1; } // 想要開始新的生命時,直接給出建立的新的物件 else{ hero=new Hero(HEROS); } } } } // 定義我方飛機碰撞的方法 this.bang=function(){ this.down=true; } // 增加我方飛機射擊的方法 //定義射擊速度初始值為0 this.time=0; //我方飛機射擊的時候,就會出現子彈,所以要呼叫子彈的陣列中產生的子彈物件 this.shoot=function(){ this.time++; //每3步射擊一次 if(this.time % 3 == 0){ //這裡把子彈每次建立好的新的例項增加到陣列的末尾 bullets.push(new Bullet(BULLETS)) } } } //3.1.4建立我方飛機的物件例項 var hero=new Hero(HEROS); //3.1.5獲取滑鼠移動事件 canvas.onmousemove=function(event){ var event=event || window.event; if(state == RUNNING){//判斷當前遊戲狀態 //把獲取到的頁面中的滑鼠橫座標的值賦給飛機的橫座標(位置) hero.x = event.offsetX-hero.width/2; //把獲取到的頁面中的滑鼠縱座標的值賦給飛機的縱座標(位置) hero.y = event.offsetY-hero.height/2; } } // 3.2繪製子彈 // 3.2.0建立子彈的圖片 var bullet=new Image(); bullet.src="img/bullet1.png"; // 3.2.1初始化資料 var BULLETS={ imgs:bullet,//注意這裡的“ :” width:9, height:21 } // 3.2.2建立子彈的建構函式 function Bullet(config){//用形參config去接收 this.imgs=config.imgs; this.width=config.width; this.height=config.height; // 定義子彈的座標 //子彈的橫座標 this.x=hero.x + hero.width / 2-this.width / 2 ; //子彈縱座標 this.y=hero.y - this.height; // 定義繪製方法 this.paint=function(){ context.drawImage(this.imgs,this.x,this.y) }; // 定義運動方法 this.step=function (){ this.y-=10; }; // 定義子彈碰撞屬性 ,false表示沒有碰撞 this.candel=false; // 定義子彈碰撞方法,碰撞之後他的碰撞屬性為true this.bang=function(){ this.candel=true; } } // 3.2.3讓所有new的子彈物件放到一個數組中(方便後面遍歷,增加,刪除) var bullets=[]; // 3.2.4通過遍歷繪製子彈;最後在定時器中呼叫該函式即可 function bulletdPaint(){ for(var i=0;i<bullets.length;i++){ //呼叫陣列物件中的方法paint() bullets[i].paint(); } } // 3.2.5通過遍歷呼叫子彈的運動; function bulletdStep(){ for(var i=0;i<bullets.length;i++){ bullets[i].step(); } } // 3.2.6增加子彈的刪除函式 function bulletDel(){ //刪除子彈所滿足的條件: // 1.碰撞的時候刪除子彈 // 2.超出畫布的高度,其實就是負的子彈的高度 for(var i=0;i<bullets.length;i++){ if(bullets[i].candel || bullets[i].y < -bullets[i].height){ //呼叫陣列中的方法 bullets.splice(i,1) } } } // 3.3敵方飛機的繪製 // 3.3.0建立敵方飛機圖片 var enemy1=[];//小飛機 enemy1[0]=new Image(); enemy1[0].src="img/enemy1.png"; enemy1[1]=new Image(); enemy1[1].src='img/enemy1_down1.png'; enemy1[2]=new Image(); enemy1[2].src='img/enemy1_down2.png'; enemy1[3]=new Image(); enemy1[3].src='img/enemy1_down3.png'; enemy1[4]=new Image(); enemy1[4].src='img/enemy1_down4.png'; var enemy2=[];//中飛機 enemy2[0]=new Image(); enemy2[0].src="img/enemy2.png"; enemy2[1]=new Image(); enemy2[1].src="img/enemy2_down1.png"; enemy2[2]=new Image(); enemy2[2].src="img/enemy2_down2.png"; enemy2[3]=new Image(); enemy2[3].src="img/enemy2_down3.png"; enemy2[4]=new Image(); enemy2[4].src="img/enemy2_down4.png"; var enemy3 = []; //大飛機 enemy3[0] = new Image(); enemy3[0].src = "img/enemy3_n1.png" enemy3[1] = new Image(); enemy3[1].src = "img/enemy3_n2.png" enemy3[2] = new Image(); enemy3[2].src = "img/enemy3_down1.png" enemy3[3] = new Image(); enemy3[3].src = "img/enemy3_down2.png" enemy3[4] = new Image(); enemy3[4].src = "img/enemy3_down3.png" enemy3[5] = new Image(); enemy3[5].src = "img/enemy3_down4.png" enemy3[6] = new Image(); enemy3[6].src = "img/enemy3_down5.png" enemy3[7] = new Image(); enemy3[7].src = "img/enemy3_down6.png" // 3.3.2敵方飛機的初始化資料 var ENEMY1={ imgs:enemy1, length:enemy1.length, width:57, height:51, type:1, frame:1, life:1, score:1 } var ENEMY2={ imgs:enemy2, length:enemy2.length, width:69, height:95, type:2, frame:1, life:5, score:5 } var ENEMY3={ imgs:enemy3, length:enemy3.length, width:165, height:261, type:3, frame:2, life:15, score:20 } // 3.3.3敵方飛機的建構函式 function Enemy(config){ this.imgs=config.imgs; this.length=config.length; this.width=config.width; this.height=config.height; this.type=config.type; this.frame=config.frame; this.life=config.life; this.score=config.score; // 定義敵方飛機的座標 this.x=Math.random()*(WIDTH-this.width); this.y=-this.height; //定義下標 this.startIndex=0; //定義碰撞屬性,沒有碰撞為false this.down=false; //定義是否爆炸完成的屬性 this.candel=false; //定義繪製方法 this.paint=function(){ context.drawImage(this.imgs[this.startIndex],this.x,this.y); }; //定義運動方法 this.step = function(){ if(!this.down){ //飛機處於正常狀態 // 小飛機,中飛機的下標始終都是0 // 大飛機的下標是在0和1之間進行切換 this.startIndex ++; this.startIndex = this.startIndex % this.frame; // 飛機向下的動畫 this.y += 2; } else{ //飛機發生碰撞以後 this.startIndex ++; if(this.startIndex == this.length){ this.candel = true; this.startIndex = this.length - 1; } } } //是否被碰撞的方法 this.checkHit=function(wo){//判斷四個邊 return wo.y + wo.height > this.y && wo.x + wo.width > this.x && wo.y < this.y + this.height && wo.x < this.x + this.width; } //敵方飛機碰撞以後的方法 this.bang=function(){ this.life--; if(this.life == 0){ this.down=true; score +=this.score; } } } // 3.3.4建立陣列存放敵方飛機 var enemise=[]; // 3.3.5建立函式,往陣列中新增資料 function enterEnemise(){ //定義一個變數存放隨機數 var rand=Math.floor(Math.random()*100) if(rand < 10){ //新增小飛機 enemise.push(new Enemy(ENEMY1)); }else if(rand < 55 && rand > 50){ //新增中飛機 enemise.push(new Enemy(ENEMY2)); }else if(rand == 88){ //新增大飛機 //大飛機尤其只有一個 //並且把大飛機放在陣列的第一位 if(enemise[0].type != 3 && enemise.length > 0){ enemise.splice(0,0,new Enemy(ENEMY3)); } } } // 3.3.6建立函式繪製敵方飛機(因為敵方飛機在陣列中,所以要迴圈遍歷畫出) function enemyPaint(){ for(var i=0;i<enemise.length;i++){ enemise[i].paint(); } } // 3.3.7建立函式敵方飛機的運動 function enemyStep(){ for(var i=0;i<enemise.length;i++){ enemise[i].step(); } } // 3.3.8建立函式刪除敵方飛機 function delenemy(){ for(var i=0;i<enemise.length;i++){ // console.log(enemise[i].candel) if(enemise[i].y > HEIGHT || enemise[i].candel){ enemise.splice(i,1) // arrayObject.splice(index,howmany,item1,.....,itemX)引數 描述 // index 必需。整數,規定新增/刪除專案的位置,使用負數可從陣列結尾處規定位置。 // howmany 必需。要刪除的專案數量。如果設定為 0,則不會刪除專案。 // item1, ..., itemX 可選。向陣列新增的新專案。 } } } // 3.3.9繪製碰撞以後的函式 // (在上面3.3.3敵方飛機的物件中定義了一個檢測{我方飛機wo}是否碰撞的方法) // (那個wo作為形參)(那麼下面的hero作為我方飛機的實參傳進去)(子彈的陣列中的元素物件{bullets[j]}作為實參傳進去) function hitEnemise(){ for(var i = 0;i< enemise.length;i++){ // 如果我方飛機撞到了敵方飛機以後 if(enemise[i].checkHit(hero)){ // 處理敵方飛機碰撞以後的邏輯 enemise[i].bang(); // 處理我方飛機碰撞以後的邏輯 hero.bang() } // 子彈如果碰到敵方飛機以後 for(var j = 0;j<bullets.length;j++){ if(enemise[i].checkHit(bullets[j])){ enemise[i].bang(); // 子彈的碰撞邏輯 bullets[j].bang(); } } } } // 3.4繪製分數和生命值 function scoreText(){ context.font="30px bold" context.fillText("score:"+score,10,30) context.fillText("life:"+life,300,30) } //第三階段的函式和物件方法寫完了 // 4.繪製遊戲暫停的階段 //呼叫畫布的滑鼠移出事件,改變狀態 canvas.onmouseout=function(){ if(state == RUNNING){ state = PAUSE; } } //呼叫畫布的滑鼠移入事件 canvas.onmouseover=function(){ if(state == PAUSE){ state = RUNNING; } } //繪製暫停圖片 var pause=new Image() pause.src="img/game_pause_nor.png" // 5.繪製遊戲結束 function gameover(){ context.font="50px bold" context.fillText("GAME OVER !!!",80,300) } setInterval(function(){ //背景圖片無論在哪個狀態都有背景圖片以及它的動態效果 sky.paint(); sky.step(); if(state==START){//第一階段 context.drawImage(logo,35,0) }else if(state == STARTING){//第二階段 loading.paint(); loading.step(); }else if(state == RUNNING){//第三狀態 //繪製我放飛機 hero.paint(); //我方飛機的運動 hero.step(); // 我方飛機的射擊方法 hero.shoot(); //子彈的繪製 bulletdPaint(); //子彈的運動 bulletdStep(); //子彈的刪除 bulletDel(); //建立敵方飛機 enterEnemise() //繪製敵方飛機 enemyPaint(); //繪製敵方飛機的運動 enemyStep(); //刪除敵方飛機 delenemy(); //判斷是否撞擊 hitEnemise(); //繪製分數和生命值 scoreText() }else if(state == PAUSE){ sky.paint(); sky.step(); hero.paint(); bulletdPaint(); enemyPaint(); scoreText() context.drawImage(pause,220,300) }else if(state == GAMEOVER){ sky.paint(); sky.step(); hero.paint(); bulletdPaint(); enemyPaint(); scoreText(); gameover(); } },100)
</script> </body> </html>
  1. 可能還有一些bug(遊戲體驗)
  2. 程式碼沒有優化到最佳。只是按照我最能理解的方式寫的。
  3. 還沒有形成那種很嚴密的邏輯思維,還需要多多努力。
  4. 感覺語法上的錯誤還好,邏輯上錯誤很不容易找出來,我還不能特別熟練通過自己調試出程式碼錯誤。
  5. 在應用熟練中學習了js中物件的建立,以及構造器的使用,函式呼叫等。