canvas+js实现简单的双人坦克对战小游戏

相信有很多人对坦克大战的游戏模仿很有兴趣,在实现经典的坦克大战之前,我先写了个双人的坦克对战,实现了基本的对战功能。下面就开始介绍游戏的编写。

首先创建一个基本的html文件,添加canvas标签以实现游戏的展示。

<!DOCTYPE html>
<html>
<head>
	<title>双人对战坦克大战</title>
	<meta charset="utf-8">
	<link rel="stylesheet" type="text/css" href="css/tank.css">
</head>
<body>
	<div class="homePage" id="home">
		<h1>坦克大战对战版</h1>
		<p class="startGame">游戏开始</p>
		<p>地图选择</p>
		<p>游戏规则</p>
	</div>
	<div class="gameStart" id="start">
		<canvas id="canvas" class="canvas">当前浏览器不支持canvas标签,请更换浏览器重新尝试</canvas>
	</div>
	<div class="picChoose" id="picCh">
		<p></p>
		<button type="button" class="startGame">开始游戏</button>
		<button type="button" class="back">回到首页</button>
	</div>
	<div class="gameRules" id="rule">
		<p>玩家双方操纵自己的坦克,被对方炮弹击中会失去一格生命,生命值为0即对方获胜</p>
		<p>道具&nbsp;&nbsp;T+:坦克变大&nbsp;T-:坦克变小&nbsp;H+:生命加一&nbsp;V+:坦克速度变快&nbsp;S+:炮弹速度变快&nbsp;S-:炮弹发射间隔减短</p>
		<button type="button" class="startGame">游戏开始</button>
		<button type="button" class="back">回到首页</button>
	</div>
	<script type="text/javascript" src="js/barrier.js"></script>
	<script type="text/javascript" src="js/tank.js"></script>
</body>
</html>

这里将canvas标签放入一个div中,同时设置了首页,地图的选择(在下文会实现该功能),和游戏规则,下面将通过js使几个div在不同的点击事件中显示。(这里不写出对应的css代码)

然后,在js中,我首先定义了各个变量,并封装了一个通过class获取节点的方法。

var canvas=document.getElementById('canvas');
var context=canvas.getContext('2d');
canvas.width=520;
canvas.height=520;
//获取各个div元素
var home=document.getElementById('home'),
	picCh=document.getElementById('picCh'),
	set=document.getElementById('set'),
	rule=document.getElementById('rule'),
	start=document.getElementById('start');
//定义原始位置记录
var oriposition=[
	{x:0,y:0},
	{x:canvas.width-20,y:canvas.height-40}
];
//定义坦克数组
var tanks=[//对应定义内容为坦克的x,y坐标,坦克的速度,坦克所在方格边长,坦克的生命,坦克的方向,坦克的炮弹发射冷却,坦克移动方向和发射炮弹对应的按键和状态,坦克炮弹发射速度,坦克炮弹冷却时间
	{x:oriposition[0].x,y:oriposition[0].y,v:5,l:20,h:3,d:3,i:0,up:[38,false],right:[39,false],down:[40,false],left:[37,false],shot:[32,false],t:6,inter:500},
	{x:oriposition[1].x,y:oriposition[1].y,v:5,l:20,h:3,d:1,i:0,up:[87,false],right:[68,false],down:[83,false],left:[65,false],shot:[192,false],t:6,inter:500}
];
//定义炮弹数组
var bullets=[];
//定义木头宽度
var woodLength=10;
//定义铁块宽度
var Felength=10;
//选择地图
var pic=0;
//定义道具标签
var randomProp=[{t:'T+',c:'rgb(102,204,204)'},{t:'T-',c:'rgb(0,204,51)'},{t:'H+',c:'rgb(204,102,0)'},{t:'V+',c:'rgb(204,0,204)'},{t:'S+',c:'rgb(102,102,102)'},{t:'S-',c:'rgb(204,153,204)'}];
//定义道具数组
var props=[];
//定义道具边长
var propLength=15;
//封装通过class获取节点方法
function getElementsByClassName(node,classname){
	if(node.getElementsByClassName){
		return node.getElementsByClassName(classname);
	}
	else{
		var results=new Array(),
			elems=node.getElementsByTagName('*');
		for(var i=0;i<elems.length;i++)
			if(elems[i].className.indexOf(classname)!=-1){
				results[results.length]=elems[i];
			}
	}
	return results;
}
window.onload=function(){
	clickDisplay();
	clickStart();
	clickBack();
	codeSet();
	setMaps();
}
//点击首页显示对应内容
function clickDisplay(){
	for(var i=0,len=home.getElementsByTagName('p').length;i<len;i++)
	{
		(function(i){
			home.getElementsByTagName('p')[i].onclick=function(){
				home.style.display='none';
				document.getElementsByTagName('div')[i+1].style.display='block';
			}
		})(i);
	}
}
//点击开始游戏
function clickStart(){
	for(var i=0,len=getElementsByClassName(document,'startGame').length;i<len;i++)
	{
		(function(i){
			getElementsByClassName(document,'startGame')[i].onclick=function(){
				this.parentNode.style.display='none';
				start.style.display='block';
				gameHandle();
				markProp();
				if(document.getElementById('modeChoose').getElementsByTagName('input')[1].checked)
					flagSymbol=true;
			}
		})(i);
	}
}
//点击返回首页
function clickBack(){
	for(var i=0,len=getElementsByClassName(document,'back').length;i<len;i++)
	{
		(function(i){
			getElementsByClassName(document,'back')[i].onclick=function(){
				this.parentNode.style.display='none';
				home.style.display='block';
			}
		})(i);
	}
}

