web版canvas做飛機大戰游戲 總結


  嘮嘮:兩天的時間跟着做了個飛機大戰的游戲,感覺做游戲挺好的。說是用html5做,發現全都是js。說js里一切皆為對象,寫的最多的還是函數,都是函數調用。對這兩天的代碼做個總結,希望路過的大神指點一下,我對這個游戲的思路,可改進優化的代碼。

  先說一下游戲的基本內容: 打飛機(不要想歪了),有鼠標控制移動英雄機,子彈自動射擊;敵機從上而下,有三種敵機;

  先說下HTML代碼(主要就是這一行):  

<canvas id="canFly" width="480" height="650"></canvas>

 

一、對這個游戲的的基本數據狀態做定義

  主要包括:

    游戲的狀態: 開始狀態 英雄機入場狀態 游戲進行狀態 暫停狀態 gameOver;得分 英雄機的生命 

 1 var canvas = document.getElementById("canFly");//獲取canvas元素
 2 //創建畫布對象
 3 var context = canvas.getContext("2d");
 4 //游戲的基本數據
 5 var gameData = {
 6     state : this.START,
 7     //游戲狀態
 8     START : 0,//開始界面狀態
 9     STARTING : 1,//入場動畫過渡狀態
10     RUNNING : 2,//游戲運行狀態
11     PAUSED : 3,//暫停
12     GAMEOVER : 4,//游戲結束
13     //英雄機生命
14     heroLife : 3,
15     //得分
16     score : 0,
17     //畫布寬高
18     HEIGHT : canvas.height
19 }
View Code

二、對圖片資源的加載及初始化相關數據

 1             /*-- 加載游戲圖片 -------------------------------------------------------------------*/
 2             //背景圖片
 3             var bgImg = new Image();
 4             bgImg.src="images/background.png";
 5             //logo圖片
 6             var startLogo = new Image();
 7             startLogo.src = "images/start.png";
 8             //加載飛機入場動畫
 9             var loadings = [];
10             loadings[0] = new Image();
11             loadings[0].src="Images/game_loading1.png";
12             loadings[1] = new Image();
13             loadings[1].src="Images/game_loading2.png";
14             loadings[2] = new Image();
15             loadings[2].src="Images/game_loading3.png";
16             loadings[3] = new Image();
17             loadings[3].src="Images/game_loading4.png";
18             //加載英雄機圖片
19             var heros = [];
20             heros[0] = new Image();
21             heros[0].src="images/hero1.png";
22             heros[1] = new Image();
23             heros[1].src="images/hero2.png";
24             //英雄機爆破動畫圖片
25             heros[2] = new Image();
26             heros[2].src="images/hero_blowup_n1.png";
27             heros[3] = new Image();
28             heros[3].src="images/hero_blowup_n2.png";
29             heros[4] = new Image();
30             heros[4].src="images/hero_blowup_n3.png";
31             heros[5] = new Image();
32             heros[5].src="images/hero_blowup_n4.png";
33             //加載子彈的圖片
34             var bullet = [];
35             bullet[0] = new Image();
36             bullet[0].src = "images/bullet1.png";
37             ...
View Code
 1             /*-- 初始化游戲內容相關數據 --*/
 2             //初始化游戲背景圖片數據
 3             var SKY = {
 4                 imgs : bgImg,//背景圖片
 5                 width : 480,//圖片寬度
 6                 height : 852 //圖片高度
 7             }
 8             //初始化英雄機入場動畫圖片數據
 9             var LOADING = {
10                 imgs : loadings,
11                 width : 186,//圖片寬度
12                 height : 38,//圖片高度
13                 sum : loadings.length //圖片個數
14             }
15             //初始化英雄機的數據
16             var HERO = {
17                 imgs : heros,
18                 width : 99,
19                 height : 124,
20                 sum : heros.length,
21                 length : 2//我方飛機正常圖片個數
22             }
23             //初始化子彈的數據
24             var BULLET = {//默認子彈
25                 imgs : bullet,
26                 width : 9,
27                 height : 21,
28                 sum : bullet.length
29             }
30 .......
View Code

 

