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

首先创建一个基本的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;elsefalse;
}

完成了地图的设置后,我们需要在首页部分选择地图,为了同步化我在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版的坦克大战,相信可以学到很多东西。

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

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

  1. java实现游戏对战过程_【Java实战】Java实现简易坦克对战小游戏

    //此程序用来实现一个简易的坦克对战小游戏! //Version:1.0 // @Author:Yongchun_zha package cha09; import javax.swing.*; im ...

  2. 【Java实战】Java实现简易坦克对战小游戏

    摘要:前期学习了Java入门的相关基础,现在通过这个小项目来熟悉巩固所学.该程序主要实现了一个简易的坦克对战小游戏,提供UI界面 //此程序用来实现一个简易的坦克对战小游戏! //Version:1. ...

  3. 手把手教你用Java实现一个简易联网坦克对战小游戏

    作者:炭烧生蚝 cnblogs.com/tanshaoshenghao/p/10708586.html 介绍 通过本项目能够更直观地理解应用层和运输层网络协议, 以及继承封装多态的运用. 网络部分是本 ...

  4. Java实现简易联网坦克对战小游戏(内涵源码)//Java+Java游戏+拓展学习+资源分享

    介绍 通过本项目能够更直观地理解应用层和运输层网络协议, 以及继承封装多态的运用. 网络部分是本文叙述的重点, 你将看到如何使用Java建立TCP和UDP连接并交换报文, 你还将看到如何自己定义一个简 ...

  5. java怎么实现网络对战平台_手把手教你用Java实现一个简易联网坦克对战小游戏...

    介绍 通过本项目能够更直观地理解应用层和运输层网络协议, 以及继承封装多态的运用. 网络部分是本文叙述的重点, 你将看到如何使用Java建立TCP和UDP连接并交换报文, 你还将看到如何自己定义一个简 ...

  6. java联机_Java实现简易联网坦克对战小游戏

    介绍 通过本项目能够更直观地理解应用层和运输层网络协议, 以及继承封装多态的运用. 网络部分是本文叙述的重点, 你将看到如何使用Java建立TCP和UDP连接并交换报文, 你还将看到如何自己定义一个简 ...

  7. 如何用java让坦克发射子弹_手把手教你用Java实现一个简易联网坦克对战小游戏 !...

    介绍 通过本项目能够更直观地理解应用层和运输层网络协议, 以及继承封装多态的运用. 网络部分是本文叙述的重点, 你将看到如何使用Java建立TCP和UDP连接并交换报文, 你还将看到如何自己定义一个简 ...

  8. 《uni-app》一个非canvas的飞机对战小游戏实现-碰撞检测的实现

    这是一个没有套路的前端博主,热衷各种前端向的骚操作,经常想到哪就写到哪,如果有感兴趣的技术和前端效果可以留言-博主看到后会去代替大家踩坑的-接下来的几篇都是uni-app的小实战,有助于我们更好的去学 ...

  9. 《uni-app》一个非canvas的飞机对战小游戏实现-敌机模型实现

    这是一个没有套路的前端博主,热衷各种前端向的骚操作,经常想到哪就写到哪,如果有感兴趣的技术和前端效果可以留言-博主看到后会去代替大家踩坑的-接下来的几篇都是uni-app的小实战,有助于我们更好的去学 ...

最新文章

  1. 学python有哪些书推荐-有哪些Python学习路线值得推荐?线路及书籍推荐都在这里...
  2. java poi word 表格_java 使用POI 读写word 表格
  3. python的easygui_Python的easygui学习
  4. C++ DNN Opencv3.4 实现人脸计数和人脸检测
  5. CVE-2015-1635-HTTP.SYS远程执行代码漏洞(ms15-034)
  6. Js中Number对象
  7. orm2 中文文档 5. 同步和删除模型
  8. php设计验证码图片,php实现简单的图片验证码
  9. 学生管理系统IPO图_C语言学生信息管理系统演示和说明(文件版)
  10. CorelDRAW2023全新版功能及下载安装教程
  11. 计算机毕业设计Java-超市会员积分管理系统-(源码+系统+mysql数据库+lw文档)
  12. 微信中的表情符号代码对照表
  13. AS3实现经典算法(一) 斐波纳契数列
  14. 拇指射箭服务器维护,拇指射箭1小游戏:这个拇指射箭3D游戏有点意思,拇指枪王对决...
  15. 【NVMe2.0b 16-1】Get Log Page
  16. 做网站windows和linux哪个好,做网站永Linux系统服务器到底有什么优势?
  17. 【Pytorch】| Pytorch中softmax的dim的详细总结
  18. VUE 表格设置序号
  19. arcgis for js 4 隐藏缩放按钮及底部powered by ESRI
  20. 4. 多重背包问题 I

热门文章

  1. IDEA HTTP Client (秒懂)
  2. 超便利!教你用ESP32开发板DIY掌上网页服务器!
  3. 计算机编程语言排行榜—TIOBE世界编程语言排行榜(2020年7月份最新版)
  4. 《真心话大冒险》微电影启动 张宁江献青涩初吻_0
  5. 2021年茶艺师(高级)考试内容及茶艺师(高级)考试资料
  6. 20190221——挟飞仙以遨游 Tomcat与Idea服务器部署
  7. 小型电子商务网站设计原则
  8. 【C#视频】——循环
  9. 程序员都用什么牌子电脑编程?国产电脑很强大啊!
  10. 体育硕士代码_教育硕士专业及代码.doc