javascript 模拟坦克大战游戏(html5版)附源码下载

一、总结关键点和遇到的问题

1.javascript中的继承,最好父类只提供方法共享,属性写到各自子类中,避免父类和子类的构造函数混杂。

2.prototype模拟继承的代码,应写在所有方法定义之前,否则原型对象被改变,方法就变成了未定义,如:

代码如下:

Hero.prototype = new Tank (0, 0, 0); 
Hero.prototype.constructor = Hero; 
Hero.prototype.addLife = function(){ 
this.lifetimes++; 
document.querySelector("#life").innerHTML = hero.lifetimes; 
}

3.canvas画图形时,除了画矩形,其他的都要加上 ctx.beginPath();、ctx.closePath();,否则会出现意想不到的错误。

4.concat函数可以合并数组,或者是元素返回一个新的数组

5.Image的src属性赋值后就会加载图片,但如果没有加载完毕就画图片,会导致失效,所以使用onload事件处理

6.扩展Array功能,删除指定元素

代码如下:

//扩展 删除指定元素 
Array.prototype.deleteElement = function (obj) { 
if (obj) { 
for (var i = 0; i < this.length; i++) { 
if (this[i] === obj) { 
this.splice (i, 1); 
} 
} 
} 
}

7.定时器设置,setInterval(“fun”,1000)方法的第一个参数,可以是字符串,如"hero.say()",类似eval会去执行这串代码,所以它可以给函数带上参数,并且也指定了这个函数的运行上下文。但如果传入是函数的句柄,则不能带参数,并且不能指定上下文,除了第一种方式解决外,我用了闭包来解决这个问题

代码如下:

//定时器,自行运动 
this.timer = setInterval ((function (context) { 
return function () { 
Bullet.prototype.move.call (context) 
} 
}) (this), 30);

我保存了当前的执行环境,并调用call方法手动执行。

8.方法的功能设计,除了功能外,应该包括执行此功能的条件检测,如move,就应该包括什么情况下可以移动,移动到什么地方就不能移动了。此检测不应该放在外部。

9.写代码时不应该去想设计或者优化的问题,先实现功能,再谈优化,或者先设计再实现。思路要清晰,别混乱,着重于一点。

10.javascript中没有sleep的功能,可以创建一个变量作为缓冲,来达到间隔执行的目的

二、代码实现

1.本程序分为Bomb.js,Bullet.js,Draw.js,Tank.js,index.html,img,music,

2.最终效果

javascript 模拟坦克大战游戏(html5版)附源码下载

javascript 模拟坦克大战游戏(html5版)附源码下载

javascript 模拟坦克大战游戏(html5版)附源码下载 

3.代码

1.index.html

代码如下:

<!DOCTYPE html> 
<html> 
<head> 
<title></title> 
<meta charset="utf-8"> 
<style type="text/css"> 
body { 
font: 14px "sans-serif" 
} 

#Map { 
background-color: #000000; 
} 

.show { 
float: left 
} 

#guide { 
float: left; 
width: 200px; 
height: 390px; 
margin-left: 5px; 
background: #CCCCCC; 
padding: 5px; 
} 
</style> 
<script type="text/javascript" src="Tank.js"></script> 
<script type="text/javascript" src="Bullet.js"></script> 
<script type="text/javascript" src="Bomb.js"></script> 
<script type="text/javascript" src="Draw.js"></script> 

<script type="text/javascript"> 
window.onload = function () { 
//画布信息 
width = document.getElementById ('Map').width; 
height = document.getElementById ('Map').height; 
ctx = document.getElementById ('Map').getContext ('2d'); 
//初始页面 
var starImg = new Image (); 
starImg.src = "img/star.jpg"; 
starImg.onload = function () { 
ctx.drawImage (starImg, 0, 0, width, height); 
} 

//键盘监听 回车开始游戏 
document.body.onkeydown = function () { 
var keycode = event.keyCode; 
switch (keycode) { 
case 13: 
//初始化参数 
init () 
//刷新页面 
setInterval (draw, 30); 
document.body.onkeydown = gameControl; 
break; 
} 
} 
} 

