唠唠:两天的时间跟着做了个飞机大战的游戏,感觉做游戏挺好的。说是用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 }
二、对图片资源的加载及初始化相关数据
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 ...
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 .......
三、公用构造器及对象实例化
定义一个公用的构造器函数,这是我写这个游戏认最大的收获了,在这里体会到了面向对象的思想;相当于定义一个基础类,所有的构造器都用公用构造器函数进行初始化,提高代码的复用,然而在我的优化过程中仅仅只是节省了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 }
继承实例化:
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);
利用这种方式将所有的对象都进行实例化,并添加相应的方法
四、英雄机的子弹发射
英雄机的子弹发射是自动,就是说只要控制好装弹的频率就可以了;英雄机发射子弹就是向子弹数组中添加子弹
bullets[bullets.length] = new Bullet(BULLET);;//向子弹数组中添加子弹
子弹的移动,撞击,删除等功能在子弹的构造函数中定义,英雄机只管装弹的频率;
子弹的绘制:
1 function paintBullets(){ 2 for (var i=0, length=bullets.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) 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 ) 25 { //子弹遍历 26 var bullet = bullets[j];//子弹 27 if (enemy.hit(bullet)) 28 { //子弹撞机敌方飞机 29 enemy.bang();//删除敌机 30 bullet.bang();//删除子弹 31 } 32 } 33 } 34 }
六、主体流程的控制
这里使用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);
七、响应事件的绑定
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 }
3.鼠标离开画布事件,鼠标离开则游戏暂停
1 canvas.onmouseout = function(){ 2 if (gameData.state == gameData.RUNNING) 3 { 4 gameData.state = gameData.PAUSED; 5 } 6 }
八、后续的一些设想
现在的游戏不能重新开始,需要刷新才能重新开始,所以定义了 init() 函数用于游戏结束后重新开始(需要删除setTimeout事件):
function init(){ //设置游戏的初始状态 gameData.state = gameData.START; gameData.score = 0;//分数重置 gameData.heroLife = 3;//声明值重置 //游戏运行 gameExec(); }
还有关于子弹的类型的设想: 可以设置 双列子弹,散花弹等子弹的类型,子弹可升级,设置子弹的威力等;可以设置速度的变更等
有路过的大神可以看下下边的源码,指点下(源码不长就10kb多点)
九、源码链接
完整源码下载