HTML5+JS实现坦克大战小游戏

   听了韩顺平老师的视频教程,学到了不少编程思想,原来看似简单的坦克大战小游戏写起来其实并不简单。这里总结一下这几天学到的东西。

        首先是关于html5的知识了。这里我们基本上只用了画布canvas来画坦克,还有就是html5的第一行<!DOCTYPE html>来说明这是一个使用html5写的页面,html5的特性之一就包括canvas,这是html中没有的标签。

        另外一个就是js了,涉及到的语法都是最简单、最基本的用法,这在代码中进行解释。同时,韩老师在这个视频中代码有个bug就是敌人坦克在死后依然会发子弹,小弟在此进行了修正。

       接下来最重要的就是我们要来分析一下坦克大战小游戏的设计思路:

       1、要有作战区域,这有canvas 可以帮我们解决。

       2、要有坦克,包括自己的、敌人的。

       3、要能有子弹,包括自己的、敌人的。

       4、要有炸弹。

       5、我的坦克要能按键发射子弹,敌人的坦克要能连续自动发射子弹。

       6、我的子弹打到敌人时子弹要消失,同时敌人爆炸并消失,不能再发子弹。

       以上是游戏设计的简单思路,这是初步设计,接下来以代码来分析详细的设计过程:

       步骤一:

          组织代码,为了代码易读,而且我使用的是html5+js来实现,自不必多说,使用tankGame.js+tankGame.html来组织我的代码,js用于类以及方法的实现过程,html实现函数调用以及网页的显示。

       步骤二:设计作战区域:

         code 1 from tankeGame.html

<canvas id="tankMap" width="500px" height="500px" style="background-color:black"></canvas>
<pre class="html" name="code"><script type="text/javascript">

	//得到画布
	var canvas1=document.getElementById("tankMap");
	//获得画图工具
	var cxt=canvas1.getContext("2d");</script>
<script type="text/javascript">
	//得到画布
	var canvas1=document.getElementById("tankMap");
	//得到绘图上下文(你可以理解是画笔)
	var cxt=canvas1.getContext("2d");</script>


                      

     步骤三:画坦克,包括自己的和敌人的:

// 基类,里面有基本的共有属性和方法
function Tank(x,y,direct,color){
	
		this.x=x;
		this.y=y;
		this.speed=1;
		this.isLive=true;
		this.direct=direct;
		//一个坦克,需要两个颜色.
		this.color=color;
		//上移
		this.moveUp=function(){
			this.y-=this.speed;
			this.direct=0;
		}
		//向右
		this.moveRight=function(){
			this.x+=this.speed;
			this.direct=1;
		}

		//下移
		this.moveDown=function(){
			this.y+=this.speed;
			this.direct=2;
		}
		//左
		this.moveLeft=function(){
			this.x-=this.speed;
			this.direct=3;
		}
}


//子类Hero和EnemyTanke继承自基类。注意继承的方法。