首先在这里获取了对canvas的调用,设定了画布的宽高。封装了三个方法用于在不同的div之间切换。接下来就开始游戏的核心部分的编写了。

首先,在canvas中,我们要先画出游戏的界面,然后画出坦克的模型和炮弹的模型,这里我将方法添加到context的原型对象中,分别写了绘制圆角矩形,坦克,炮弹的方法。

//定义画出圆角矩形的原型方法
CanvasRenderingContext2D.prototype.cirRct=function(x,y,w,l,r,color)//矩形所在的x,y坐标,宽高,圆角半径,颜色
{
	this.beginPath();
	this.moveTo(x+r,y);
	this.lineTo(x+w-r,y);
	this.arcTo(x+w,y,x+w,y+r,r);
	this.lineTo(x+w,y+l-r);
	this.arcTo(x+w,y+l,x+w-r,y+l,r);
	this.lineTo(x+r,y+l);
	this.arcTo(x,y+l,x,y+l-r,r);
	this.lineTo(x,y+r);
	this.arcTo(x,y,x+r,y,r);
	this.fillStyle=color;
	this.closePath();
	this.fill();
	this.stroke();
}
//定义画出坦克的原型方法
CanvasRenderingContext2D.prototype.drawTank=function(x,y,l,d)//坦克所在的x,y坐标,坦克所在方格大小,坦克的方向
{
	var l1=0.5*l,w1=0.2*l,l2=0.4*l,w2=0.3*l,l3=0.2*l,w3=0.2*l,l4=0.6*l,w4=0.3*l;
	var spaceW=l-w2-2*w4;
	if(d==1){
		//画出两个车轮
		this.cirRct(x+spaceW/2,y+l-l4,w4,l4,0,'rgb(204,102,51)');
		this.cirRct(x+(l+w2)/2,y+l-l4,w4,l4,0,'rgb(204,102,51)');
		//画出炮口
		this.cirRct(x+spaceW/2+w4+(w2-w3)/2,y,w1,l1,0,'rgb(0,255,0)');
		//画出炮身
		this.cirRct(x+spaceW/2+w4,y+l1,w2,l2,0,'rgb(255,102,0)');
		//画出炮舱
		this.cirRct(x+spaceW/2+w4+(w2-w3)/2,y+l1+(l2-l3)/2,w3,l3,0,'rgb(204,102,51)');
	}
	else if(d==2){
		this.cirRct(x,y+spaceW/2,l4,w4,0,'rgb(204,102,51)');
		this.cirRct(x,y+spaceW/2+w2+w4,l4,w4,0,'rgb(204,102,51)');
		this.cirRct(x+l-l1,y+spaceW/2+w4,l1,w1,0,'rgb(0,255,0)');
		this.cirRct(x+l-l1-l2,y+spaceW/2+w4,l2,w2,0,'rgb(255,102,0)');
		this.cirRct(x+l-l1-(l2+l3)/2,y+spaceW/2+w4+(w2-w3)/2,l3,w3,0,'rgb(204,102,51)');
	}
	else if(d==3){
		this.cirRct(x+spaceW/2,y,w4,l4,0,'rgb(204,102,51)');
		this.cirRct(x+(l+w2)/2,y,w4,l4,0,'rgb(204,102,51)');
		this.cirRct(x+spaceW/2+w4+(w2-w3)/2,y+l-l1,w1,l1,0,'rgb(0,255,0)');
		this.cirRct(x+spaceW/2+w4,y+l-l1-l2,w2,l2,0,'rgb(255,102,0)');
		this.cirRct(x+spaceW/2+w4+(w2-w3)/2,y+l-l1-(l2+l3)/2,w3,l3,0,'rgb(204,102,51)');
	}
	else if(d==4){
		this.cirRct(x+l-l4,y+spaceW/2,l4,w4,0,'rgb(204,102,51)');
		this.cirRct(x+l-l4,y+(l+w2)/2,l4,w4,0,'rgb(204,102,51)');
		this.cirRct(x,y+spaceW/2+w4+(w2-w3)/2,l1,w1,0,'rgb(0,255,0)');
		this.cirRct(x+l1,y+spaceW/2+w4,l2,w2,0,'rgb(255,102,0)');
		this.cirRct(x+l1+(l2-l3)/2,y+spaceW/2+w4+(w2-w3)/2,l3,w3,0,'rgb(204,102,51)');
	}
}
//画出炮弹
CanvasRenderingContext2D.prototype.drawBullets=function(x,y,l,color){//炮弹的x,y坐标,炮弹的边长,炮弹的颜色
	this.cirRct(x,y,l,l,0,color);
}

