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 相关文章推荐
Discuz! 6.1_jQuery兼容问题
Sep 23 Javascript
JavaScript 乱码问题
Aug 06 Javascript
jQuery$命名冲突怎么办如何解决
Jan 16 Javascript
javaScript年份下拉列表框内容为当前年份及前后50年
May 28 Javascript
JS数组array元素的添加和删除方法代码实例
Jun 01 Javascript
谈一谈js中的执行环境及作用域
Mar 30 Javascript
解决vue中对象属性改变视图不更新的问题
Feb 23 Javascript
Vue 将后台传过来的带html字段的字符串转换为 HTML
Mar 29 Javascript
浅谈手写node可读流之流动模式
Jun 01 Javascript
Angular-UI Bootstrap组件实现警报功能
Jul 16 Javascript
详解在HTTPS 项目中使用百度地图 API
Apr 26 Javascript
vue实现简单计算商品价格
Sep 14 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
颠覆常识!无色透明的咖啡诞生了(中日双语)
2021/03/03 咖啡文化
简单示例AJAX结合PHP代码实现登录效果代码
2008/07/25 PHP
PHP的autoload自动加载机制使用说明
2010/12/28 PHP
Array of country list in PHP with Zend Framework
2011/10/17 PHP
10条php编程小技巧
2015/07/07 PHP
获取DOM对象的几种扩展及简写
2006/10/09 Javascript
JS自动缩小超出大小的图片
2012/10/12 Javascript
Jquery修改页面标题title其它JS失效的解决方法
2014/10/31 Javascript
director.js实现前端路由使用实例
2015/02/03 Javascript
浅析javascript中函数声明和函数表达式的区别
2015/02/15 Javascript
Angular 4.x 路由快速入门学习
2017/05/03 Javascript
详解Vue学习笔记进阶篇之列表过渡及其他
2017/07/17 Javascript
打造通用的匀速运动框架(实例讲解)
2017/10/17 Javascript
微信小程序实现image组件图片自适应宽度比例显示的方法
2018/01/16 Javascript
vue.js 底部导航栏 一级路由显示 子路由不显示的解决方法
2018/03/09 Javascript
vue实现分环境打包步骤(给不同的环境配置相对应的打包命令)
2019/06/04 Javascript
微信小程序canvas分享海报功能
2019/10/31 Javascript
前端使用crypto.js进行加密的函数代码
2020/08/16 Javascript
Python语言编写电脑时间自动同步小工具
2013/03/08 Python
Python基于递归实现电话号码映射功能示例
2018/04/13 Python
numpy的文件存储.npy .npz 文件详解
2018/07/09 Python
实列教程 一款基于jquery和css3的响应式二级导航菜单
2014/11/13 HTML / CSS
欧迪办公美国官网:Office Depot
2016/08/22 全球购物
奥地利体育网上商店:Gigasport
2019/10/09 全球购物
super关键字的用法
2012/04/10 面试题
结婚典礼证婚词
2014/01/08 职场文书
科技之星事迹材料
2014/06/02 职场文书
中层干部培训方案
2014/06/16 职场文书
四风问题个人对照检查剖析材料
2014/09/27 职场文书
离婚协议书应该怎么写
2014/10/12 职场文书
幼儿园推普周活动总结
2015/05/07 职场文书
入党介绍人意见2015
2015/06/01 职场文书
CSS3实现模糊背景的三种效果示例
2021/03/30 HTML / CSS
Nginx搭建rtmp直播服务器实现代码
2021/03/31 Servers
详解Laravel服务容器的优势
2021/05/29 PHP
苹果发布了MagSafe固件更新,可以不外接电源实现最高7.5W充电
2022/04/21 数码科技