//定义一个Hero类
	//x 表示坦克的 横坐标, y 表示纵坐标, direct 方向	
	function Hero(x,y,direct,color){
	
		//继承的方法和格式,tank其实就是一个对象
		this.tank=Tank;
		this.tank(x,y,direct,color);

		//增加一个私有函数,射击敌人坦克.
		this.shotEnemy=function(){
			
			//创建子弹, 子弹的位置应该和hero有关系,并且和hero的方向有关
			switch(this.direct){
				case 0:
				heroBullet=new Bullet(this.x+9,this.y,this.direct,3,"hero",this);
				break;
				case 1:
				heroBullet=new Bullet(this.x+30,this.y+9,this.direct,3,"hero",this);
				break;
				case 2:
				heroBullet=new Bullet(this.x+9,this.y+30,this.direct,3,"hero",this);
				break;
				case 3: //右
				heroBullet=new Bullet(this.x,this.y+9,this.direct,3,"hero",this);
				break;
			}

			//把这个子弹对象放入到数组中	 -> push函数
			heroBullets.push(heroBullet);
			//定时器.
			var timer=window.setInterval("heroBullets["+(heroBullets.length-1)+"].run()",50);
			//把这个timer赋给这个子弹(js对象是引用传递!)
			heroBullets[heroBullets.length-1].timer=timer;

	}
}
 //定义一个EnemyTank类
 function EnemyTank (x,y,direct,color){
  
  //继承Tank
  this.tank=Tank;
  this.count=0;
  this.bulletIsLive=true;
  
  this.tank(x,y,direct,color);
  this.qetBullet=null;
  this.run=function run(){
   
   //判断敌人的坦克当前方向
   switch(this.direct){
    
    case 0:
     if(this.y>0){
      this.y-=this.speed;
     } 
     break;
    case 1:
     if(this.x+30<400){
      this.x+=this.speed;
     }
     break;
    case 2:
     if(this.y+30<300){
      this.y+=this.speed;
     }
     break;
    case 3:
     if(this.x>0){
      this.x-=this.speed;
     }
     break;
   }
   //改变方向,走50次,再改变方向
   if(this.count>50){
    this.direct=Math.round(Math.random()*3);//随机生成 0,1,2,3
    this.count=0;
   }
   this.count++;
   //判断敌人坦克是否还活着
   //if(this.tank.isLive==true){
   //判断子弹是否已经死亡,如果死亡,则增加新的一颗子弹
   if(this.bulletIsLive==false){
    //增子弹,这是需要考虑当前这个敌人坦克的方向,在增加子弹
     switch(this.direct){
      case 0:
       qetBullet=new Bullet(this.x+9,this.y,this.direct,3,"enemy",this);
      break;
      case 1:
       qetBullet=new Bullet(this.x+30,this.y+9,this.direct,3,"enemy",this);
      break;
      case 2:
       qetBullet=new Bullet(this.x+9,this.y+30,this.direct,3,"enemy",this);
      break;
      case 3: //右
      qetBullet=new Bullet(this.x,this.y+9,this.direct,3,"enemy",this);
      break;
        }</p><p>    //把子弹添加到敌人子弹数组中
    enemyBullets.push(qetBullet);
    //启动新子弹run
    var mytimer=window.setInterval("enemyBullets["+(enemyBullets.length-1)+"].run()",50);
    enemyBullets[enemyBullets.length-1].timer=mytimer;</p><p>    this.bulletIsLive=true;
   }
  }
 
//绘制坦克(敌人坦克和自己的坦克)
	function drawTank(tank){
	
		//说明所有的坦克都要isLive这个属性
		if(tank.isLive){
		

			//考虑方向
			switch(tank.direct){

			case 0: //上
			case 2:// 下
				//画出自己的坦克,使用前面的绘图技术
				//设置颜色
				cxt.fillStyle=tank.color[0];
				//韩老师使用 先死--->后活 (初学者最好用这个方法)
				//先画出左面的矩形
				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);
				//画出坦克的盖子
				cxt.fillStyle=tank.color[1];
				cxt.arc(tank.x+10,tank.y+15,4,0,360,true);
				cxt.fill();
				//画出炮筒(直线)
				cxt.strokeStyle=tank.color[1];
				//设置线条的宽度
				cxt.lineWidth=1.5;
				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:
				//画出自己的坦克,使用前面的绘图技术
				//设置颜色
				cxt.fillStyle=tank.color[0];
				//韩老师使用 先死--->后活 (初学者最好用这个方法)
				//先画出左面的矩形
				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);
				//画出坦克的盖子
				cxt.fillStyle=tank.color[1];
				cxt.arc(tank.x+15,tank.y+10,4,0,360,true);
				cxt.fill();
				//画出炮筒(直线)
				cxt.strokeStyle=tank.color[1];
				//设置线条的宽度
				cxt.lineWidth=1.5;
				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;

			}
		}
	}

步骤四:画子弹:

type表示:这颗子弹是敌人的,还是自己的
//tank表示对象,说明这颗子弹,属于哪个坦克.
function Bullet(x,y,direct,speed,type,tank){
	this.x=x;
	this.y=y;
	this.direct=direct;
	this.speed=speed;
	this.timer=null;
	this.isLive=true;
	this.type=type;
	this.tank=tank;
	this.run=function run(){
		
			//在该表这个子弹的坐标时,我们先判断子弹是否已经到边界
			//子弹不前进,有两个逻辑,1.碰到边界,2. 碰到敌人坦克.
			if(this.x<=0||this.x>=500||this.y<=0||this.y>=500||this.isLive==false){
				//子弹要停止.
				window.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;
				}
			}

	}
}
//Bullet只是一个类,并没有画坦克的方法,需要我们自己设计,draw一个
<pre class="javascript" name="code">//画出自己的子弹
		function drawHeroBullet(){

				//现在要画出所有子弹
				for( var i=0;i<heroBullets.length;i++){
					var heroBullet=heroBullets[i];
                      			if(heroBullet!=null&&heroBullet.isLive){
					   cxt.fillStyle="#FEF26E";
					   cxt.fillRect(heroBullet.x,heroBullet.y,2,2);
					}
				}

		}

