canvas+js实现简单的坦克大战小游戏

使用canvas和js实现经典的坦克大战并不难,坦克和炮弹的绘制可以使用canvas的里API画出,而坦克的移动和炮弹的发射可以依赖于键盘事件和定时器来实现。

在这里我要实现的坦克大战是双人模式,有木块(能被炮弹摧毁)和铁块(不能被炮弹摧毁)两种障碍物,有一些简单的小道具,摧毁一定数量的坦克就能获得胜利,下面就着手来实现这个坦克大战。

首先我们需要在html中创建一个canvas标签,以显示游戏的界面。canavs标签中的文字会在浏览器不支持canvas标签时显示,在支持的浏览器中该段文字无效。

<!DOCTYPE html>
<html>
<head>
	<title>坦克大战</title>
	<meta charset="utf-8">
</head>
<body>
	<canvas id="canvas" class="canvas">当前浏览器不支持canvas标签,请更换浏览器重新尝试</canvas>
	<script type="text/javascript" src="js/barrier.js"></script>
	<script type="text/javascript" src="js/tankAI.js"></script>
</body>
</html>

在barrier.js文件中,我定义了各个障碍物的位置,每个障碍物对应几个数组,并定义了pic来选择对应的数组。之后可以通过在代码中修改这个值来更换地图,也可以添加另外的html标签配合事件来改变pic的值达到更换地图的效果。

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: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: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}
	]
];
var protects=[
	{x:25,y:51},{x:25,y:50},{x:25,y:49},{x:26,y:49},{x:27,y:49},{x:28,y:49},{x:29,y:49},{x:29,y:51},{x:29,y:50}
]
//初始化地图选取
var pic=0;

anksAI.js文件中,我们首先获取容器,定义容器的宽高,设置坦克的模型,定义储存炮弹信息的数组,定义木块,铁块,道具,旗的边长,初始化地图的选择,设置道具的类型。

var canvas=document.getElementById('canvas');
var context=canvas.getContext('2d');
canvas.width=520;
canvas.height=520;
//定义坦克初始位置
var tanksPosition=[
	{x:220,y:canvas.height-20},
	{x:310,y:canvas.height-20}
]
//定义坦克数组
var tanks=[
	{x:220,y:canvas.height-20,v:5,l:20,h:3,d:1,i:0,up:[38,false],right:[39,false],down:[40,false],left:[37,false],shot:[32,false],t:6,inter:500,color:'rgb(0,204,204)'},
	{x:310,y:canvas.height-20,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,color:'rgb(0,255,0)'}
];
//x,y:坦克坐标;v:坦克移动的速度,l:坦克的边长,h:坦克的生命值,d:坦克的方向(1:上,2:右,3:下,4:左),i:坦克是否可以发射炮弹(0:是,1:否),up,right,down,left,shot:坦克各方向按键对应键码及按键状态,t:炮弹每t毫秒移动一次,inter:坦克发射炮弹的时间间隔,color:坦克颜色
//初始化摧毁数
var kills=0;
var kill=[0,0]
//定义炮弹数组
var bullets=[];
//定义木头宽度
var woodLength=10;
//定义铁块宽度
var Felength=10;
//定义旗的宽度
var flagLength=15;
//定义道具边长
var propLength=15;
//定义不同的坦克AI
var tankAiModel=[
	{v:6,h:2,l:25,inter:1000,t:10,color:'rgb(255,0,0)'},//炮弹t毫秒移动一次
	{v:10,h:0,l:15,inter:1000,t:4,color:'rgb(204,204,204)'},
	{v:5,h:0,l:20,inter:1000,t:8,color:'rgb(204,204,204)'}
];
//初始化坦克同一时间出现数量
var tankDisplay=5;
//初始化胜利所需摧毁的坦克数
var wins=15;
//游戏结束声明
var end=false;
//储存坦克AI的数组
var tanksAi=[];
//定义道具标签
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)'}];
//道具T+:坦克变大T-:坦克变小H+:生命加一V+:坦克速度变快S+:炮弹速度变快S-:炮弹发射间隔减短
//定义道具数组
var props=[];