function init () { 
//玩家和电脑 
hero = new Hero (100, 300, 0); 
enemys = []; 
for (var i = 0; i < 3; i++) { 
enemys.push (new Enemy (100 + i * 50, 0, 2)); 
} 
//合并数组 
allTank = enemys.concat (hero); 

//炸弹 
Bombs = []; 
im = new Image (); 
im2 = new Image (); 
im3 = new Image (); 
im.src = "img/bomb_3.gif"; 
im2.src = "img/bomb_2.gif"; 
im3.src = "img/bomb_1.gif"; 
} 

function gameControl () { 
var keycode = event.keyCode; 
switch (keycode) { 
case 65: 
hero.moveLeft (); 
break;//左 
case 83: 
hero.moveDown (); 
break;//下 
case 87: 
hero.moveUp (); 
break;//上 
case 68: 
hero.moveRight (); 
break;//右 
case 74: 
hero.shot (); 
break; 
case 49: 
hero.addLife () 
break; 
} 
} 

//扩展 删除指定元素 
Array.prototype.deleteElement = function (obj) { 
if (obj) { 
for (var i = 0; i < this.length; i++) { 
if (this[i] === obj) { 
this.splice (i, 1); 
} 
} 
} 
} 

</script> 
</head> 
<body> 
<div class="show"> 
<canvas id="Map" width="500px" height="400px"> 
</canvas> 
<audio id="music" autoplay="autoplay"> 
<source src="music/111.wav"> 
</audio> 
</div> 
<div id="guide"> 

<p>按下回车键开始游戏</p> 

<p>按下1键增加生命,默认是1</p> 

<p>剩余生命数 :<label id="life">1</label></p> 

<div id="data"> 

</div> 
</div> 
</body> 
</html>

2.Draw.js

代码如下:

/** 
* Created by Alane on 14-3-18. 
*/ 

function draw(){ 
//检测子弹和坦克生死 
checkDead(); 
//清空画布 
ctx.clearRect(0,0,500,400); 
//画玩家 
if(!hero.isdead){ 
drawTank(hero); 
}else{ 
hero.cutLife(); 
} 
//画敌人坦克 
for (var i = 0; i < enemys.length; i++) { 
drawTank(enemys[i]); 
} 
//画敌人子弹 
for(var j=0;j<enemys.length;j++){ 
var temp = enemys[j].bulletsList; 
for (var i = 0; i < temp.length; i++) { 
drawBullet(temp[i]); 
} 
} 
//画玩家子弹 
var temp = hero.bulletsList; 
for (var i = 0; i < temp.length; i++) { 
drawBullet(temp[i]); 
} 

//画炸弹 
for(var i=0;i<Bombs.length;i++){ 
drawBown(Bombs[i]); 
} 

} 

function drawTank(tank){ 
var x = tank.x; 
var y = tank.y; 
ctx.fillStyle = tank.color; 

if(tank.direct == 0 || tank.direct ==2){ 
ctx.fillRect(x, y, 5,30); 
ctx.fillRect(x+15, y, 5,30); 

ctx.fillRect(x+6, y+8, 8,15); 

ctx.strokeStyle = tank.color; 
ctx.lineWidth = '1.5'; 
if(tank.direct == 0){ 
ctx.beginPath(); 
ctx.moveTo(x+10,y-2); 
ctx.lineTo(x+10,y+8); 
ctx.closePath(); 
}else{ 
ctx.beginPath(); 
ctx.moveTo(x+10,y+24); 
ctx.lineTo(x+10,y+32); 
ctx.closePath(); 
} 

ctx.stroke(); 
}else{ 
ctx.fillRect(x, y, 30,5); 
ctx.fillRect(x, y+15, 30,5); 

ctx.fillRect(x+8, y+6, 15,8); 

ctx.strokeStyle = '#FF0000'; 
ctx.lineWidth = '1.5'; 
if(tank.direct == 3){ 
ctx.beginPath(); 
ctx.moveTo(x-2,y+10); 
ctx.lineTo(x+8,y+10); 
ctx.closePath(); 
}else{ 
ctx.beginPath(); 
ctx.moveTo(x+24,y+10); 
ctx.lineTo(x+32,y+10); 
ctx.closePath(); 
} 

ctx.stroke(); 
} 

} 
function drawBullet(bullet){ 
ctx.fillStyle = bullet.color; 
ctx.beginPath(); 
ctx.arc(bullet.x,bullet.y,2,360,true); 
ctx.closePath(); 
ctx.fill(); 
} 

function drawBown (obj){ 
if(obj.life>8){ 
ctx.drawImage(im,obj.x,obj.y,50,50); 
}else if(obj.life>4){ 
ctx.drawImage(im2,obj.x,obj.y,50,50); 
}else{ 
ctx.drawImage(im3,obj.x,obj.y,50,50); 
} 

obj.lifeDown(); 
if(obj.life<=0){ 
Bombs.deleteElement(obj); 
} 
} 

function checkDead(){ 
//检测敌人子弹生死 
for(var j=0;j<enemys.length;j++){ 
var temp = enemys[j].bulletsList; 
for (var i = 0; i < temp.length; i++) { 
var o = temp[i]; 
if(o.isdead){ 
temp.deleteElement(o); 
} 
} 
} 
//检测玩家子弹生死 
var temp = hero.bulletsList; 
for (var i = 0; i < temp.length; i++) { 
var o = temp[i]; 
if(o.isdead){ 
temp.deleteElement(o); 
} 
} 

//检测敌人坦克生死 
for (var i = 0; i < enemys.length; i++) { 
var o = enemys[i]; 
if(o.isdead){ 
enemys.deleteElement(o); 
} 
} 
}

Bomb.js

代码如下:

/** 
* Created by Alane on 14-3-18. 
*/ 
function Bomb(x,y){ 
this.life = 12; 
this.x = x; 
this.y = y; 
} 
Bomb.prototype.lifeDown = function(){ 
this.life--; 
}

Tank.js

代码如下:

/** 
* Created by Alane on 14-3-7. 
*/ 
/** 
* direct 0 上 
* 1 右 
* 2 下 
* 3 左 
* @param x 
* @param y 
* @param direct 
* @constructor 
*/ 
//******************************************************************************************/ 
//坦克父类 
function Tank (x, y, direct) { 
this.speed = 2; 

} 
Tank.prototype.moveUp = function () { 
//边界检测 
if (this.y < 0) { 
//换方向 
this.changeDirect (); 
return; 
} 
this.y -= this.speed; 
this.direct = 0; 

} 
Tank.prototype.moveDown = function () { 
if (this.y > height - 30) { 
this.changeDirect (); 
return; 
} 
this.y += this.speed; 
this.direct = 2; 
} 
Tank.prototype.moveLeft = function () { 
if (this.x < 0) { 
this.changeDirect (); 
return; 
} 
this.x -= this.speed; 
this.direct = 3; 

} 
Tank.prototype.moveRight = function () { 
if (this.x > width - 30) { 
this.changeDirect (); 
return; 
} 
this.x += this.speed; 
this.direct = 1; 

} 

//变换方向 
Tank.prototype.changeDirect = function () { 
while (true) { 
var temp = Math.round (Math.random () * 3); 
if (this.direct != temp) { 
this.direct = temp; 
break; 
} 
} 
//alert("x="+this.x+" y="+this.y+" direct="+this.direct) 
} 

//射击子弹 
Tank.prototype.shot = function () { 
if(this.isdead){ 
return; 
} 
if (this.bulletsList.length < this.maxBulletSize) { 
//新建子弹 
var bullet = null; 
switch (this.direct) { 
case 0: 
bullet = new Bullet (this.x + 10, this.y - 2, 0, this.color); 
break; 
case 1: 
bullet = new Bullet (this.x + 32, this.y + 10, 1, this.color); 
break; 
case 2: 
bullet = new Bullet (this.x + 10, this.y + 32, 2, this.color); 
break; 
case 3: 
bullet = new Bullet (this.x - 2, this.y + 10, 3, this.color); 
break; 
} 
//放入弹夹 
this.bulletsList.push (bullet); 
} 
} 
//******************************************************************************************/ 
//玩家 
function Hero (x, y, direct) { 
this.lifetimes = 5; 
this.isdead = false; 
this.color = '#FF0000'; 
this.x = x; 
this.y = y; 
this.direct = direct; 
this.bulletsList = []; 
this.maxBulletSize = 10; 
this.newlife = null; 
} 
Hero.prototype = new Tank (0, 0, 0); 
Hero.prototype.constructor = Hero; 
Hero.prototype.addLife = function(){ 
this.lifetimes++; 
document.querySelector("#life").innerHTML = hero.lifetimes; 
} 
Hero.prototype.cutLife = function(){ 
if(this.lifetimes>=1 && !this.newlife){ 
this.lifetimes--; 
this.newlife = setTimeout("hero.newLife()",2000); 
} 
} 
Hero.prototype.newLife = function(){ 
this.isdead = false; 
clearTimeout(hero.newlife); 
hero.newlife = null; 
document.querySelector("#life").innerHTML = hero.lifetimes; 
} 


//******************************************************************************************/ 
//敌人坦克 
function Enemy (x, y, direct) { 
this.isdead = false; 
this.color = 'blue'; 
this.x = x; 
this.y = y; 
this.direct = direct; 
this.bulletsList = []; 
this.maxBulletSize = 1; 


//定时器,自动移动 
this.timer1 = setInterval ((function (context) { 
return function () { 
//移动 
Enemy.prototype.move.call (context); 
} 
}) (this), 30); 

//定时器,射击 
this.timer2 = setInterval ((function (context) { 
return function () { 
//射击 
Tank.prototype.shot.call (context); 
} 
}) (this), 2000); 

//定时器,变换方向 
this.timer3 = setInterval ((function (context) { 
return function () { 
//射击 
Tank.prototype.changeDirect.call (context); 
} 
}) (this), 3000); 
} 

Enemy.prototype = new Tank (0, 0, 0); 
Enemy.prototype.constructor = Enemy; 
Enemy.prototype.move = function () { 
switch (this.direct) { 
case 0: 
this.moveUp (); 
break; 
case 1: 
this.moveRight (); 
break; 
case 2: 
this.moveDown (); 
break; 
case 3: 
this.moveLeft (); 
break; 
} 
}

Bullet.js

代码如下:

/** 
* Created by Alane on 14-3-11. 
*/ 
function Bullet (x, y, direct, color) { 
this.isdead = false; 
this.x = x; 
this.y = y; 
this.direct = direct; 
this.speed = 4; 
this.color = color; 
//定时器,自行运动 
this.timer = setInterval ((function (context) { 
return function () { 
Bullet.prototype.move.call (context) 
} 
}) (this), 30); 
} 
Bullet.prototype.move = function () { 
switch (this.direct) { 
case 0: 
this.y -= this.speed; 
break; 
case 1: 
this.x += this.speed; 
break; 
case 2: 
this.y += this.speed; 
break; 
case 3: 
this.x -= this.speed; 
break; 
} 

//边界检测 
if (this.y < 0 || this.x > width || this.y > height || this.x < 0) { 
clearInterval (this.timer); 
this.isdead = true; 
} 

//碰撞检测 检测敌人坦克 
for(var i=0;i<allTank.length;i++){ 
var temp = allTank[i]; 
if(temp.isdead){ 
continue; 
} 
switch (temp.direct){ 
case 0: 
case 2:if(this.x>temp.x && this.x<temp.x+20 && this.y>temp.y&& this.y<temp.y+30){ 
if(this.color == temp.color){ 
break; 
} 
Bombs.push(new Bomb(temp.x-10,temp.y-10)); 
clearInterval (this.timer); 
this.isdead = true; 
temp.isdead = true; 
}break 
case 1: 
case 3:if(this.x>temp.x && this.x<temp.x+30 && this.y>temp.y&& this.y<temp.y+20){ 
if(this.color == temp.color){ 
break; 
} 
Bombs.push(new Bomb(temp.x-10,temp.y-10)); 
clearInterval (this.timer); 
this.isdead = true; 
temp.isdead = true; 
}break; 
} 
} 

}

源码下载

相关推荐