在绘制坦克的过程中,我对应每个方向画了一个坦克模型。为了后面的道具添加方便,在控制坦克的大小上我只传入了一个参数l。

这里我封装了一个游戏开始的方法,每0.04秒重绘一次画面,使其生成动画效果,里面的具体方法将在下文解释。

//游戏开始
function gameHandle(){
	codeChange();
	setInterval(function(){
		codeHandle(0);
		codeHandle(1);
		context.clearRect(0,0,canvas.width,canvas.height);
		context.cirRct(0,0,canvas.width,canvas.height,0,'rgb(0,0,0)');
		woodContrust();
		FeContrust();
		for(var i=0;i<tanks.length;i++)
		{
			if(props.length>0)
			{
				context.drawProp(props[0].x,props[0].y,propLength,props[0].t);
			}
			judgeProps(i);
			if(flagSymbol){
				context.drawFlag(flags[i].x,flags[i].y,flagRct);
			}
			context.drawTank(tanks[i].x,tanks[i].y,tanks[i].l,tanks[i].d);
		}
		for(var j=0;j<bullets.length;j++)
		{
			context.drawBullets(bullets[j].x,bullets[j].y,bullets[j].l,'rgb(255,0,0)');
		}
	},40);
}

接下来就是坦克和炮弹的移动了,坦克的移动由方向键控制,而炮弹的移动可以在按下炮弹键后由坦克的炮口位置为起点移动。如果直接使用onkeydown事件判断按下按键的键码并使坦克移动,会出现坦克在移动时发射炮弹会停下,转换方向时也会停顿一下,甚至无法同时移动两只坦克,也即是总是会触发后按下的事件。为了解决这一问题,我同时使用了keydown事件和keyup事件,在上面定义变量中我对坦克中每个方向和炮弹发射键对应设置了一个对象,其中有对应的键码和方向状态(默认为false),在触发键码对应的keydown事件时,将对应的坦克的方向的状态改为true,炮弹也一样,在触发对应的keyup事件时修改回false停止移动,同时按下多个方向键时,会将按下方向键之外的三个方向键状态全改为false,以防止坦克斜向移动。然后封装一个方法,在对应的方向,炮弹对应状态为true时改变坦克坦克坐标和发射炮弹。发射炮弹通过封装方法bulletsMove实现,而shotInterval用于实现坦克的发射冷却。(代码中的judgebarrier方法将在下文介绍)