三、公用構造器及對象實例化

  定義一個公用的構造器函數,這是我寫這個游戲認最大的收獲了,在這里體會到了面向對象的思想;相當於定義一個基礎類,所有的構造器都用公用構造器函數進行初始化,提高代碼的復用,然而在我的優化過程中僅僅只是節省了50多行的代碼。

  公共構造器函數:在這里定義了圖片的寬高,圖片對象是否執行爆破,是否刪除,圖片繪制坐標等一些公共的屬性和方法

 1             /*-- 通用構造器對象 前端代碼盡量地使用通用代碼 -------------------------------------------------------------------------------------------------------*/
 2             function Compant(config){
 3                 //加載圖片
 4                 this.imgs = config.imgs;
 5                 //圖片的寬度和高度
 6                 this.width = config.width;
 7                 this.height = config.height;
 8                 this.sum = config.sum;
 9                 this.length = config.length;
10                 // 敵方飛機具有以下屬性
11                 this.type = config.type;//敵機類型
12                 this.life = config.life;//敵機聲明值
13                 this.score = config.score;//敵機分數
14                 // 設置相對速度
15                 this.time = 0;
16                 // 設置圖片的索引值
17                 this.index = 0;
18                 // 是否執行爆破動畫的標識
19                 this.down = false;
20                 // 是否刪除標識
21                 this.canDelete = false;
22                 //繪圖坐標
23                 this.x = 0;
24                 this.y = 0;
25                 // 繪制方法
26                 this.paint = function(){
27                     context.drawImage(this.imgs[this.index],this.x,this.y);
28                 }
29                 // 移動方法
30                 this.step = function(){}
31                 // 執行撞擊后的邏輯方法
32                 this.bang = function(){}
33             }
View Code

 

   繼承實例化:

 1             //---背景
 2             //創建背景圖片的構造器
 3             function BgSky(config){
 4                 //調用通用構造器初始化
 5                 Compant.call(this,config);
 6                 //圖片繪制高度變量
 7                 this.y1 = -this.height;
 8                 this.y2 = 0;
 9                 //定義繪制方法
10                 this.paint = function(){            
11                     context.drawImage(this.imgs,0,this.y1);//第一張圖片
12                     context.drawImage(this.imgs,0,this.y2);//第二張圖片
13                 }
14                 //背景heigth運動方法
15                 this.step = function(){
16                     this.time++;
17                     if (this.time%3==0)
18                     {//控制背景圖片height值的增加
19                         this.y1++;//圖片運動下一幀
20                         this.y2++;
21                         //圖片移動處畫布后將y坐標重置為-height 實現圖片銜接滾動
22                         this.y1>this.height&&(this.y1 = -this.height);
23                         this.y2>this.height&&(this.y2 = -this.height);
24                         this.time=1;//重置移動時間
25                     }
26                 }
27             }
28             //創建圖片對象
29             var sky = new BgSky(SKY);
30             
31             //---英雄機入場動畫構造器
32             function Loading(config){
33                 Compant.call(this,config);
34                 //定義繪制
35                 this.paint = function(){
36                     //繪制飛機入場動畫圖片
37                     context.drawImage(this.imgs[this.index],0,gameData.HEIGHT-this.height);
38                 }
39                 //定義入場動畫
40                 this.step = function(){
41                     this.time++;
42                     if (this.time%20==0)
43                     {//實現動畫的播放速度
44                         this.index++;//下一幀動畫
45                         if (this.index==this.sum)
46                         {//判斷動畫結束后,更改游戲的狀態,進入第三階段游戲階段
47                             gameData.state=gameData.RUNNING;    
48                             this.time=0;//重置動畫時間
49                         }
50                     }
51                 }
52             }
53             //創建飛機入場動畫的對象
54             var loading = new Loading(LOADING);
View Code

  利用這種方式將所有的對象都進行實例化,並添加相應的方法

四、英雄機的子彈發射

  英雄機的子彈發射是自動,就是說只要控制好裝彈的頻率就可以了;英雄機發射子彈就是向子彈數組中添加子彈

bullets[bullets.length] = new Bullet(BULLET);;//向子彈數組中添加子彈

 

   子彈的移動,撞擊,刪除等功能在子彈的構造函數中定義,英雄機只管裝彈的頻率;

  子彈的繪制:

 1             function paintBullets(){
 2                 for (var i=0, length=bullets.length;  i<length; i++)
 3                 {
 4                     bullets[i].paint();//繪制當前子彈
 5                     if (gameData.state==gameData.RUNNING)
 6                     {//游戲運行中時移動子彈
 7                         bullets[i].step();//移動子彈
 8                     }
 9                 }
10             }

 

  刪除子彈的判斷:

1             function clearStep(){
2                 for (var i = bullets.length-1; i>=0 ; i--)
3                 {
4                     if (bullets[i].y<=-bullets[i].height || (bullets[i].canDelete))
5                     {
6                         bullets.splice(i,1);//刪除當前超出屏幕的子彈和撞機的子彈
7                     }
8                 }
9             }
      //這個函數可以跟上邊的合並到一起

  

