游戏截图
这个是游戏做出来的效果:
用到的图片资源:
这里把所有的坦克动画所需的图片,地形图片等放在了一张图中,使用的时候就可以通过截取一部分来使用。
游戏代码
下面就是整个游戏目前的代码,有什么建议或者不懂的可以评论,反正我现在也正好在学html的canvas和javascript。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>坦克大战</title>
<script src="modernizr.js"></script>
<script type="text/javascript">
//当网页加载完毕后运行js代码
window.addEventListener('load', eventWindowLoaded, false);
//运行canvasApp()函数
function eventWindowLoaded() {
canvasApp();
}
function canvasSupport () {
return Modernizr.canvas;
}
function canvasApp(){
if (!canvasSupport()) {
return;
}else{
var theCanvas = document.getElementById('canvas'); //将变量theCanvas与画布绑定
var context = theCanvas.getContext('2d'); //将变量context作为画布内容
}
var tileSheet=new Image(); //创建图片对象
tileSheet.addEventListener('load', eventSheetLoaded , false);
tileSheet.src="tanks_sheet.png";
//地图属性
var mapRows=10;
var mapCols=10;
var tileMap=[[32,31,31,31,1,31,31,31,31,32],
[1,1,1,1,1,1,1,1,1,1],
[32,1,1,26,1,1,1,26,1,32],
[32,1,26,26,26,1,26,26,1,32],
[32,1,1,26,1,1,1,1,1,32],
[32,1,1,1,1,1,26,26,1,32],
[32,1,26,1,26,1,26,1,1,32],
[1,1,26,1,26,1,1,1,1,1],
[32,1,1,1,1,1,1,1,1,32],
[32,31,31,31,1,31,31,31,31,32]];
//与坦克动画相关
var animationFrames=[1,2,3,4,5,6,7,8]; //用来存储坦克移动动画的8张图片的下标
var frameIndex=0;
//与坦克属性有关,包括坐标,方向,是否移动
var x=0; //当前x坐标
var y=32; //当前y坐标
var dx=4; //x方向上的移动速度
var dy=0; //y方向上的移动速度
var count=0; //用于记录坦克动画执行的次数,到8后会清0,0代表着未执行动画,即没有移动
var rotation=90; //旋转角度
var moveflag=false; //是否移动
var live=1;
var win=false;
//敌方坦克的属性
var tanks=[]; //用于存储所有敌方坦克
function Tanks(x,y){
this.x=x;
this.y=y;
this.xspeed=0;
this.yspeed=-4;
this.count=0;
this.rotation=0;
this.moveflag=false; //移动指令,为false时可以移动
this.frameIndex=0; //动画帧
}
tanks[0]=new Tanks(32,256);
tanks[1]=new Tanks(224,256);
//与子弹动画相关
var bullets=[]; //用于存储子弹对象的数组
//与子弹属性有关,如子弹的方向,飞行速度
function Bullet(rotation,x,y,xspeed,yspeed,checkFlag){
this.rotation=rotation;
this.x=x;
this.y=y;
this.xspeed=xspeed;
this.yspeed=yspeed;
this.checkFlag=checkFlag; //子弹是否碰撞的标志
}
//与爆炸动画有关
var booms=[];
//爆炸点的属性,主要是坐标值,爆炸动画由三张图片组成,需要记录当前帧播放的是哪张图片
function Boom(x,y){
this.x=x;
this.y=y;
this.nowpic=1;
}
//与游戏结束相关
var gameover=false; //游戏结束的标志
var tt; //运行动画的函数
function eventSheetLoaded() {
drawScreen();
}
function drawScreen() {
//绘制背景
context.fillStyle="#aaaaaa";
context.fillRect(0,0,320,320);
//绘制地图
for(var rowCtr=0;rowCtr<mapRows;rowCtr++) {
for(var colCtr=0;colCtr<mapCols;colCtr++){
var tileId = tileMap[rowCtr][colCtr]-1;
var sourceX = Math.floor(tileId % 8) *32;
var sourceY = Math.floor(tileId / 8) *32;
context.drawImage(tileSheet, sourceX,sourceY,32,32,colCtr*32,rowCtr*32,32,32);
}
}
//玩家的坦克动画
if(moveflag==true){ //如果moveflag==true,则进行移动
x=x+dx;
y=y+dy;
count++; //每一步,count加1
frameIndex++; // 调用下一张图片
if (frameIndex ==animationFrames.length) {
frameIndex=0; //下标清零,重新开始播放动画
}
}
if(count==8){ //当count==8时,一次移动完毕,即移动了32px
count=0; //将count清零
moveflag=false; //令moveflag=false,停止移动
}
context.save(); //保存当前变换状态
context.translate(x+16,y+16); //平移原点坐标
context.rotate(rotation * Math.PI / 180); //旋转整个坐标系
var sourceX=Math.floor(animationFrames[frameIndex] % 8) *32;
var sourceY=Math.floor(animationFrames[frameIndex] / 8) *32;
context.drawImage(tileSheet, sourceX, sourceY,32,32,-16,-16,32,32);
context.restore(); //读取保存的变换状态
//敌方坦克动画
var tankslength=tanks.length;
for(var i=0;i<tankslength;i++){
//敌方坦克检测到十字方向上有玩家坦克时,瞄准玩家开火
if(tanks[i].count==0){
tanksAttack(i);
}
//敌方坦克进行随机移动
if(tanks[i].moveflag==true){
tanks[i].x+=tanks[i].xspeed;
tanks[i].y+=tanks[i].yspeed;
tanks[i].count++;
tanks[i].frameIndex++;
if(tanks[i].frameIndex==animationFrames.length){
tanks[i].frameIndex=0;
}
}else{
tanksChange(i);
}
if(tanks[i].count==8){
tanks[i].count=0;
tanks[i].moveflag=false;
}
context.save();
context.translate(tanks[i].x+16,tanks[i].y+16);
context.rotate(tanks[i].rotation*Math.PI/180);
sourceX=Math.floor(animationFrames[tanks[i].frameIndex]%8)*32;
sourceY=Math.floor(animationFrames[tanks[i].frameIndex]/8+1)*32;
context.drawImage(tileSheet,sourceX,sourceY,32,32,-16,-16,32,32);
context.restore();
}
//子弹飞行动画
var bulletCount=bullets.length;
for(var i=0;i<bulletCount;i++){
//检测子弹碰撞的效果
checkChase(i,bullets[i].x,bullets[i].y);
//绘制子弹
if(bullets[i].checkFlag==false){
context.save();
context.translate(bullets[i].x+16,bullets[i].y+16);
context.rotate(bullets[i].rotation*Math.PI/180);
context.drawImage(tileSheet,128,64,32,32,-16,-16,32,32);
context.restore();
//子弹前进
bullets[i].x+=bullets[i].xspeed;
bullets[i].y+=bullets[i].yspeed;
}else{
createBoom(bullets[i].rotation,bullets[i].x,bullets[i].y); //产生一个爆炸动画对象
}
}
for(var i=0;i<bulletCount;i++){
if(bullets[i].checkFlag==true){
bullets.splice(i,1); //将该子弹对象从数组中移除并重排数组
i--;
bulletCount--;
}
}
//子弹爆炸动画
var boomCount=booms.length;
for(var i=0;i<boomCount;i++){
context.save();
context.translate(booms[i].x,booms[i].y);
context.drawImage(tileSheet,32*booms[i].nowpic,64,32,32,0,0,32,32);
context.restore();
booms[i].nowpic++;
}
for(var i=0;i<boomCount;i++){
if(booms[i].nowpic==4){
booms.splice(i,1);
i--;
boomCount--;
}
}
if(live==0||tanks.length==0){
gameover=true;
if(live==0){
win=false;
}
if(tanks.length==0){
win=true;
}
}
if(gameover==true){
clearInterval(tt);
if(win==false)
alert("你输了");
else
alert("你赢了");
window.location=window.location;
}
}
//键盘监听事件,用于改变玩家坦克的方向,同时修改坦克的一些数值
function changeDirection(e){
if(count==0){ //count==0代表着坦克的前一个动作执行完毕
var xindex=x/32;
var yindex=y/32;
switch(e.keyCode){
case 37:
if(tileMap[yindex][xindex-1]==1){
moveflag=true;
rotation=-90;
dx=-4;
dy=0;
}else if(tileMap[yindex][xindex-1]==undefined){
moveflag=true;
rotation=-90;
x=320;
dx=-4;
dy=0;
}
break;
case 38:
if(yindex-1==-1){
moveflag=true;
rotation=0;
y=320;
dx=0;
dy=-4;
}else if(tileMap[yindex-1][xindex]==1){
moveflag=true;
rotation=0;
dx=0;
dy=-4;
}
break;
case 39:
if(tileMap[yindex][xindex+1]==1){
moveflag=true;
rotation=90;
dx=4;
dy=0;
}else if(tileMap[yindex][xindex+1]==undefined){
moveflag=true;
rotation=90;
x=0;
dx=4;
dy=0;
}
break;
case 40:
if(yindex+1==10){
moveflag=true;
rotation=180;
y=0;
dx=0;
dy=4;
}else if(tileMap[yindex+1][xindex]==1){
moveflag=true;
rotation=180;
dx=0;
dy=4;
}
}
}
}
//改变敌方坦克移动方向的函数
function tanksChange(i){
var randMath=Math.floor(Math.random()*4-1);
var xindex=tanks[i].x/32;
var yindex=tanks[i].y/32;
switch(randMath){
case -1:
if(tileMap[yindex][xindex-1]==1){
tanks[i].moveflag=true;
tanks[i].rotation=-90;
tanks[i].xspeed=-4;
tanks[i].yspeed=0;
}else if(tileMap[yindex][xindex-1]==undefined){
tanks[i].moveflag=true;
tanks[i].rotation=-90;
tanks[i].x=320;
tanks[i].xspeed=-4;
tanks[i].yspeed=0;
}
break;
case 0:
if(yindex-1==-1){
tanks[i].moveflag=true;
tanks[i].rotation=0;
tanks[i].y=320;
tanks[i].xspeed=0;
tanks[i].yspeed=-4;
}else if(tileMap[yindex-1][xindex]==1){
tanks[i].moveflag=true;
tanks[i].rotation=0;
tanks[i].xspeed=0;
tanks[i].yspeed=-4;
}
break;
case 1:
if(tileMap[yindex][xindex+1]==1){
tanks[i].moveflag=true;
tanks[i].rotation=90;
tanks[i].xspeed=4;
tanks[i].yspeed=0;
}else if(tileMap[yindex][xindex+1]==undefined){
tanks[i].moveflag=true;
tanks[i].rotation=90;
tanks[i].x=0;
tanks[i].xspeed=4;
tanks[i].yspeed=0;
}
break;
case 2:
if(yindex+1==10){
tanks[i].moveflag=true;
tanks[i].rotation=180;
tanks[i].y=0;
tanks[i].xspeed=0;
tanks[i].yspeed=4;
}else if(tileMap[yindex+1][xindex]==1){
tanks[i].moveflag=true;
tanks[i].rotation=180;
tanks[i].xspeed=0;
tanks[i].yspeed=4;
}
}
}
//键盘监听事件,用于产生子弹,根据坦克当前方向和位置,产生子弹的方向,坐标,以及速度
var time=0;
function createBullet(e){
if(count==0&&time==0){
var newBullet;
if(e.keyCode==32){
switch(rotation){
case -90:
newBullet=new Bullet(-90,x-32,y,-32,0,false);
break;
case 0:
newBullet=new Bullet(0,x,y-32,0,-32,false);
break;
case 90:
newBullet=new Bullet(90,x+32,y,32,0,false);
break;
case 180:
newBullet=new Bullet(180,x,y+32,0,32,false);
}
bullets.push(newBullet);
time=1;
setTimeout(retime,400); //设置延时是为了不让子弹快速连发,快速连发会破坏游戏体验
function retime(){
time=0;
}
}
}
}
//敌方坦克检测十字方向上是否有玩家坦克,若有,则开火
function tanksAttack(i){
if(tanks[i].x==x&&!checkBlocky(i)){
if(tanks[i].y>y){
bullets.push(new Bullet(0,tanks[i].x,tanks[i].y-32,0,-32,false));
}
if(tanks[i].y<y){
bullets.push(new Bullet(180,tanks[i].x,tanks[i].y+32,0,32,false));
}
}
if(tanks[i].y==y&&!checkBlockx(i)){
if(tanks[i].x>x){
bullets.push(new Bullet(-90,tanks[i].x-32,tanks[i].y,-32,0,false));
}
if(tanks[i].x<x){
bullets.push(new Bullet(90,tanks[i].x+32,tanks[i].y,32,0,false));
}
}
function checkBlocky(i){
var y1=Math.min(Math.floor(y/32),tanks[i].y/32);
var y2=Math.max(Math.floor(y/32),tanks[i].y/32);
var x_=tanks[i].x/32;
for(var i=y1;i<y2;i++){
if(tileMap[i][x_]!=1){
return true;
}
}
return false;
}
function checkBlockx(i){
var x1=Math.min(Math.floor(x/32),tanks[i].x/32);
var x2=Math.max(Math.floor(x/32),tanks[i].x/32);
var y_=tanks[i].y/32;
for(var i=x1;i<x2;i++){
if(tileMap[y_][i]!=1){
return true;
}
}
return false;
}
}
//根据地图数组来检测子弹的碰撞,若子弹所在格子不为1,将播放子弹爆炸效果
function checkChase(a,b,c){
if(c/32>0&&c/32<9){
if(tileMap[c/32][b/32]!=1){
bullets[a].checkFlag=true;
}else if(x-16<=b&&b<=x+16&&y-16<=c&&c<=y+16){
bullets[a].checkFlag=true;
live=false;
}else if(attackTanks(b,c)==true){
bullets[a].checkFlag=true;
}
}else{
bullets[a].checkFlag=true;
}
}
//判定子弹是否打中了敌方坦克
function attackTanks(b,c){
var aa=tanks.length;
for(var i=0;i<aa;i++){
if(tanks[i].x-16<=b&&b<=tanks[i].x+16&&tanks[i].y-16<=c&&c<=tanks[i].y+16){
tanks.splice(i,1);
return true;
}
}
}
//产生一个爆炸动画,并根据子弹方向调整爆炸动画的坐标
function createBoom(a,b,c){
var newBoom;
switch(a){
case -90:
newBoom=new Boom(b+16,c);
break;
case 0:
newBoom=new Boom(b,c+16);
break;
case 90:
newBoom=new Boom(b-16,c);
break;
case 180:
newBoom=new Boom(b,c-16);
}
booms.push(newBoom);
}
//开始游戏按钮点击事件
var button=document.getElementById("start"); //将参数button与id="start"的按钮绑定
button.addEventListener("click",startgame); //按钮点击事件
//开始游戏
var tt; //用于保存播放动画函数,在游戏结束时需要删除动画(即clearInterval(tt)),才能刷新页面
function startgame(){
button.style.visibility="hidden"; //将开始游戏按钮隐藏
document.getElementById("mask").style.visibility="hidden";
tt=setInterval(drawScreen, 50);
window.addEventListener("keydown",changeDirection,true); //添加键盘监听事件(键盘按下)——用于改变坦克运动方向
window.addEventListener("keyup",createBullet,true); //添加键盘监听事件(键盘松开)——用于产生子弹
}
}
</script>
</head>
<body>
<div style="position: absolute; top: 50px; left: 50px;">
<canvas id="canvas" width="320" height="320">
Your browser does not support the HTML 5 Canvas.
</canvas>
<div id="mask" style="position:absolute;top:0;left:0;background-color:black;opacity:0.4;">
<table width="320" height="320"></table>
</div>
<button id="start" style="position:absolute;left:80px;top:150px;width:150px;height:40px;">开始游戏</button>
</div>
<span style="position:absolute;left:50px;top:400px;color:blue">使用上下左右方向键移动,空格键开火</span>
</body>
</html>