因为keydown事件每次只判断一个按键是否被按下,为了使坦克在移动过程中能发射炮弹,对每个按键对应设置一个布尔值,另外写一个方法来判断该布尔值的值为true或false,为true时就执行对应的事件,即移动和发射炮弹,触发keydown事件时对应布尔值变为true,触发keyup事件时对应布尔值变为false,为了使每次的移动只能有一个方向,在每次按下一个按键时,应先把该坦克对应按键的所有方向所对应的布尔值全部变为false。

在发射炮弹时,玩家的炮弹不能连续发射,两次发射需要有一定的时间间隔,这个时间间隔我赋值给tanks中的inter属性,通过定时器来改变tanks.i的值。

而在坦克的炮弹的移动中,因为玩家的坦克的移动和射击是通过按键按下,而电脑的坦克是由计时器不断调用自己的方法实现的,所以用不同的方法实现,其中每一步的移动使用同一个方法tanksMove实现。而炮弹的发射则通过同一个方法实现,使用belong判断炮弹属于玩家坦克或是电脑的坦克。

为了判断物体之间的碰撞,我定义了一个judgeCrash方法来判断,先判断两个物体的大小,再判断小物体的四个顶点是否在大物体内。代码如下:

//射击间隔
function shotInterval(i,tank){//坦克的索引,对应的坦克数组
	setTimeout(function(){
		if(tank[i])
			tank[i].i--;
	},tank[i].inter);
}
//按键按下对应改变按键状态
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 codeReset(i){//坦克索引
	tanks[i].up[1]=false;
	tanks[i].right[1]=false;
	tanks[i].left[1]=false;
	tanks[i].down[1]=false;
}
//玩家坦克的移动
function playerTanksMove(i)//坦克索引
{
	if(tanks[i].up[1]==true)
	{
		tanks[i].d=1;
		tanksMove(i,0,-1,'player');
	}
	else if(tanks[i].right[1]==true)
	{
		tanks[i].d=2;
		tanksMove(i,1,0,'player');
	}
	else if(tanks[i].down[1]==true)
	{
		tanks[i].d=3;
		tanksMove(i,0,1,'player');
	}
	else if(tanks[i].left[1]==true)
	{
		tanks[i].d=4;
		tanksMove(i,-1,0,'player');
	}
	if((tanks[i].shot[1]==true)&&(tanks[i].i==0))
	{
		tanks[i].i=1;
		shotInterval(i,tanks);
		bullets[bullets.length]={x:tanks[i].x+tanks[i].l*3/8,y:tanks[i].y+tanks[i].l*3/8,l:tanks[i].l/4,t:tanks.t,color:'rgb(255,0,0)',belong:i+1};
//x,y:炮弹的坐标,l:炮弹的边长,t:炮弹每隔t毫秒移动一次,color:炮弹的颜色,炮弹所属坦克
		if(tanks[i].d==1)
		{
			bullets[bullets.length-1].y=tanks[i].y-tanks[i].l/4;
			bulletsMove(i,bullets[bullets.length-1],0,-1,bullets.length-1);
		}
		else if(tanks[i].d==2)
		{
			bullets[bullets.length-1].x=tanks[i].x+tanks[i].l;
			bulletsMove(i,bullets[bullets.length-1],1,0,bullets.length-1);
		}
		else if(tanks[i].d==3)
		{
			bullets[bullets.length-1].y=tanks[i].y+tanks[i].l;
			bulletsMove(i,bullets[bullets.length-1],0,1,bullets.length-1);
		}
		else if(tanks[i].d==4)
		{
			bullets[bullets.length-1].x=tanks[i].x-tanks[i].l/4;
			bulletsMove(i,bullets[bullets.length-1],-1,0,bullets.length-1);
		}
	}
}
//AI坦克的移动
function AImove()
{
	var Rd=Math.floor(Math.random()*4)+1;
	var tanksAiX,tanksAiY;
	for(var i=0,len=tanksAi.length;i<len;i++)
	{
		if(tanksAi[i].x<canvas.width)
		{
			tanksAiX=tanksAi[i].x;
			tanksAiY=tanksAi[i].y;
			if (tanksAi[i].d==1) {
				tanksMove(i,0,-1,'AI');
				if((tanksAiX==tanksAi[i].x)&&(tanksAiY==tanksAi[i].y))
					tanksAi[i].d=Rd;
			}
			else if(tanksAi[i].d==2){
				tanksMove(i,1,0,'AI');
				if((tanksAiX==tanksAi[i].x)&&(tanksAiY==tanksAi[i].y))
					tanksAi[i].d=Rd;
			}
			else if(tanksAi[i].d==3){
				tanksMove(i,0,1,'AI');
				if((tanksAiX==tanksAi[i].x)&&(tanksAiY==tanksAi[i].y))
					tanksAi[i].d=Rd;
			}
			else if(tanksAi[i].d==4){
				tanksMove(i,-1,0,'AI');
				if((tanksAiX==tanksAi[i].x)&&(tanksAiY==tanksAi[i].y))
					tanksAi[i].d=Rd;
			}
		}
	}
}
//坦克的移动
function tanksMove(i,dx,dy,type)//坦克索引,坦克在x,y正方向上的移动,(0:不移动,1:向正方向移动,-1:向反方向移动),坦克类型
{
	if(type=='AI')
	{
		tanksAi[i].x+=tanksAi[i].v*dx;
		tanksAi[i].y+=tanksAi[i].v*dy;
		for(var j=0,len=tanksAi.length;j<len;j++)
		{
			if((i!=j)&&(judgeCrash(tanksAi[i].x,tanksAi[i].y,tanksAi[i].l,tanksAi[j].x,tanksAi[j].y,tanksAi[j].l)))
			{
				tanksAi[i].x-=tanksAi[i].v*dx;
				tanksAi[i].y-=tanksAi[i].v*dy;
				break;
			}
		}
		for(var w=0,wlen=woods[pic].length;w<wlen;w++)
		{
			if(judgeCrash(tanksAi[i].x,tanksAi[i].y,tanksAi[i].l,woods[pic][w].x*woodLength,woods[pic][w].y*woodLength,woodLength))
			{
				tanksAi[i].x-=tanksAi[i].v*dx;
				tanksAi[i].y-=tanksAi[i].v*dy;
				break;
			}
		}
		for(var f=0,flen=Fes[pic].length;f<flen;f++)
		{
			if(judgeCrash(tanksAi[i].x,tanksAi[i].y,tanksAi[i].l,Fes[pic][f].x*Felength,Fes[pic][f].y*Felength,Felength))
			{
				tanksAi[i].x-=tanksAi[i].v*dx;
				tanksAi[i].y-=tanksAi[i].v*dy;
				break;
			}
		}
		for(var p=0,plen=tanks.length;p<plen;p++)
		{
			if(judgeCrash(tanksAi[i].x,tanksAi[i].y,tanksAi[i].l,tanks[p].x,tanks[p].y,tanks[p].l))
			{
				tanksAi[i].x-=tanksAi[i].v*dx;
				tanksAi[i].y-=tanksAi[i].v*dy;
				break;
			}
		}
			for(var pr=0,prlen=protects.length;pr<prlen;pr++)
			{
				if(judgeCrash(tanksAi[i].x,tanksAi[i].y,tanksAi[i].l,protects[pr].x*woodLength,protects[pr].y*woodLength,woodLength))
				{
					tanksAi[i].x-=tanksAi[i].v*dx;
					tanksAi[i].y-=tanksAi[i].v*dy;
					break;
				}
			}
		if((tanksAi[i].x<0)||(tanksAi[i].x+tanksAi[i].l>canvas.width)||(tanksAi[i].y<0)||(tanksAi[i].y+tanksAi[i].l>canvas.height))
		{
			tanksAi[i].x-=tanksAi[i].v*dx;
			tanksAi[i].y-=tanksAi[i].v*dy;
		}
	}
	else
	{
		tanks[i].x+=(tanks[i].v*dx);
		tanks[i].y+=(tanks[i].v*dy);
		for(var j=0,len=tanksAi.length;j<len;j++)
		{
			if(judgeCrash(tanks[i].x,tanks[i].y,tanks[i].l,tanksAi[j].x,tanksAi[j].y,tanksAi[j].l))
			{
				tanks[i].x-=(tanks[i].v*dx);
				tanks[i].y-=(tanks[i].v*dy);
				break;
			}
		}
		for(var w=0,wlen=woods[pic].length;w<wlen;w++)
		{
			if(judgeCrash(tanks[i].x,tanks[i].y,tanks[i].l,woods[pic][w].x*woodLength,woods[pic][w].y*woodLength,woodLength))
			{
				tanks[i].x-=(tanks[i].v*dx);
				tanks[i].y-=(tanks[i].v*dy);
				break;
			}
		}
		for(var f=0,flen=Fes[pic].length;f<flen;f++)
		{
			if(judgeCrash(tanks[i].x,tanks[i].y,tanks[i].l,Fes[pic][f].x*Felength,Fes[pic][f].y*Felength,Felength))
			{
				tanks[i].x-=(tanks[i].v*dx);
				tanks[i].y-=(tanks[i].v*dy);
				break;
			}
		}
		if(props.length>0)
		{
			if(judgeCrash(tanks[i].x,tanks[i].y,tanks[i].l,props[0].x,props[0].y,propLength))
			{
				propsApply(i);
				props.length=0;
			}
		}
			for(var pr=0,prlen=protects.length;pr<prlen;pr++)
			{
				if(judgeCrash(tanks[i].x,tanks[i].y,tanks[i].l,protects[pr].x*woodLength,protects[pr].y*woodLength,woodLength))
				{
					tanks[i].x-=tanks[i].v*dx;
					tanks[i].y-=tanks[i].v*dy;
					break;
				}
			}
		if((tanks[i].x<0)||(tanks[i].x+tanks[i].l>canvas.width)||(tanks[i].y<0)||(tanks[i].y+tanks[i].l>canvas.height))
		{
			tanks[i].x-=tanks[i].v*dx;
			tanks[i].y-=tanks[i].v*dy;
		}
	}
}
//炮弹的移动
function bulletsMove(belong,bullet,dx,dy,index)//炮弹所属,要移动的炮弹,炮弹在x,y正方向上的移动,(0:不移动,1:向正方向移动,-1:向反方向移动),炮弹的索引
{
	bullet.x+=dx;
	bullet.y+=dy;
	if(bullet.belong!=0)
	{
		for(var t=0,tlen=tanksAi.length;t<tlen;t++)
		{
			if(judgeCrash(bullet.x,bullet.y,bullet.l,tanksAi[t].x,tanksAi[t].y,tanksAi[t].l))
			{
				bullet.x=0-bullet.l;
				bullet.y=0-bullet.l;
				tanksAi[t].h--;
				switch(tanksAi[t].h)
				{
					case 0:tanksAi[t].color='rgb(204,204,204)';break;
					case 1:tanksAi[t].color='rgb(153,51,51)';break;
					default:;
				}
				if(tanksAi[t].h<0)
				{
					kill[bullet.belong-1]++;
					kills++;
					tanksAi[t].l=0;
					tanksAi[t].x=canvas.width+t;
				}
				if(kills>=wins)
				{
					alert('you win');
					alert('player1:'+kill[0]+'\nplayer2:'+kill[1]);
					end=true;
					kills=0;
				}
				break;
			}
		}
	}
	else if(bullet.belong==0)
	{
		for(var p=0,plen=tanks.length;p<plen;p++)
		{
			if(judgeCrash(bullet.x,bullet.y,bullet.l,tanks[p].x,tanks[p].y,tanks[p].l))
			{
				bullet.x=-bullet.l;
				bullet.y=-bullet.l;
				tanks[p].h--;
				tanks[p].x=tanksPosition[p].x;
				tanks[p].y=tanksPosition[p].y;
				if(tanks[p].h<0)
					tanks.splice(p,1);
				if(tanks.length==0)
				{
					alert('game over');
					end=true;
				}
				break;
			}
		}
	}
	for(var b=0,blen=bullets.length;b<blen;b++)
	{
		if(index!=b)
		{
			if(judgeCrash(bullet.x,bullet.y,bullet.l,bullets[b].x,bullets[b].y,bullets[b].l))
			{
				bullet.x=-bullet.l;
				bullets[b].x=canvas.width+bullets[b].l+b;
				bullet.y=-bullet.l;
				bullets[b].y=canvas.height+bullets[b].l+b;
				break;
			}
		}
	}
	for(var w=0,wlen=woods[pic].length;w<wlen;w++)
	{
		if(judgeCrash(bullet.x,bullet.y,bullet.l,woods[pic][w].x*woodLength,woods[pic][w].y*woodLength,woodLength))
		{
			bullet.x=-bullet.l;
			bullet.y=-bullet.l;
			woods[pic].splice(w,1);
			break;
		}
	}
	for(var f=0,flen=Fes[pic].length;f<flen;f++)
	{
		if(judgeCrash(bullet.x,bullet.y,bullet.l,Fes[pic][f].x*Felength,Fes[pic][f].y*Felength,Felength))
		{
			bullet.x=-bullet.l;
			bullet.y=-bullet.l;
			break;
		}
	}
		for(var pr=0,prlen=protects.length;pr<prlen;pr++)
		{
			if(judgeCrash(bullet.x,bullet.y,bullet.l,protects[pr].x*woodLength,protects[pr].y*woodLength,woodLength))
			{
				bullet.x=-bullet.l;
				bullet.y=-bullet.l;
				protects.splice(pr,1);
				break;
			}
		}
		if(judgeCrash(bullet.x,bullet.y,bullet.l,(canvas.width+flagLength)/2,canvas.height-flagLength,flagLength))
		{
			bullet.x=-bullet.l;
			bullet.y=-bullet.l;
			alert('game over');
			end=true;
		}
	if(!outOfcContainer(bullet.x,bullet.y,bullet.l,0,0,canvas.width,canvas.height))
		setTimeout(function(){bulletsMove(belong,bullet,dx,dy,index);},bullets.t);
	else
	{
		bullet.x=-bullet.l;
		bullet.y=-bullet.l;
	}
}
//判断两物体是否碰撞
function judgeCrash(x1,y1,l1,x2,y2,l2){//两个物体的左上角坐标和边长
	if(l1>l2)
	{
		if((x2>=x1)&&(x2<=x1+l1)&&(y2>=y1)&&(y2<=y1+l1))
			return true;
		else if((x2+l2>=x1)&&(x2+l2<=x1+l1)&&(y2>=y1)&&(y2<=y1+l1))
			return true;
		else if((x2>=x1)&&(x2<=x1+l1)&&(y2+l2>=y1)&&(y2+l2<=y1+l1))
			return true;
		else if((x2+l2>=x1)&&(x2+l2<=x1+l1)&&(y2+l2>=y1)&&(y2+l2<=y1+l1))
			return true;
	}
	else
	{
		if((x1>=x2)&&(x1<=x2+l2)&&(y1>=y2)&&(y1<=y2+l2))
			return true;
		else if((x1+l1>=x2)&&(x1+l1<=x2+l2)&&(y1>=y2)&&(y1<=y2+l2))
			return true;
		else if((x1>=x2)&&(x1<=x2+l2)&&(y1+l1>=y2)&&(y1+l1<=y2+l2))
			return true;
		else if((x1+l1>=x2)&&(x1+l1<=x2+l2)&&(y1+l1>=y2)&&(y1+l1<=y2+l2))
			return true;
	}
	return false;
}
//AI坦克射击
function AIshot(i) {
    if (tanksAi[i].i == 0) {
        tanksAi[i].i = 1;
        shotInterval(i, tanksAi);
        bullets[bullets.length] = { x: tanksAi[i].x + tanksAi[i].l * 3 / 8, y: tanksAi[i].y + tanksAi[i].l * 3 / 8, l: tanksAi[i].l / 4, v: tanksAi[i].bv, color: 'rgb(255,0,0)', belong: 0, d: tanksAi[i].d };
        if (tanksAi[i].d == 1) {
            bullets[bullets.length - 1].y = tanksAi[i].y - tanksAi[i].l / 4;
            bulletsMove(i, bullets[bullets.length - 1], 0, -1, bullets.length - 1);
        } else if (tanksAi[i].d == 2) {
            bullets[bullets.length - 1].x = tanksAi[i].x + tanksAi[i].l;
            bulletsMove(i, bullets[bullets.length - 1], 1, 0, bullets.length - 1);
        } else if (tanksAi[i].d == 3) {
            bullets[bullets.length - 1].y = tanksAi[i].y + tanksAi[i].l;
            bulletsMove(i, bullets[bullets.length - 1], 0, 1, bullets.length - 1);
        } else if (tanksAi[i].d == 4) {
            bullets[bullets.length - 1].x = tanksAi[i].x - tanksAi[i].l / 4;
            bulletsMove(i, bullets[bullets.length - 1], -1, 0, bullets.length - 1);
        }
    }
}
//判断是否超出容器
function outOfcContainer(x, y, l, vx, vy, width, height) { //物体的x,y坐标,边长,速度,容器宽高
    if ((x + vx < 0) || (x + l + vx > width))
        return true;
    else if ((y + vy < 0) || (y + l + vy > height))
        return true;
    else
        return false;
}