//按键按下对应改变按键状态
function codeChange(){
	document.onkeydown=function(){
		var e=event||window.event;
		for(var i=0;i<tanks.length;i++){
			(function(i){
				if(e.keyCode==tanks[i].left[0])
				{
					codeReset(i);
					tanks[i].left[1]=true;
				}
				else if(e.keyCode==tanks[i].up[0])
				{
					codeReset(i);
					tanks[i].up[1]=true;
				}
				else if(e.keyCode==tanks[i].right[0])
				{
					codeReset(i);
					tanks[i].right[1]=true;
				}
				else if(e.keyCode==tanks[i].down[0])
				{
					codeReset(i);
					tanks[i].down[1]=true;
				}
				if ((e.keyCode==tanks[i].shot[0])&&(tanks[i].i==0)) {
					tanks[i].shot[1]=true;
				}
			})(i);
		}
	}
	document.onkeyup=function(){
		var e=event||window.event;
		for(var i=0;i<tanks.length;i++){
			(function(i){
				if(e.keyCode==tanks[i].left[0])
				{
					tanks[i].left[1]=false;
				}
				else if(e.keyCode==tanks[i].up[0])
				{
					tanks[i].up[1]=false;
				}
				else if(e.keyCode==tanks[i].right[0])
				{
					tanks[i].right[1]=false;
				}
				else if(e.keyCode==tanks[i].down[0])
				{
					tanks[i].down[1]=false;
				}
				if (e.keyCode==tanks[i].shot[0]) {
					tanks[i].shot[1]=false;
				}
			})(i);
		}
	}
}
//按键对应操作
function codeHandle(i){
	if(tanks[i].left[1]==true)
	{
		tanks[i].x-=tanks[i].v;
		tanks[i].d=4;
		if(tanks[i].x-tanks[i].v<0)
			tanks[i].x=0;
		if(judgebarrier(tanks[i].x,tanks[i].y,tanks[i].l)!=-1)
			tanks[i].x+=tanks[i].v;
	}
	else if(tanks[i].up[1]==true)
	{
		tanks[i].y-=tanks[i].v;
		tanks[i].d=1;
		if(tanks[i].y-tanks[i].v<0)
			tanks[i].y=0;
		if(judgebarrier(tanks[i].x,tanks[i].y,tanks[i].l)!=-1)
			tanks[i].y+=tanks[i].v;
	}
	else if(tanks[i].right[1]==true)
	{
		tanks[i].x+=tanks[i].v;
		tanks[i].d=2;
		if(tanks[i].x+tanks[i].v>canvas.width-tanks[i].l)
			tanks[i].x=canvas.width-tanks[i].l;
		if(judgebarrier(tanks[i].x,tanks[i].y,tanks[i].l)!=-1)
			tanks[i].x-=tanks[i].v;
	}
	else if(tanks[i].down[1]==true)
	{
		tanks[i].y+=tanks[i].v;
		tanks[i].d=3;
		if(tanks[i].y+tanks[i].v>canvas.width-tanks[i].l)
			tanks[i].y=canvas.width-tanks[i].l;
		if(judgebarrier(tanks[i].x,tanks[i].y,tanks[i].l)!=-1)
			tanks[i].y-=tanks[i].v;
	}
	if ((tanks[i].shot[1]==true)&&(tanks[i].i==0)) {
		tanks[i].i=1;
		shotInterval(i);
		bullets[bullets.length]={x:tanks[i].x+tanks[i].l*3/8,y:tanks[i].y+tanks[i].l*3/8,t:tanks[i].t,l:tanks[i].l/4,belong:i}
		if(tanks[i].d==1)
		{
			bullets[bullets.length-1].y=tanks[i].y-tanks[i].l/4;
			bulletsMove(0,-2,bullets[bullets.length-1],bullets.length-1,bullets[bullets.length-1].t);
		}
		else if(tanks[i].d==2)
		{
			bullets[bullets.length-1].x=tanks[i].x+tanks[i].l;
			bulletsMove(2,0,bullets[bullets.length-1],bullets.length-1,bullets[bullets.length-1].t);
		}
		else if(tanks[i].d==3)
		{
			bullets[bullets.length-1].y=tanks[i].y+tanks[i].l;
			bulletsMove(0,2,bullets[bullets.length-1],bullets.length-1,bullets[bullets.length-1].t);
		}
		else if(tanks[i].d==4)
		{
			bullets[bullets.length-1].x=tanks[i].x-tanks[i].l/4;
			bulletsMove(-2,0,bullets[bullets.length-1],bullets.length-1,bullets[bullets.length-1].t);
		}
	}
}
//炮弹的移动
function bulletsMove(vx,vy,bullet,index,t){//炮弹在x,y上的速度,要移动的炮弹,炮弹在数组中的下标,每隔t毫秒移动一次炮弹
	setTimeout(function(){
		bullet.x+=vx;
		bullet.y+=vy;
		judgeWin();
		for(var i=0;i<bullets.length;i++)
		{
			if(index!=i)
			{
				if(bullet.l>=bullets[i].l)
				{
					if(judgeIn(bullets[i].x,bullets[i].y,bullet.x,bullet.y,bullet.l)||judgeIn(bullets[i].x+bullets[i].l,bullets[i].y,bullet.x,bullet.y,bullet.l)||judgeIn(bullets[i].x,bullets[i].y+bullets[i].l,bullet.x,bullet.y,bullet.l)||judgeIn(bullets[i].x+bullets[i].l,bullets[i].y+bullets[i].l,bullet.x,bullet.y,bullet.l))
					{
						vx=0;
						vy=0;
						bullet.x=0-bullet.l-index;
						bullets[i].x=canvas.width+bullets[i].l+index;
					}
				}
				else
				{
					if(judgeIn(bullet.x,bullet.y,bullets[i].x,bullets[i].y,bullets[i].l)||judgeIn(bullet.x+bullet.l,bullet.y,bullets[i].x,bullets[i].y,bullets[i].l)||judgeIn(bullet.x,bullet.y+bullet.l,bullets[i].x,bullets[i].y,bullets[i].l)||judgeIn(bullet.x+bullet.l,bullet.y+bullet.l,bullets[i].x,bullets[i].y,bullets[i].l))
					{
						vx=0;
						vy=0;
						bullet.x=0-bullet.l-index;
						bullets[i].x=canvas.width+bullets[i].l+index;
					}
				}
			}
		}
		if((judgebarrier(bullet.x,bullet.y,bullet.l)!=-1)&&(judgebarrier(bullet.x,bullet.y,bullet.l)!=-2))
		{
			var i=judgebarrier(bullet.x,bullet.y,bullet.l);
			woods[pic].splice(i,1);
			bullet.x=canvas.width+bullet.l;
			vx=0;
			vy=0;
		}
		else if(bullet.x>canvas.width||bullet.x<0||bullet.y>canvas.width||bullet.y<0||judgebarrier(bullet.x,bullet.y,bullet.l)==-2)
		{
			bullet.x=canvas.width+bullet.l;
			vx=0;
			vy=0;
		}
		else
		{
			if((vx!=0)||(vy!=0))
				bulletsMove(vx,vy,bullet,index,t);
		}
	},t);
}
//射击间隔
function shotInterval(i){//传入对应坦克编号
	setTimeout(function(){
		tanks[i].i--;
		if(tanks[i].i!=0)
			shotInterval(i);
	},tanks[i].inter);
}

