这两天参照韩顺平的坦克大战游戏并在其基础上初步完善成一个小的页面游戏,最终js代码大约570行,html代码200多行,具体可参见https://github.com/Mrkaiyang/Art/tree/gh-pages/tank。主要实现了以下功能:
- 英雄坦克控制移动、射击,敌人坦克自动移动、射击;
- 添加了老巢与保护墙,使得坦克无法穿过墙,但是可以击破墙;
现对其主要编程思想做下总结:1、使用html5的<canvas>元素绘制坦克,
主要用到了以下技术:
1、JavaScript面向对象的编程(包括三类对象:坦克、子弹、墙);
//利用构造函数构建坦克类,属性包括:坦克的横纵坐标、坦克的方向、坦克移动速度、坦克的类型(英雄坦克/敌人坦克)、坦克生存状态判断属性;方法实现了坦克上下左右移动
function Tank(x,y,direct,speed,type) { this.x=x; this.y=y; this.speed=speed; this.direct=direct; this.isLive=true; this.type=type; this.moveUp=function () { this.direct=0; this.y-=this.speed; } this.moveDown=function () { this.direct=2; this.y+=this.speed; } this.moveRight=function () { this.direct=1; this.x+=this.speed; } this.moveLeft=function () { this.direct=3; this.x-=this.speed; } }
//构造子弹类,属性包括:子弹横纵坐标,子弹的方向,子弹的移动速度,子弹的类型(英雄子弹/敌人子弹),子弹存在判断属性,所属坦克等;方法实现了子弹的移动的条件及位置移动
function Bullet(x,y,direct,speed,type,tank) { this.x=x; this.y=y; this.isLive=true; this.timer=timer; this.direct=direct; this.speed=speed; this.type=type; this.tank=tank; this.run=function() { if(this.x<0||this.x>500||this.y<10||this.y>600||this.isLive==false){ clearInterval(this.timer); this.isLive=false; if(this.type=="enemy"){ this.tank.bulletIsLive=false; } }else { switch (this.direct) { case 0: this.y -= this.speed; break; case 1: this.x += this.speed; break; case 2: this.y += this.speed; break; case 3: this.x -= this.speed; break; } } } }//利用构造函数构造墙体类,属性包括:墙的横纵坐标、墙的存在判断属性;
//利用对象冒充继承方法构造英雄坦克类,英雄坦克自带射击方法function Wall(x,y){ this.x=x; this.y=y; this.isLive=true; }
var heroBullets=new Array(); function Hero(x,y,direct,speed,type) { //通过对象冒充达到继承的的效果 this.tank=Tank; this.tank(x,y,direct,speed,type); this.shotEnermy=function () { switch (this.direct){ case 0: heroBullet=new Bullet(this.x+9,this.y,this.direct,10);//全局变量 break; case 1: heroBullet=new Bullet(this.x+30,this.y+9,this.direct,10);//全局变量 break; case 2: heroBullet=new Bullet(this.x+9,this.y+30,this.direct,10);//全局变量 break; case 3: heroBullet=new Bullet(this.x,this.y+9,this.direct,10);//全局变量 break; } heroBullets.push(heroBullet); heroBullet.timer=timer//面向对象引用传递; var timer=window.setInterval("heroBullets["+(heroBullets.length-1)+"].run()",50); } }
//利用对象冒充继承方法构造敌人坦克类
function EnemyTank(x,y,direct,speed,type) { this.tank=Tank;//继承实现 this.isLive=true; this.bulletIsLive=true; //this.enermy(x,y,direct,speed,type); this.count=0; this.bulletIsLive=true; this.tank(x,y,direct,speed,type); this.run=function () { var flag = null; switch (this.direct) { case 0: for (var i = 0; i < wall.length; i++) {//实现遇到墙体换向 if ((this.y > 0) && ((this.y - this.speed) >= wall[i].y) &&((this.y - this.speed) < wall[i] + 40) && (this.x >= wall[i].x) &&( this.x < wall[i].x + 40)&&(wall[i].isLive==true)) { flag = 1; } } if (this.y > 0 && flag != 1) { this.y -= this.speed; } else { this.direct = Math.round(Math.random() * 3); } break; case 1: for (var i = 0; i < wall.length; i++) { if ((this.x+20 <500) && (this.y+30 > wall[i].y) && (this.y < (wall[i].y + 40)) && ((this.x+30 + this.speed) >= wall[i].x) && ((this.x + this.speed) < (wall[i].x + 40))&&(wall[i].isLive==true)){ flag = 1; } } if (this.x + 30 < 500 && flag != 1) { this.x += this.speed; } else { this.direct = Math.round(Math.random() * 3); } break; case 2: for(var i=0;i<wall.length;i++){ if ((this.y+30<600) && ((this.y+30 + this.speed)>= wall[i].y) &&(this.y+30 + this.speed) < (wall[i].y + 40) && (this.x+20 >= wall[i].x) &&( this.x < wall[i].x + 40)&&(wall[i].isLive==true)){ flag = 1; } } if (this.y+30<600&& flag != 1) { this.y += this.speed; } else { this.direct = Math.round(Math.random() *3); } break; case 3: for (var i = 0; i < wall.length; i++) { if ((this.x-20 > 0) && (this.y+30 > wall[i].y) && (this.y < (wall[i].y + 40)) && ((this.x - this.speed) >= wall[i].x) && ((this.x - this.speed) < (wall[i].x + 40))&&(wall[i].isLive==true)){ flag = 1; } } if (this.x > 0 && flag != 1) { this.x -= this.speed; } else { this.direct = Math.round(Math.random() * 3); } break; } if (this.count > 30) { this.direct = Math.round(Math.random() * 3);//随机生成 0,1,2,3 this.count = 0; } this.count++; } }
2、绘图技术;利用html5<canvas>元素绘制作战区域,坦克及子弹
<canvas id="tankMap1" width="500px" height="600px" style="background-color:black" ></canvas>
<script type="text/javascript"> var drawing=document.getElementById("tankMap1"); var cxt=drawing.getContext('2d');
</script>var hero=new Hero(150,420,0,5,"hero");//绘制坦克 function drawTank(tank) { if (tank.isLive==true){ switch (tank.direct){ case 0: case 2: if(tank.type=="hero"){ cxt.fillStyle="#DED284"; }else if(tank.type=="enemy"){ cxt.fillStyle="yellow"; } cxt.fillRect(tank.x,tank.y,5,30); cxt.fillRect(tank.x+15,tank.y,5,30); cxt.fillRect(tank.x+6,tank.y+5,8,20); if(tank.type=="hero"){ cxt.fillStyle="#FFD972"; }else if(tank.type=="enemy"){ cxt.fillStyle="red"; } cxt.arc(tank.x+10,tank.y+15,4,0,2*Math.PI,true); cxt.fill(); //������Ͳ //cxt.strokeStyle="#FFD972"; if(tank.type=="hero"){ cxt.strokeStyle="#FFD972"; }else if(tank.type=="enemy"){ cxt.strokeStyle="red"; } cxt.lineWidth=2; cxt.beginPath(); cxt.moveTo(tank.x+10,tank.y+15); if(tank.direct==0){ cxt.lineTo(tank.x+10,tank.y); }else if(tank.direct==2) { cxt.lineTo(tank.x+10,tank.y+30) } cxt.closePath(); cxt.stroke(); break; case 1: case 3: if(tank.type=="hero"){ cxt.fillStyle="#DED284"; }else if(tank.type=="enemy"){ cxt.fillStyle="yellow"; } cxt.fillRect(tank.x,tank.y,30,5); cxt.fillRect(tank.x,tank.y+15,30,5); cxt.fillRect(tank.x+5,tank.y+6,20,8); if(tank.type=="hero"){ cxt.fillStyle="#FFD972"; }else if(tank.type=="enemy"){ cxt.fillStyle="red"; } cxt.arc(tank.x+15,tank.y+10,4,0,2*Math.PI,true); cxt.fill(); //������Ͳ /* cxt.strokeStyle="#FFD972"; cxt.lineWidth=2;*/ if(tank.type=="hero"){ cxt.strokeStyle="#FFD972"; }else if(tank.type=="enemy"){ cxt.strokeStyle="red"; } cxt.lineWidth=2; cxt.beginPath(); cxt.moveTo(tank.x+15,tank.y+10); if(tank.direct==1){ cxt.lineTo(tank.x+30,tank.y+10); }else if(tank.direct==3) { cxt.lineTo(tank.x,tank.y+10) } cxt.closePath(); cxt.stroke(); break; } } }
//绘制英雄子弹
function drawHeroBullet(){ for(var i=0;i<heroBullets.length;i++){ var heroBullet=heroBullets[i]; if(heroBullet!=null&&heroBullet.isLive){ cxt2.fillStyle="yellow"; cxt2.fillRect(heroBullet.x,heroBullet.y,2,2); } } }//绘制敌人子弹
function drawEnemyBullet(tank,i,bullet){ var etBullet = bullet; if (etBullet.isLive == true&&tank.bulletIsLive==true&&tank.isLive==true) { cxt.fillStyle = "#00FEFE"; cxt.fillRect(etBullet.x, etBullet.y, 2, 2); } }//绘制墙体
function drawWall() { var wall=new Array(wall1,wall2,wall3,wall4,wall5,wall6,wall7,wall8,wall9,wall10,wall11,wall12,wall13,wall14); for (var i = 0; i < wall.length; i++) { if(wall[i].isLive==true){ var img = new Image() img.src = "wall_1.png" cxt.drawImage(img, wall[i].x, wall[i].y); } } }
3、界面编程;
4、多个独立的定时器系统工作;
//启动这个敌人的坦克 window.setInterval("enemyTanks["+i+"].run()",50);
//不停地刷新画图,实现坦克移动;
var timer1=setInterval("flashTanMap()",100);//不停地刷新画图,实现英雄子弹移动;
var timer=setInterval("flashBullet()",100);
5、网页游戏的编程思想;
难点所在:
1、如何实现坦克爆炸后产生爆炸动画效果?
//定义一个炸弹类 function Bomb(x,y){ this.x=x; this.y=y; this.isLive=true; //炸弹是否活的,默认true; //炸弹有一个生命值 this.blood=9; //减生命值 this.bloodDown=function(){ if(this.blood>0){ this.blood--; }else{ //说明炸弹死亡 this.isLive=false; } } }
//编写一个函数,专门用于判断我的子弹,是否击中了某个敌人坦克 function isHitEnemyTank(){ //取出每颗子弹 for(var i=0;i<heroBullets.length;i++){ //取出一颗子弹 var heroBullet=heroBullets[i]; /* alert(heroBullet.islive);*/ if(heroBullet.isLive===true){ //子弹是活的,才去判断 //让这颗子弹去和遍历每个墙判断 for(var i=0;i<wall.length;i++){ var walls=wall[i]; if(walls.isLive==true){ if(heroBullet.x>=wall[i].x&&heroBullet.x<=wall[i].x+40 &&heroBullet.y>=wall[i].y&&heroBullet.y<=wall[i].y+40){ wall[i].isLive=false; heroBullet.isLive=false; } } } //让这颗子弹去和遍历每个敌人坦克判断 for(var j=0;j<enemyTanks.length;j++){ var enemyTank=enemyTanks[j]; if(enemyTank.isLive===true){ //子弹击中敌人坦克的条件是什么? 很多思路 , 韩老师的思想是 //(看看这颗子弹,是否进入坦克所在矩形) //根据当时敌人坦克的方向来决定 switch(enemyTank.direct){ case 0: //敌人坦克向上 case 2://敌人坦克向下 if(heroBullet.x>=enemyTank.x&&heroBullet.x<=enemyTank.x+20 &&heroBullet.y>=enemyTank.y&&heroBullet.y<=enemyTank.y+30){; //把坦克isLive 设为false ,表示死亡 enemyTank.isLive=false; //该子弹也死亡 heroBullet.isLive=false; //创建一颗炸弹 var bomb=new Bomb(enemyTank.x,enemyTank.y);//在坦克确认被击中后,增加一个爆炸实例,然后将其画出 //然后把该炸弹放入到bombs数组中 bombs.push(bomb); } break; case 1: //敌人坦克向右 case 3://敌人坦克向左 if(heroBullet.x>=enemyTank.x&&heroBullet.x<=enemyTank.x+30 &&heroBullet.y>=enemyTank.y&&heroBullet.y<=enemyTank.y+20){ //把坦克isLive 设为false ,表示死亡 enemyTank.isLive=false; heroBullet.isLive=false; //创建一颗炸弹 var bomb=new Bomb(enemyTank.x,enemyTank.y); //然后把该炸弹放入到bombs数组中 bombs.push(bomb); } break; } } }//for } } }