//道具生效
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++;
            if (getElementsByClassName(document, 'h').length > 1)
                getElementsByClassName(document, 'h')[index].innerHTML = 'h:' + tanks[index].h;
            else
                getElementsByClassName(document, 'h')[index].innerHTML = 'h:' + tanks[0].h;
        }
    } else if (props[0].t == 3) {
        if (tanks[index].v <= 8)
            tanks[index].v++;
    } else if (props[0].t == 4) {
        if (tanks[index].bv < 12)
            tanks[index].bv++;
    } else if (props[0].t == 5) {
        if (tanks[index].inter >= 300)
            tanks[index].inter -= 50;
    }
}

接下来,我们要生成电脑的坦克,为了不让坦克在生成时重叠,我定义了方法判断坦克是否位置重叠,在生成坦克时,我将坦克全都生成在容器顶部。方法如下:

//生成AI坦克
function tankAiProduce(){
	var Rform=Math.floor(Math.random()*tankAiModel.length),
		Rd=Math.floor(Math.random()*4)+1,
		Rx=Math.floor(Math.random()*canvas.width);
	if(Rx>canvas.width-tankAiModel[Rform].l)
		Rx=canvas.width-tankAiModel[Rform].l;
	while(judgeRepeat(Rx,Rform))
	{
		Rx=(Math.random()*canvas.width);
		if(Rx>canvas.width-tankAiModel[Rform].l)
			Rx=canvas.width-tankAiModel[Rform].l;
	}
	tanksAi[tanksAi.length]={x:Rx,y:0,i:0,v:tankAiModel[Rform].v,h:tankAiModel[Rform].h,inter:tankAiModel[Rform].inter,t:tankAiModel[Rform].t,d:Rd,l:tankAiModel[Rform].l,color:tankAiModel[Rform].color};
}
//判断随机数出现是否重叠
function judgeRepeat(x,r){//坦克的x坐标和边长
	for(var i=0,len=tanksAi.length;i<len;i++)
	{
		if(judgeCrash(x,0,tankAiModel[r].l,tanksAi[i].x,tanksAi[i].y,tanksAi[i].l))
			return true;//重叠
	}
	return false;
}