在完成了坦克的移动和炮弹的发射后,在之前的编写中,坦克始终是在空白的地图中进行移动的,这里要添加新的地图,通过两种不同的障碍物来填充地图,一种可以被炮弹消灭(木块),一种不可以被炮弹消灭(铁块)。这里我通过数组对象储存障碍物对应得位置,也方便后面小地图的实现以选择地图。

var woods=[
	[],
	[{x:25,y:26},{x:24,y:26},{x:23,y:26},{x:22,y:26},{x:27,y:26},{x:28,y:26},{x:29,y:26},{x:30,y:26},{x:26,y:25},{x:26,y:24},{x:26,y:23},{x:26,y:22},{x:26,y:27},{x:26,y:28},{x:26,y:29},{x:26,y:30},{x:11,y:12},{x:10,y:12},{x:9,y:12},{x:8,y:12},{x:13,y:12},{x:14,y:12},{x:15,y:12},{x:16,y:12},{x:12,y:11},{x:12,y:10},{x:12,y:9},{x:12,y:8},{x:12,y:13},{x:12,y:14},{x:12,y:15},{x:12,y:16},{x:39,y:12},{x:38,y:12},{x:37,y:12},{x:36,y:12},{x:41,y:12},{x:42,y:12},{x:43,y:12},{x:44,y:12},{x:40,y:11},{x:40,y:10},{x:40,y:9},{x:40,y:8},{x:40,y:13},{x:40,y:14},{x:40,y:15},{x:40,y:16},{x:39,y:40},{x:38,y:40},{x:37,y:40},{x:36,y:40},{x:41,y:40},{x:42,y:40},{x:43,y:40},{x:44,y:40},{x:40,y:39},{x:40,y:38},{x:40,y:37},{x:40,y:36},{x:40,y:41},{x:40,y:42},{x:40,y:43},{x:40,y:44},{x:11,y:40},{x:10,y:40},{x:9,y:40},{x:8,y:40},{x:13,y:40},{x:14,y:40},{x:15,y:40},{x:16,y:40},{x:12,y:39},{x:12,y:38},{x:12,y:37},{x:12,y:36},{x:12,y:41},{x:12,y:42},{x:12,y:43},{x:12,y:44}
	],
	[],
	[{x:11,y:0},{x:11,y:1},{x:11,y:2},{x:11,y:3},{x:11,y:4},{x:11,y:5},{x:11,y:6},{x:11,y:7},{x:11,y:8},{x:11,y:9},{x:11,y:10},{x:11,y:12},{x:11,y:13},{x:11,y:14},{x:11,y:15},{x:11,y:16},{x:11,y:17},{x:11,y:18},{x:11,y:19},{x:11,y:20},{x:11,y:22},{x:11,y:23},{x:11,y:24},{x:11,y:25},{x:11,y:26},{x:11,y:27},{x:11,y:28},{x:11,y:29},{x:11,y:30},{x:11,y:32},{x:11,y:33},{x:11,y:34},{x:11,y:35},{x:11,y:36},{x:11,y:37},{x:11,y:38},{x:11,y:39},{x:11,y:40},{x:11,y:42},{x:11,y:43},{x:11,y:44},{x:11,y:45},{x:31,y:0},{x:31,y:1},{x:31,y:2},{x:31,y:3},{x:31,y:4},{x:31,y:5},{x:31,y:6},{x:31,y:7},{x:31,y:8},{x:31,y:9},{x:31,y:10},{x:31,y:12},{x:31,y:13},{x:31,y:14},{x:31,y:15},{x:31,y:16},{x:31,y:17},{x:31,y:18},{x:31,y:19},{x:31,y:20},{x:31,y:22},{x:31,y:23},{x:31,y:24},{x:31,y:25},{x:31,y:26},{x:31,y:27},{x:31,y:28},{x:31,y:29},{x:31,y:30},{x:31,y:32},{x:31,y:33},{x:31,y:34},{x:31,y:35},{x:31,y:36},{x:31,y:37},{x:31,y:38},{x:31,y:39},{x:31,y:40},{x:31,y:42},{x:31,y:43},{x:31,y:44},{x:31,y:45},{x:21,y:7},{x:21,y:8},{x:21,y:9},{x:21,y:10},{x:21,y:12},{x:21,y:13},{x:21,y:14},{x:21,y:15},{x:21,y:16},{x:21,y:17},{x:21,y:18},{x:21,y:19},{x:21,y:20},{x:21,y:22},{x:21,y:23},{x:21,y:24},{x:21,y:25},{x:21,y:26},{x:21,y:27},{x:21,y:28},{x:21,y:29},{x:21,y:30},{x:21,y:32},{x:21,y:33},{x:21,y:34},{x:21,y:35},{x:21,y:36},{x:21,y:37},{x:21,y:38},{x:21,y:39},{x:21,y:40},{x:21,y:42},{x:21,y:43},{x:21,y:44},{x:21,y:45},{x:21,y:46},{x:21,y:47},{x:21,y:48},{x:21,y:49},{x:21,y:50},{x:21,y:51},{x:21,y:52},{x:41,y:7},{x:41,y:8},{x:41,y:9},{x:41,y:10},{x:41,y:12},{x:41,y:13},{x:41,y:14},{x:41,y:15},{x:41,y:16},{x:41,y:17},{x:41,y:18},{x:41,y:19},{x:41,y:20},{x:41,y:22},{x:41,y:23},{x:41,y:24},{x:41,y:25},{x:41,y:26},{x:41,y:27},{x:41,y:28},{x:41,y:29},{x:41,y:30},{x:41,y:32},{x:41,y:33},{x:41,y:34},{x:41,y:35},{x:41,y:36},{x:41,y:37},{x:41,y:38},{x:41,y:39},{x:41,y:40},{x:41,y:42},{x:41,y:43},{x:41,y:44},{x:41,y:45},{x:41,y:46},{x:41,y:47},{x:41,y:48},{x:41,y:49},{x:41,y:50},{x:41,y:51},{x:41,y:52}
	]
];
var Fes=[
	[],
	[{x:26,y:26},{x:12,y:12},{x:40,y:40},{x:12,y:40},{x:40,y:12}
	],
	[{x:25,y:26},{x:24,y:26},{x:23,y:26},{x:22,y:26},{x:27,y:26},{x:28,y:26},{x:29,y:26},{x:30,y:26},{x:26,y:25},{x:26,y:24},{x:26,y:23},{x:26,y:22},{x:26,y:27},{x:26,y:28},{x:26,y:29},{x:26,y:30},{x:11,y:12},{x:10,y:12},{x:9,y:12},{x:8,y:12},{x:13,y:12},{x:14,y:12},{x:15,y:12},{x:16,y:12},{x:12,y:11},{x:12,y:10},{x:12,y:9},{x:12,y:8},{x:12,y:13},{x:12,y:14},{x:12,y:15},{x:12,y:16},{x:39,y:12},{x:38,y:12},{x:37,y:12},{x:36,y:12},{x:41,y:12},{x:42,y:12},{x:43,y:12},{x:44,y:12},{x:40,y:11},{x:40,y:10},{x:40,y:9},{x:40,y:8},{x:40,y:13},{x:40,y:14},{x:40,y:15},{x:40,y:16},{x:39,y:40},{x:38,y:40},{x:37,y:40},{x:36,y:40},{x:41,y:40},{x:42,y:40},{x:43,y:40},{x:44,y:40},{x:40,y:39},{x:40,y:38},{x:40,y:37},{x:40,y:36},{x:40,y:41},{x:40,y:42},{x:40,y:43},{x:40,y:44},{x:11,y:40},{x:10,y:40},{x:9,y:40},{x:8,y:40},{x:13,y:40},{x:14,y:40},{x:15,y:40},{x:16,y:40},{x:12,y:39},{x:12,y:38},{x:12,y:37},{x:12,y:36},{x:12,y:41},{x:12,y:42},{x:12,y:43},{x:12,y:44}
	],
	[{x:11,y:11},{x:11,y:21},{x:11,y:31},{x:11,y:41},{x:21,y:11},{x:21,y:21},{x:21,y:31},{x:21,y:41},{x:31,y:11},{x:31,y:21},{x:31,y:31},{x:31,y:41},{x:41,y:11},{x:41,y:21},{x:41,y:31},{x:41,y:41}
	]
]

