1. 程式人生 > >+js實現簡單的雙人坦克對戰小遊戲

+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版的坦克大戰,相信可以學到很多東西。

希望本篇博文對大家有所幫助,也希望各位大神不吝賜教,指出我程式碼中的不足,謝謝大家。