接下来要绘制出我们需要的东西的方法,包括坦克,炮弹,基地,木块,铁块,道具

//定义画出圆角矩形的原型方法
CanvasRenderingContext2D.prototype.cirRct=function(x,y,w,l,r,color)
{
	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,color)
{
	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,color);
		this.cirRct(x+(l+w2)/2,y+l-l4,w4,l4,0,color);
		//画出炮口
		this.cirRct(x+spaceW/2+w4+(w2-w3)/2,y,w1,l1,0,color);
		//画出炮身
		this.cirRct(x+spaceW/2+w4,y+l1,w2,l2,0,color);
		//画出炮舱
		this.cirRct(x+spaceW/2+w4+(w2-w3)/2,y+l1+(l2-l3)/2,w3,l3,0,color);
	}
	else if(d==2){//右
		this.cirRct(x,y+spaceW/2,l4,w4,0,color);
		this.cirRct(x,y+spaceW/2+w2+w4,l4,w4,0,color);
		this.cirRct(x+l-l1,y+spaceW/2+w4,l1,w1,0,color);
		this.cirRct(x+l-l1-l2,y+spaceW/2+w4,l2,w2,0,color);
		this.cirRct(x+l-l1-(l2+l3)/2,y+spaceW/2+w4+(w2-w3)/2,l3,w3,0,color);
	}
	else if(d==3){//下
		this.cirRct(x+spaceW/2,y,w4,l4,0,color);
		this.cirRct(x+(l+w2)/2,y,w4,l4,0,color);
		this.cirRct(x+spaceW/2+w4+(w2-w3)/2,y+l-l1,w1,l1,0,color);
		this.cirRct(x+spaceW/2+w4,y+l-l1-l2,w2,l2,0,color);
		this.cirRct(x+spaceW/2+w4+(w2-w3)/2,y+l-l1-(l2+l3)/2,w3,l3,0,color);
	}
	else if(d==4){//左
		this.cirRct(x+l-l4,y+spaceW/2,l4,w4,0,color);
		this.cirRct(x+l-l4,y+(l+w2)/2,l4,w4,0,color);
		this.cirRct(x,y+spaceW/2+w4+(w2-w3)/2,l1,w1,0,color);
		this.cirRct(x+l1,y+spaceW/2+w4,l2,w2,0,color);
		this.cirRct(x+l1+(l2-l3)/2,y+spaceW/2+w4+(w2-w3)/2,l3,w3,0,color);
	}
}
//定义画出旗的方法
CanvasRenderingContext2D.prototype.drawFlag=function(x,y,l){
	this.cirRct(x+l*0.1,y,l*0.75,l/2,0,'rgb(255,0,0)');
	this.beginPath();
	this.moveTo(x+l*0.1+1,y+l/2);
	this.lineTo(x+l*0.1+1,y+l);
	this.strokeStyle='rgb(255,255,255)';
	this.stroke();
	this.closePath();
	this.strokeStyle='rgb(0,0,0)';
}
//画出炮弹
CanvasRenderingContext2D.prototype.drawBullets=function(x,y,l,color){
	this.cirRct(x,y,l,l,0,color);
}
//画出道具
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();
	},8000);
}
//建筑木块
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 protectContrust(){
	for(var i=0,len=protects.length;i<len;i++)
	{
		context.cirRct(protects[i].x*woodLength,protects[i].y*woodLength,woodLength,woodLength,0,'rgb(255,204,51)');
	}
}