在这里我设置了四张地图,紧接着,我封装了几个方法以绘制木块和铁块,同时封装方法判断坦克或炮弹是否撞上障碍物。

//建筑木块
function woodContrust(){
	for(var i=0,len=woods[pic].length;i<len;i++)
	{
		context.cirRct(woods[pic][i].x*woodLength,woods[pic][i].y*woodLength,woodLength,woodLength,0,'rgb(255,204,51)');
	}
}
//建筑铁块
function FeContrust(){
	for(var i=0,len=Fes[pic].length;i<len;i++)
	{
		context.cirRct(Fes[pic][i].x*Felength,Fes[pic][i].y*Felength,Felength,Felength,0,'rgb(102,102,102)');
	}
}
//判断是否撞上障碍物
function judgebarrier(x,y,l)
{
	if(l>=woodLength)
	{
		for(var i=0,len=woods[pic].length;i<len;i++)
		{
			if(judgeIn(woods[pic][i].x*woodLength,woods[pic][i].y*woodLength,x,y,l)||judgeIn(woods[pic][i].x*woodLength+woodLength,woods[pic][i].y*woodLength,x,y,l)||judgeIn(woods[pic][i].x*woodLength,woods[pic][i].y*woodLength+woodLength,x,y,l)||judgeIn(woods[pic][i].x*woodLength+woodLength,woods[pic][i].y*woodLength+woodLength,x,y,l))
				return i;
		}
	}
	else if(l<woodLength)
	{
		for(var i=0,len=woods[pic].length;i<len;i++)
		{
			if(judgeIn(x,y,woods[pic][i].x*woodLength,woods[pic][i].y*woodLength,woodLength)||judgeIn(x+l,y,woods[pic][i].x*woodLength,woods[pic][i].y*woodLength,woodLength)||judgeIn(x,y+l,woods[pic][i].x*woodLength,woods[pic][i].y*woodLength,woodLength)||judgeIn(x+l,y+l,woods[pic][i].x*woodLength,woods[pic][i].y*woodLength,woodLength))
				return i;
		}
	}
	if(l>=Felength)
	{
		for(var i=0,len=Fes[pic].length;i<len;i++)
		{
			if(judgeIn(Fes[pic][i].x*Felength,Fes[pic][i].y*Felength,x,y,l)||judgeIn(Fes[pic][i].x*Felength+Felength,Fes[pic][i].y*Felength,x,y,l)||judgeIn(Fes[pic][i].x*Felength,Fes[pic][i].y*Felength+Felength,x,y,l)||judgeIn(Fes[pic][i].x*Felength+Felength,Fes[pic][i].y*Felength+Felength,x,y,l))
				return -2;
		}
	}
	else
	{
		for(var i=0,len=Fes[pic].length;i<len;i++)
		{
			if(judgeIn(x,y,Fes[pic][i].x*Felength,Fes[pic][i].y*Felength,Felength)||judgeIn(x+l,y,Fes[pic][i].x*Felength,Fes[pic][i].y*Felength,Felength)||judgeIn(x,y+l,Fes[pic][i].x*Felength,Fes[pic][i].y*Felength,Felength)||judgeIn(x+l,y+l,Fes[pic][i].x*Felength,Fes[pic][i].y*Felength,Felength))
				return -2;
		}
	}
	return -1;
}
//判断点是否在方块内
function judgeIn(x,y,x1,y1,l){//x1,y1为左上角的点,x2,y2为右下角的点
	if((x>=x1)&&(x<=x1+l)&&(y>=y1)&&(y<=y1+l))
		return true;
	else
		false;
}

