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 相关文章推荐
javascript获取web应用根目录的方法
Feb 12 Javascript
javascript去除字符串中所有标点符号和提取纯文本的正则
Jun 07 Javascript
jQuery操作动态生成的内容的方法
May 28 Javascript
JavaScript中ES6 Babel正确安装过程
Jul 18 Javascript
Bootstrap在线电子商务网站实战项目5
Oct 14 Javascript
原生JS实现图片网格式渐显、渐隐效果
Jun 05 Javascript
微信小程序的分类页面制作
Jun 27 Javascript
JS遍历DOM文档树的方法实例详解
Apr 03 Javascript
CSS3 动画卡顿性能优化的完美解决方案
Sep 20 Javascript
微信小程序开发之tabbar图标和颜色的实现
Oct 17 Javascript
PHP实现基于Redis的MessageQueue队列封装操作示例
Feb 02 Javascript
小程序getLocation需要在app.json中声明permission字段
Apr 04 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
浏览器预览PHP文件时顶部出现空白影响布局分析原因及解决办法
2013/01/11 PHP
PHP类的反射用法实例
2014/11/03 PHP
详解PHP的Yii框架中扩展的安装与使用
2016/04/01 PHP
Zend Framework入门教程之Zend_Config组件用法详解
2016/12/09 PHP
php 函数使用可变数量的参数方法
2017/05/02 PHP
JavaScript类库D
2010/10/24 Javascript
Ext修改GridPanel数据和字体颜色、css属性等
2014/06/13 Javascript
JavaScript数据类型判定的总结笔记
2015/07/31 Javascript
jquery判断输入密码两次是否相等
2020/04/22 Javascript
javascript正则表达式总结
2016/02/29 Javascript
protractor的安装与基本使用教程
2017/07/07 Javascript
AngularJS实现注册表单验证功能
2017/10/16 Javascript
vue代理和跨域问题的解决
2018/07/18 Javascript
Vue2.0使用嵌套路由实现页面内容切换/公用一级菜单控制页面内容切换(推荐)
2019/05/08 Javascript
JS模拟浏览器实现全局搜索功能
2019/09/11 Javascript
微信小程序实现点击图片放大预览
2019/10/21 Javascript
解决ant Design中this.props.form.validateFields未执行的问题
2020/10/27 Javascript
详解实现vue的数据响应式原理
2021/01/20 Vue.js
javascript中layim之查找好友查找群组
2021/02/06 Javascript
[50:28]LGD女子学院第三期 DOTA2复仇之魂教学
2013/12/24 DOTA
Python多线程同步Lock、RLock、Semaphore、Event实例
2014/11/21 Python
CentOS安装pillow报错的解决方法
2016/01/27 Python
Python第三方库的安装方法总结
2016/06/06 Python
Python中动态创建类实例的方法
2017/03/24 Python
Python机器学习k-近邻算法(K Nearest Neighbor)实例详解
2018/06/25 Python
Python CVXOPT模块安装及使用解析
2019/08/01 Python
Python pip install之SSL异常处理操作
2020/09/03 Python
Furla官网:意大利著名的皮革品牌
2019/08/06 全球购物
教育学专业实习生的自我鉴定
2013/11/26 职场文书
英语演讲稿范文
2014/01/03 职场文书
村委会主任先进事迹
2014/01/15 职场文书
幼儿运动会邀请函
2014/01/17 职场文书
写给女朋友的检讨书
2014/01/28 职场文书
投资建议书模板
2014/05/12 职场文书
毕业论文致谢怎么写
2015/05/14 职场文书
python数字图像处理之图像的批量处理
2022/06/28 Python