//这里我们还需要添加一个函数,用于画出敌人的子弹
function drawEnemyBullet(){
//检查坦克是否还活着
for(var j=0;j<enemyTanks.length;j++){
					
    var enemyTank=enemyTanks[j];
						
    if(enemyTank.isLive==true){
       for( var i=0;i<enemyBullets.length;i++){
       var etBullet=enemyBullets[i];
       if(etBullet!=null&&etBullet.isLive){
	   cxt.fillStyle="#00FEFE";
	   cxt.fillRect(etBullet.x,etBullet.y,2,2);
	}
       } 
     }	
   }
}    	
<p>步骤五:检测判断自己的坦克是否击中敌人坦克。</p><pre class="javascript" name="code">//编写一个函数,专门用于判断我的子弹,是否击中了某个敌人坦克
function isHitEnemyTank(){
	
     //取出子弹
	for(var i=0;i<heroBullets.length;i++){
			
	  //取出一颗子弹
	  var heroBullet=heroBullets[i];
	  if(heroBullet.isLive){ //子弹是活的,才去判断
				//让这颗子弹去和遍历每个敌人坦克判断
	  for(var j=0;j<enemyTanks.length;j++){
	     var enemyTank=enemyTanks[j];
	     if(enemyTank.isLive){
	     //(看看这颗子弹,是否进入坦克所在矩形)
	     //根据当时敌人坦克的方向来决定
	     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
   }
  }
}

步骤六:设计爆炸效果

//定义一个炸弹类
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 drawEnemyBomb(){
	
	for(var i=0;i<bombs.length;i++){
	
		//取出一颗炸弹
		var bomb=bombs[i];
		if(bomb.isLive){


			
			//更据当前这个炸弹的生命值,来画出不同的炸弹图片
			if(bomb.blood>6){  //显示最大炸弹图
				var img1=new Image();
				img1.src="picture/bomb_1.gif";
				var x=bomb.x;
				var y=bomb.y;
				img1.οnlοad=function(){
					cxt.drawImage(img1,x,y,30,30);
				}
			}else if(bomb.blood>3){
				var img2=new Image();
				img2.src="picture/bomb_2.gif";
				var x=bomb.x;
				var y=bomb.y;
				img2.οnlοad=function(){
					cxt.drawImage(img2,x,y,30,30);
				}
			}else {
				var img3=new Image();
				img3.src="picture/bomb_3.gif";
				var x=bomb.x;
				var y=bomb.y;
				img3.οnlοad=function(){
					cxt.drawImage(img3,x,y,30,30);
				}
			}

			//减血
			bomb.bloodDown();
			if(bomb.blood<=0){
				//怎么办?把这个炸弹从数组中去掉
				bombs.splice(i,1);

			}
		}
	}
}

步骤七:响应键盘事件

function getCommand(){
		
		//我怎么知道,玩家按下的是什么键
		//说明当按下键后 事件--->event对象----->事件处理函数()
		var code=event.keyCode;//对应字母的ascii码->我们看码表
		
		switch(code){
			case 87://上
				hero.moveUp();
				
			   break;
			case 68:
			
			  hero.moveRight();
			   break;
			 case 83:
				
				hero.moveDown();
				break;
			case 65:
				hero.moveLeft();
				break;
			case 74:
				hero.shotEnemy();
				break;
		}


步骤八:页面刷新:

//专门写一个函数,用于定时刷新我们的作战区,把要在作战区出现的元素(自己坦克,敌人坦克,子弹,炸弹,
	//障碍物...)->游戏思想
	function flashTankMap(){
		
		//把画布清理
		cxt.clearRect(0,0,500,500); 

		//我的坦克
		drawTank(hero);

		//画出自己的子弹
		//子弹飞效果是怎么出现的?[答 : 首先我们应该每隔一定时间(setInterval)就去刷新作战区,如果在刷新的时候,子弹坐标变换了,给人的感觉就是子弹在飞!]
		drawHeroBullet();

		//敌人的坦克
		//判断一下敌人坦克是否击中
		isHitEnemyTank();
		drawEnemyBomb();
		drawEnemyBullet();
		
		//画出所有敌人坦克
		for(var i=0;i<3;i++){
			drawTank(enemyTanks[i]);
		}
			
	}


总结:坦克大战小游戏虽小但是涉及到的内容和思想却不少,在学习编程的过程中慢慢的体会编程思想非常重要。在此非常感谢韩顺平老师的视频!!!

本文涉及的完整代码工程请见:https://github.com/happyLiMing/TankGame

 


 

  • 9
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值