完成了地图的设置后,我们需要在首页部分选择地图,为了同步化我在woods和Fes数组中设置的位置,我在首页中通过创建canvas标签传入对应的障碍物位置以供选择地图。

//创建小地图
function setMaps(){
	var txt=document.createTextNode('当前浏览器不支持canvas,请换取较高级浏览器尝试该游戏');
	var canvasArray=[];
	var contextArray=[];
	for(var i=0;i<woods.length;i++)
	{
		canvasArray[i]=document.createElement('canvas');
		canvasArray[i].appendChild(txt);
		picCh.getElementsByTagName('p')[0].appendChild(canvasArray[i]);
		canvasArray[i].width=104;
		canvasArray[i].height=104;
		contextArray[i]=canvasArray[i].getContext('2d');
		contextArray[i].cirRct(0,0,canvasArray[i].width,canvasArray[i].height,0,'rgb(0,0,0,0.8)');
		for(var j=0;j<woods[i].length;j++)
		{
			contextArray[i].cirRct(woods[i][j].x*2,woods[i][j].y*2,2,2,0,'rgb(255,204,51)');
		}
		for(var k=0;k<Fes[i].length;k++)
		{
			contextArray[i].cirRct(Fes[i][k].x*2,Fes[i][k].y*2,2,2,0,'rgb(102,102,102)');
		}
	}
	//点击更换地图
	for(var i=0;i<document.getElementById('picCh').getElementsByTagName('canvas').length;i++)
	{
		(function(i){
			document.getElementById('picCh').getElementsByTagName('canvas')[i].onclick=function(){
				pic=i;
				for(var j=0;j<document.getElementById('picCh').getElementsByTagName('canvas').length;j++)
				{
					document.getElementById('picCh').getElementsByTagName('canvas')[j].style.filter='brightness(100%)';
					document.getElementById('picCh').getElementsByTagName('canvas')[j].style.border='none';
				}
				this.style.border='2px solid rgb(255,0,0)';
				this.style.filter='brightness(200%)';
			}
		})(i)
	}
}

在这里别忘了还没有设置游戏的输赢判断,这里我用了一个judgeWin方法来实现这一功能,由于这个游戏中只有两只坦克,所以我直接传入0,1来对对应的坦克进行操作,在中弹后坦克回到起始位置,生命值减一,当生命值小于0时游戏结束。

