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.最终效果
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 模拟坦克大战游戏(html5版)附源码下载
声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。
Reply on: @reply_date@
@reply_contents@