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;

  }

  }

  }

  源码下载