五、敵機的相關設置

  敵機的創建: 應為有三種類型的敵機,按照幾率小的最多,中飛機的其次,打飛機滿屏只能有一個

 1             //創建用於創建敵方飛機的函數
 2             function createEnemies(){
 3                 /*創建敵方飛機 - 小,中,大*/
 4                 var num = Math.floor(Math.random()*100);
 5                 if (num < 80)
 6                 {//小飛機
 7                     enemies[enemies.length] = new Enemy(ENEMY1);
 8                 }else if (num < 90)
 9                 {//中飛機
10                     enemies[enemies.length] = new Enemy(ENEMY2);
11                 }else {
12                     //大飛機只能存在一個
13                     if (enemies.length > 0 && enemies[0].type != 2)
14                     {
15                         enemies.unshift(new Enemy(ENEMY3));//將大飛機添加到數組開頭,這樣每次判斷數組第一個就可以知道
16                     }
17                 }
18             }

   對敵機的繪制,檢測敵機是否超出屏幕,是否被打中,是否需要爆炸,是否和英雄機相撞等

 1             function paintEnemiesAndCheckHit(){
 2                 for (var i=0; i<enemies.length; i++)
 3                 {//遍歷敵機
 4                     //
 5                     var enemy = enemies[i];//敵機
 6                     //檢測敵機和英雄機是否碰撞
 7                     if ((enemy.y > gameData.HEIGHT)||(enemy.canDelete))
 8                     {
 9                         enemies.splice(i,1);//刪除當前超出屏幕的飛機
10                         continue;
11                     }
12                     enemy.paint();//繪制飛機
13                     if (gameData.state == gameData.RUNNING)
14                     {//游戲運行中時才移動飛機
15                         enemy.step();//移動飛機
16                     }
17                     //判斷是否和我方飛機碰撞
18                     if (enemy&&enemy.hit(hero))
19                     {//敵機和我方飛機相撞
20                             enemy.bang();
21                             hero.bang();//飛機銷毀
22                     }
23                     //判斷子彈
24                     for (var j=0; j<bullets.length; j++)
25                     {//子彈遍歷
26                         var bullet = bullets[j];//子彈
27                         if (enemy.hit(bullet))
28                         {//子彈撞機敵方飛機
29                             enemy.bang();//刪除敵機
30                             bullet.bang();//刪除子彈
31                         }
32                     }
33                 }
34             }
View Code

 

六、主體流程的控制

  這里使用switch來控制在執行相應狀態的操作,使用setTimeout來控制循環的進行,感覺setTimeout比setInterval更加的容易控制

 1                 //根據游戲狀態執行相應操作
 2                 switch (gameData.state)
 3                 {
 4                     case gameData.START://游戲開始狀態
 5                         context.drawImage(startLogo,30,0);//繪制開始logo
 6                         break;
 7                     case gameData.STARTING: //英雄機進場過渡狀態
 8                         loading.paint();//繪制飛機入場動畫
 9                         loading.step();//入場動畫
10                         break;
11                     case gameData.RUNNING: //游戲進行狀態
12                         hero.paint();
13                         hero.step();
14                         hero.shoot();//飛機射擊
15                         paintBullets();//繪制所有子彈
16                         clearStep();//清除超出的子彈
17 
18                         if (enemyTime%100 == 0)
19                         {
20                             createEnemies();//創建敵方飛機
21                         }
22                         paintEnemiesAndCheckHit();//繪制所有敵方飛機和碰撞檢測
23                         break;
24                     case gameData.PAUSED: //游戲暫停狀態
25                         hero.paint();
26                         paintBullets();//繪制所有子彈
27                         paintEnemiesAndCheckHit();//繪制所有敵方飛機和碰撞檢測
28                         paintPaused();
29                         break;
30                     case gameData.GAMEOVER: //游戲結束狀態
31                         gameover();
32                         break;
33                 }
34                 painText();//繪制得分
35                 
36                 //定時器,畫布刷新
37                 setTimeout(function(){
38                     gameExec();
39                 },10);
View Code

 

七、響應事件的綁定

  1.開始界面單擊鼠標,開始游戲

1             canvas.onclick = function(){
2                 if (gameData.state == gameData.START)
3                 {//在游戲開始狀態下單擊,進入游戲過渡階段
4                     gameData.state = gameData.STARTING;//改變游戲狀態
5                 }
6             }

 

   2.綁定鼠標的移動事件,英雄機是跟隨鼠標移動的

 1             canvas.onmousemove = function(event){
 2                 //獲取鼠標當前相對於canvas畫布的坐標
 3                 var x = event.offsetX;
 4                 var y = event.offsetY;
 5                 //我方飛機坐標設置
 6                 hero.x=x-hero.width/2;// x坐標
 7                 hero.y=y-hero.height/2;//y坐標
 8                 if (gameData.state == gameData.PAUSED)
 9                 {
10                     gameData.state = gameData.RUNNING;
11                 }
12             }
View Code

 

   3.鼠標離開畫布事件,鼠標離開則游戲暫停

1             canvas.onmouseout = function(){
2                 if (gameData.state == gameData.RUNNING)
3                 {
4                     gameData.state = gameData.PAUSED;
5                 }
6             }    
View Code

 

八、后續的一些設想

  現在的游戲不能重新開始,需要刷新才能重新開始,所以定義了 init() 函數用於游戲結束后重新開始(需要刪除setTimeout事件):

            function init(){
                //設置游戲的初始狀態
                gameData.state = gameData.START;
                gameData.score = 0;//分數重置
                gameData.heroLife = 3;//聲明值重置
                //游戲運行
                gameExec();
            }

 

   還有關於子彈的類型的設想: 可以設置 雙列子彈,散花彈等子彈的類型,子彈可升級,設置子彈的威力等;可以設置速度的變更等

  有路過的大神可以看下下邊的源碼,指點下(源碼不長就10kb多點)

九、源碼鏈接

  完整源碼下載

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM