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


Posted in Javascript onApril 08, 2014

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

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; 
} 
} 
}

源码下载
Javascript 相关文章推荐
使用TextRange获取输入框中光标的位置的代码
Mar 08 Javascript
鼠标经过的文本框textbox变色
May 21 Javascript
jQuery动态背景图片效果实现方法
Jul 03 Javascript
jQuery根据name属性进行查找的用法分析
Jun 23 Javascript
jQuery.parseHTML() 函数详解
Jan 09 Javascript
原生JavaScript实现的简单省市县三级联动功能示例
May 27 Javascript
Bootstrap modal只加载一次数据的解决办法(推荐)
Nov 24 Javascript
在Vue项目中引入JQuery-ui插件的讲解
Jan 27 jQuery
JS实现的Object数组去重功能示例【数组成员为Object对象】
Feb 01 Javascript
node解析修改nginx配置文件操作实例分析
Nov 06 Javascript
Node.js API详解之 dns模块用法实例分析
May 15 Javascript
用JS写一个发布订阅模式
Nov 07 Javascript
js定时调用方法成功后并停止调用示例
Apr 08 #Javascript
jquery选择器使用详解
Apr 08 #Javascript
jquery淡化版banner异步图片文字效果切换图片特效
Apr 08 #Javascript
jQuery拖动div、移动div、弹出层实现原理及示例
Apr 08 #Javascript
javascript跨域的4种方法和原理详解
Apr 08 #Javascript
通过Javascript读取本地Excel文件内容的代码示例
Apr 08 #Javascript
javascript数组操作(创建、元素删除、数组的拷贝)
Apr 07 #Javascript
You might like
PHP5多态性与动态绑定介绍
2015/04/03 PHP
php解析xml 的四种简单方法(附实例)
2016/07/11 PHP
Laravel 模型使用软删除-左连接查询-表起别名示例
2019/10/24 PHP
网页里控制图片大小的相关代码
2006/06/13 Javascript
JavaScript与DropDownList 区别分析
2010/01/01 Javascript
JavaScript调用Activex控件的事件的实现方法
2010/04/11 Javascript
深入理解JavaScript系列(9) 根本没有“JSON对象”这回事!
2012/01/15 Javascript
jquery实现浮动的侧栏实例
2015/06/25 Javascript
基于javascript实现句子翻牌网页版小游戏
2016/03/23 Javascript
JS获取IE版本号与HTML设置IE文档模式的方法
2016/10/09 Javascript
jQuery学习笔记——jqGrid的使用记录(实现分页、搜索功能)
2016/11/09 Javascript
js实现可输入可选择的select下拉框
2016/12/21 Javascript
Bootstrap源码解读导航(6)
2016/12/23 Javascript
jQuery插件HighCharts实现的2D回归直线散点效果示例【附demo源码下载】
2017/03/09 Javascript
JS实现的简单四则运算计算器功能示例
2017/09/27 Javascript
vue实现组件之间传值功能示例
2018/07/13 Javascript
详解VScode编辑器vue环境搭建所遇问题解决方案
2019/04/26 Javascript
JS块级作用域和私有变量实例分析
2019/05/11 Javascript
微信小程序使用echarts获取数据并生成折线图
2019/10/16 Javascript
JS中多层次排序算法的实现代码
2021/01/06 Javascript
基于wxpython开发的简单gui计算器实例
2015/05/30 Python
python实现堆和索引堆的代码示例
2018/03/19 Python
TensorFlow 模型载入方法汇总(小结)
2018/06/19 Python
用Python分析3天破10亿的《我不是药神》到底神在哪?
2018/07/12 Python
python定时检测无响应进程并重启的实例代码
2019/04/22 Python
Python + OpenCV 实现LBP特征提取的示例代码
2019/07/11 Python
python中必要的名词解释
2019/11/20 Python
Pycharm plot独立窗口显示的操作
2020/12/11 Python
CSS3结构性伪类选择器九种写法
2012/04/18 HTML / CSS
中学生团员自我评价分享
2013/12/07 职场文书
光信息科学与技术专业职业生涯规划
2014/03/13 职场文书
开展批评与自我批评发言稿
2014/10/16 职场文书
2019年让高校“心动”的自荐信
2019/03/25 职场文书
雄兵连:第三季先行图公开,天使恶魔联合,银河之力的新力量
2021/06/11 国漫
使用RedisTemplat实现简单的分布式锁
2021/11/20 Redis
Tomcat项目启动失败的原因和解决办法
2022/04/20 Servers