//判断输赢
function judgeWin(){
	for(var i=0;i<bullets.length;i++)
	{
		if((judgeIn(bullets[i].x,bullets[i].y,tanks[0].x,tanks[0].y,tanks[0].l)||judgeIn(bullets[i].x+bullets[i].l,bullets[i].y,tanks[0].x,tanks[0].y,tanks[0].l)||judgeIn(bullets[i].x,bullets[i].y+bullets[i].l,tanks[0].x,tanks[0].y,tanks[0].l)||judgeIn(bullets[i].x+bullets[i].l,bullets[i].y+bullets[i].l,tanks[0].x,tanks[0].y,tanks[0].l))&&bullets[i].belong==1)
		{
			if(tanks[0].h==0){
				alert('player2 win');
				tanks[0].v=0;
			}
			tanks[0].h--;
			tanks[0].x=oriposition[0].x;
			tanks[0].y=oriposition[0].y;
			bullets.splice(i,1);
		}
		else if((judgeIn(bullets[i].x,bullets[i].y,tanks[1].x,tanks[1].y,tanks[1].l)||judgeIn(bullets[i].x+bullets[i].l,bullets[i].y,tanks[1].x,tanks[1].y,tanks[1].l)||judgeIn(bullets[i].x,bullets[i].y+bullets[i].l,tanks[1].x,tanks[1].y,tanks[1].l)||judgeIn(bullets[i].x+bullets[i].l,bullets[i].y+bullets[i].l,tanks[1].x,tanks[1].y,tanks[1].l))&&bullets[i].belong==0)
		{	
			if(tanks[1].h==0){
				alert('player1 win');
				tanks[1].v=0;
			}
			tanks[1].h--;
			tanks[1].x=oriposition[1].x;
			tanks[1].y=oriposition[1].y;
			bullets.splice(i,1);
		}
	}
}

这样就实现了基本的坦克对战了,为了增加游戏的趣味性,我添加了几个道具。

具体的道具标记方法和生成方法如下:

//画出道具
CanvasRenderingContext2D.prototype.drawProp=function(x,y,l,index){//道具所在的坐标和边长,对应道具类型下标
	this.cirRct(x,y,l,l,1,randomProp[index].c);
	this.fillStyle='rgb(0,0,0)';//设置字体颜色
	this.textAlign='center';
	this.textBaseline='middle';
	this.font='8px Adobe Ming Std';
	this.fillText(randomProp[index].t,x+l/2,y+l/2);
}
//标记道具
function markProp(){
	setTimeout(function(){
		var randomx=Math.floor(Math.random()*(520-propLength)),
			randomy=Math.floor(Math.random()*(520-propLength)),
			randomt=Math.floor(Math.random()*randomProp.length);
		props[props.length]={x:randomx,y:randomy,t:randomt};
		setTimeout(function(){props.length=0},5000);
		markProp();
	},5000);
}
//判断是否碰到道具
function judgeProps(index){//传入坦克下标
	for(var i=0,len=props.length;i<len;i++)
	{
		if(tanks[index].l>=propLength)
		{
			if(judgeIn(props[0].x,props[0].y,tanks[index].x,tanks[index].y,tanks[index].l)||judgeIn(props[0].x+propLength,props[0].y,tanks[index].x,tanks[index].y,tanks[index].l)||judgeIn(props[0].x,props[0].y+propLength,tanks[index].x,tanks[index].y,tanks[index].l)||judgeIn(props[0].x+propLength,props[0].y+propLength,tanks[index].x,tanks[index].y,tanks[index].l))
			{
				propsApply(index);
				props.length=0;
			}
		}
		else
		{
			if(judgeIn(tanks[index].x,tanks[index].y,props[0].x,props[0].y,propLength)||judgeIn(tanks[index].x+tanks[index].l,tanks[index].y,props[0].x,props[0].y,propLength)||judgeIn(tanks[index].x,tanks[index].y+tanks[index].l,props[0].x,props[0].y,propLength)||judgeIn(tanks[index].x+tanks[index].l,tanks[index].y+tanks[index].l,props[0].x,props[0].y,propLength))
			{
				propsApply(index);
				props.length=0;
			}
		}

	}
}
//道具生效
function propsApply(index)
{
	if(props[0].t==0)
	{
		if(tanks[index].l<=40)
		{
			tanks[index].l+=5;
		}
	}
	else if(props[0].t==1)
	{
		if(tanks[index].l>=15)
		{
			tanks[index].l-=5;
		}
	}
	else if(props[0].t==2)
	{
		if(tanks[index].h<=4)
			tanks[index].h++;
	}
	else if(props[0].t==3)
	{
		if(tanks[index].v<=8)
			tanks[index].v++;
	}
	else if(props[0].t==4)
	{
		if(tanks[index].t>4)
			tanks[index].t--;
	}
	else if(props[0].t==5)
	{
		if(tanks[index].inter>=300)
			tanks[index].inter-=50;
	}
}

这里通过随机数选出几个道具中的一种,每5秒生成一次道具,如道具没被坦克触碰,则在5秒后会消失。对应的道具效果在html中的规则已写出,可以结合代码理解内容。

以上就是简单版的双人坦克对战的编写,大家可以通过改变其中的一些参数,制作一些新的道具,添加一些声音,相信会变成一个有趣的游戏。在熟悉这些内容后也可以尝试制作AI版的坦克大战,相信可以学到很多东西。

希望本篇博文对大家有所帮助,也希望各位大神不吝赐教,指出我代码中的不足,谢谢大家。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值