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多点)

九、源码链接

  完整源码下载

原文地址:https://www.cnblogs.com/Medeor/p/4970353.html