最后将方法放入gameHandle方法中,每隔40毫秒调用一次,以形成动画的效果。 最后在window.onload调用该方法(因为道具的生成是每隔几秒进行一次,所以不放在gameHandle中,而是在window.onload中调用)

//计算剩余存活的坦克数量
function AIalive(){
	var a=0;
	for(var i=0,len=tanksAi.length;i<len;i++)
	{
		if(tanksAi[i].h>=0)
			a++;
	}
	return a;
}
//游戏开始
function gameHandle(){
	context.cirRct(0,0,canvas.width,canvas.height,0,'rgb(0,0,0)');
	woodContrust();
	FeContrust();
	codeChange();
	protectContrust();
	context.drawFlag((canvas.width+flagLength)/2,canvas.height-flagLength,flagLength);
	if(props.length>0)
	{
		context.drawProp(props[0].x,props[0].y,propLength,props[0].t);
	}
	for(var i=0,len=tanks.length;i<len;i++)
	{
		context.drawTank(tanks[i].x,tanks[i].y,tanks[i].l,tanks[i].d,tanks[i].color);
		playerTanksMove(i);
	}
	for(var j=0,blen=bullets.length;j<blen;j++)
	{
		context.drawBullets(bullets[j].x,bullets[j].y,bullets[j].l,bullets[j].color);
	}
	for(var k=0,alen=tanksAi.length;k<alen;k++)
	{
		context.drawTank(tanksAi[k].x,tanksAi[k].y,tanksAi[k].l,tanksAi[k].d,tanksAi[k].color);
		AIshot(k);
	}
	if(tanksAi.length>0)
		AImove();
	if(AIalive()<tankDisplay)
		tankAiProduce();
	if(!end)
	{
		setTimeout(function(){
			gameHandle();
		},40);
	}
	else
	{
		setTimeout(function(){
			bullets.length=0;
			tanksAi.length=0;
		},1000)
	}
}
window.onload=function(){
	gameHandle();
	markProp();
}

至此简单的坦克大战就完成了,其中还可以添加很多有趣的道具,电脑坦克的模型还有很多类型可以添加,相信有创意的人会做出不亚于经典的坦克大战的游戏。希望本篇文章对大家有帮助,也希望各位大神不吝赐教,指出我代码中的不足。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值