文章目录
- 个人初学JS写的程序(看看就好)代码不够优雅
- 代码具体实现功能
- 1.画布代码画出坦克,子弹
- 2.实现敌人坦克的自动走路,发射子弹飞行
- 3.敌人坦克的重叠问题,边界检测
- 4.子弹打中坦克,坦克消失并产生爆炸效果
- 5.击败个数分数并显示在画布
- 6.其他一些小功能
- 效果图
- html代码(begin)
- Tank 坦克代码
- Bullet子弹相关代码
- Draw 绘制代码
- Bomb 爆炸对象
- Use 击败和重叠功能
- 知识点
- 定时器setInterval
- 坦克中的继承用法
- 坦克中的继承用法
个人初学JS写的程序(看看就好)代码不够优雅
代码具体实现功能
1.画布代码画出坦克,子弹
2.实现敌人坦克的自动走路,发射子弹飞行
3.敌人坦克的重叠问题,边界检测
4.子弹打中坦克,坦克消失并产生爆炸效果
5.击败个数分数并显示在画布
6.其他一些小功能
效果图
html代码(begin)
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>经典坦克大战</title><style type="text/css">body {text-align: center;}</style></head><body onkeydown="getConmmand()"><!--onkeydown事件用于当用户在html对象上按下键盘时(此时还未松开键盘)时,触发相应的js事件--><h1>坦克游戏大战</h1><p>提示:wasd 控制方向,空格发射子弹,按b是必杀技,按v是大招</p><canvas id="tankMap" width="600px" height="500px" style="background-color: black;"></canvas><button id="btn1">第一关</button><button id="btn2">第二关</button></body><script type="text/javascript" src="Use.js"></script><script type="text/javascript" src="Bomb.js"></script><script type="text/javascript" src="Tank.js"></script><script type="text/javascript" src="Draw.js"></script><script type="text/javascript" src="Bullet.js"></script><script type="text/javascript">//得到画布var myCanvas = document.getElementById("tankMap");//得到绘画上下文(你可以理解是画笔)var ctx = myCanvas.getContext("2d");var btn1 = document.getElementById("btn1");var btn2 = document.getElementById("btn2");//子弹变量和数组var heroBullet = null;var enemyBullet = null;var heroBullets = new Array();var enemyBullets = new Array();//开局全局变量定义var grade = 0; //分数var ene = 0; //敌机坦克数量var game = true; //对局状态var end = 0;//敌机死亡数 用来计算分数var enemynum = 0;//颜色数组var arrColor = ["#CD5C5C", "#FFFFFF", "red", "bule", "orange", "pink", "green", "#FDF5E6"]var bulletcolor = ["red", "bule", "orange", "pink", "green"]//爆炸var bombs = new Array();//定时器var timer2 = null;var timer1 = null;var enemytank = 0;//用于一键爆炸var etl = 0;//坦克初始化 var hero = new Hero(550, 250, 0, "#FFD700");var enemyTanks = new Array;//addEventListener()增加监听事件 ,click-鼠标点击btn1.addEventListener("click", function enemy1() {enemyOpen(7);btn1.style.display = "none";btn2.style.display = "none";});btn2.addEventListener("click", function enemy2() {enemyOpen(14);btn2.style.display = "none";btn1.style.display = "none";});//随机函数function rdm1(n) {return Math.ceil(Math.random() * n);}//for循环生成敌机和敌机子弹function enemyOpen(n) {for(var i = 0; i < n; i++) {var enemyTank = enemyTanks[i] = new Enemy(rdm1(500), rdm1(500), Math.floor(rdm1(3)), arrColor[rdm1(6)]);enemyTanks[i].time = setInterval("enemyTanks[" + i + "].run()", 50);ene = enemyTanks.length;//生成子弹switch(enemyTank.direct) {case 0:enemyBullet = new Bullet(enemyTank.x + 1, enemyTank.y - 10, enemyTank.direct, "enemy", enemyTank);break;case 1:enemyBullet = new Bullet(enemyTank.x + 1, enemyTank.y + 16, enemyTank.direct, "enemy", enemyTank);break;case 2:enemyBullet = new Bullet(enemyTank.x - 12, enemyTank.y - 2, enemyTank.direct, "enemy", enemyTank);break;case 3:enemyBullet = new Bullet(enemyTank.x + 24, enemyTank.y - 2, enemyTank.direct, "enemy", enemyTank);break;}enemyBullets.push(enemyBullet);//定时器 让子弹跑enemyBullets[i].timer = setInterval("enemyBullets[" + i + "].run()", 50);}}//增加分数function gradeadd() {grade += 2;}//画出分数function drawGrade() {if(game) {ctx.fillStyle = "orange";ctx.font = "15px Arial";ctx.fillText("您的分数是:" + grade, myCanvas.width - 150, myCanvas.height - 10);} else {gameEnd();}}//游戏结束 - 画布上显示分数function gameEnd() {game = false;for(var i = 0; i < enemyTanks.length; i++) {enemyTanks[i].isLive = false;}ctx.fillStyle = "orchid";ctx.font = "50px Arial";if(ene > 0) {ctx.fillText("you die", myCanvas.width / 4, myCanvas.height / 5);ctx.fillText("您的分数是:" + grade, myCanvas.width / 4, myCanvas.height / 3);} else {ctx.fillText("you win", myCanvas.width / 4, myCanvas.height / 5);ctx.fillText("您的分数是:" + grade, myCanvas.width / 4, myCanvas.height / 3);}}//初始化function getConmmand() {var code = event.keyCode;// console.log(code); 获取按键的AS码值//上下左右 -wsad b vswitch(code) {case 87:hero.moveUp();break;case 83:hero.moveDown();break;case 65:hero.moveLeft();break;case 68:hero.moveRight();break;case 32:hero.shotEnemy();break;case 66://setInterval()定时器 -->返回一个值传clearInterval()后能停止定时器if(!timer2) {timer2 = setInterval("hero.foutDirect()", 80);} else {clearInterval(timer2);timer2 = null;}break;case 86:if(!timer1) {timer1 = setInterval("hero.fastOver()", 100);} else {clearInterval(timer1);timer1 = null;}break;}}//刷新和绘制函数function flashTankMap() {//ctx.clearRect--把画布清理ctx.clearRect(0, 0, 600, 500);drawTank(hero);for(var i = 0; i < enemyTanks.length; i++) {drawTank(enemyTanks[i]);}//重新绘制if(game) {hero.isHeroTogeter();drawHeroBullet();isHitEnemyTank();isHitHeroTank();drawEnemyBomb();isTankTogeter();drawenemyBullet();drawGrade();}drawGrade();}setInterval("flashTankMap()", 50); //让页面自动刷新 解决子弹不会自己跑要用户按按键才跑的问题</script></html>
Tank 坦克代码
//父类坦克
function Tank(x, y, direct, color) {this.x = x;this.y = y;this.color = color;this.isLive = true;//存活与否this.speed = 2; //速度this.direct = direct; //方向//上下左右this.moveUp = function() {this.y -= 10;this.direct = 0;}this.moveDown = function() {this.y += 10;this.direct = 1;}this.moveLeft = function() {this.x -= 10;this.direct = 2;}this.moveRight = function() {this.x += 10;this.direct = 3;}}//--------------------------------------------------------------------------------------------------------------
//英雄坦克
function Hero(x, y, direct, color) {this.chh = Tank;this.chh(x, y, direct, color);//注意上面继承的方法//发射子弹函数this.shotEnemy = function() {if(this.isLive) {switch(this.direct) {case 0:heroBullet = new Bullet(this.x + 1, this.y - 10, this.direct, "hero", this);break;case 1:heroBullet = new Bullet(this.x + 1, this.y + 16, this.direct, "hero", this);break;case 2:heroBullet = new Bullet(this.x - 12, this.y - 2, this.direct, "hero", this);break;case 3:heroBullet = new Bullet(this.x + 24, this.y - 2, this.direct, "hero", this);break;}heroBullets.push(heroBullet);//定时器:对象里面的方法要用双引号heroBullets[heroBullets.length - 1].timer = setInterval("heroBullets[" + (heroBullets.length - 1) + "].run()", 50);}}//实现herotank不能和敌人坦克重叠(会爆炸)this.isHeroTogeter = function() {if(this.isLive) {for(var i = 0; i < enemyTanks.length; i++) {var temp = enemyTanks[i];switch(this.direct) {case 0:if((temp.x > this.x && temp.x < this.x + 21 && temp.y > this.y && temp.y < this.y + 30) ||(temp.x + 20 > this.x && temp.x + 20 < this.x + 21 && temp.y > this.y && temp.y < this.y + 30) ||(temp.x + 20 > this.x && temp.x + 20 < this.x + 21 && temp.y + 20 > this.y && temp.y + 30 < this.y + 30) ||(temp.x > this.x && temp.x < this.x + 21 && temp.y + 20 > this.y && temp.y + 20 < this.y + 30)) {heroBomb(i);}break;case 1:if((temp.x > this.x && temp.x < this.x + 21 && temp.y > this.y && temp.y < this.y + 30) ||(temp.x + 20 > this.x && temp.x + 20 < this.x + 21 && temp.y > this.y && temp.y < this.y + 30) ||(temp.x + 20 > this.x && temp.x + 20 < this.x + 21 && temp.y + 20 > this.y && temp.y + 30 < this.y + 30) ||(temp.x > this.x && temp.x < this.x + 21 && temp.y + 20 > this.y && temp.y + 20 < this.y + 30)) {heroBomb(i);}break;case 2:if((temp.x > this.x && temp.x < this.x + 21 && temp.y > this.y && temp.y < this.y + 30) ||(temp.x + 20 > this.x && temp.x + 20 < this.x + 21 && temp.y > this.y && temp.y < this.y + 30) ||(temp.x + 20 > this.x && temp.x + 20 < this.x + 21 && temp.y + 20 > this.y && temp.y + 30 < this.y + 30) ||(temp.x > this.x && temp.x < this.x + 21 && temp.y + 20 > this.y && temp.y + 20 < this.y + 30)) {heroBomb(i);}break;case 3:if((temp.x > this.x && temp.x < this.x + 21 && temp.y > this.y && temp.y < this.y + 30) ||(temp.x + 20 > this.x && temp.x + 20 < this.x + 21 && temp.y > this.y && temp.y < this.y + 30) ||(temp.x + 20 > this.x && temp.x + 20 < this.x + 21 && temp.y + 20 > this.y && temp.y + 30 < this.y + 30) ||(temp.x > this.x && temp.x < this.x + 21 && temp.y + 20 > this.y && temp.y + 20 < this.y + 30)) {heroBomb(i);}break;}}}}//v键一键爆炸this.fastOver = function() {if(hero.isLive) {if(etl < enemyTanks.length) {enterBomb(enemyTanks[etl]);etl += 1;}}}// b键必杀技this.foutDirect = function() {if(this.isLive) {heroBullet = new Bullet(this.x + 1, this.y - 10, 0, "hero", this);heroBullets.push(heroBullet);heroBullets[heroBullets.length - 1].timer = setInterval("heroBullets[" + (heroBullets.length - 1) + "].run()", 7);heroBullet = new Bullet(this.x + 1, this.y + 16, 1, "hero", this);heroBullets.push(heroBullet);heroBullets[heroBullets.length - 1].timer = setInterval("heroBullets[" + (heroBullets.length - 1) + "].run()", 7);heroBullet = new Bullet(this.x - 12, this.y - 2, 2, "hero", this);heroBullets.push(heroBullet);heroBullets[heroBullets.length - 1].timer = setInterval("heroBullets[" + (heroBullets.length - 1) + "].run()", 7);heroBullet = new Bullet(this.x + 24, this.y - 2, 3, "hero", this);heroBullets.push(heroBullet);heroBullets[heroBullets.length - 1].timer = setInterval("heroBullets[" + (heroBullets.length - 1) + "].run()", 7);}}
}//--------------------------------------------------------------------------------------------------------------
//敌人坦克
function Enemy(x, y, direct, color) {this.chh = Tank;this.chh(x, y, direct, color);this.bulletIiLive = true;this.time = null;this.count = 0;//计算走的步数//坦克的移动行为this.run = function() {//坦克移动和边界碰撞检测(会调头)if(this.isLive) {switch(this.direct) {case 0:if(this.y > 0) {this.y -= this.speed;} else {this.direct = 1;}break;case 1:if(this.y + 30 < 500) {this.y += this.speed;} else {this.direct = 0;}break;case 2:if(this.x > 0) {this.x -= this.speed;} else {this.direct = 3;}break;case 3:if(this.x + 30 < 600) {this.x += this.speed;} else {this.direct = 2;}break;}//每走70步改变方向if(this.count > 70) {this.direct = Math.round(Math.random() * 3);this.count = 0;}this.count++;//当子弹死亡时重新生成子弹if(this.bulletIiLive == false) {switch(this.direct) {case 0:enemyBullet = new Bullet(this.x + 1, this.y - 10, this.direct, "enemy", this);break;case 1:enemyBullet = new Bullet(this.x + 1, this.y + 16, this.direct, "enemy", this);break;case 2:enemyBullet = new Bullet(this.x - 12, this.y - 2, this.direct, "enemy", this);break;case 3:enemyBullet = new Bullet(this.x + 24, this.y - 2, this.direct, "enemy", this);break;}enemyBullets.push(enemyBullet);enemyBullets[enemyBullets.length - 1].timer = setInterval("enemyBullets[" + (enemyBullets.length - 1) + "].run()", 50);this.bulletIiLive = true;}}}}
Bullet子弹相关代码
//子弹对象
//type : 这颗子弹是敌人的还是自己的 "enemy"/"hero"
//tank:这颗子弹是属于哪个坦克的
function Bullet(x, y, direct, type, tank) {this.x = x;this.y = y;this.speed = 5;this.direct = direct;this.isLive = true; //边界死活this.timer = null;this.type = type;this.tank = tank;//子弹刷新--子弹飞行this.run = function() {if(this.x <= 0 || this.x >= myCanvas.width || this.y <= 0 || this.y >= myCanvas.height) {this.isLive = false;clearInterval(this.timer);if(this.type == "enemy") {this.tank.bulletIiLive = false;}} else {switch(this.direct) {case 0:this.y -= this.speed;break;case 1:this.y += this.speed;break;case 2:this.x -= this.speed;break;case 3:this.x += this.speed;break;}}}
}
Draw 绘制代码
//绘制爆炸效果
function drawEnemyBomb() {for(var i = 0; i <bombs.length; i++) {var bomb = bombs[i];if(bomb.isLive) {if(bomb.blood > 6) { //789var img1 = new Image();img1.src = "IMG/bomb_1.gif";var x = bomb.x;var y = bomb.yimg1.onload = function() {ctx.drawImage(img1, x, y, 30, 30);}} else if(bomb.blood > 3) { //456var img2 = new Image();img2.src = "IMG/bomb_2.gif";var x = bomb.x;var y = bomb.yimg2.onload = function() {ctx.drawImage(img2, x, y, 30, 30);}} else { //123var img3 = new Image();img3.src = "IMG/bomb_3.gif";var x = bomb.x;var y = bomb.yimg3.onload = function() {ctx.drawImage(img3, x, y, 30, 30);}}bomb.bloodDown();if(bomb.blood <= 0) {bombs.splice(i, 1);}}}
}//绘制子弹
function drawHeroBullet() {for(var i = 0; i < heroBullets.length; i++) {var heroBullet = heroBullets[i];if(heroBullet && heroBullet.isLive) {ctx.fillStyle = arrColor[rdm1(6)];ctx.fillRect(heroBullet.x + 8, heroBullet.y + 11, 3, 3);}}
}
function drawenemyBullet() {for(var i = 0; i < enemyBullets.length; i++) {var enemyBullet = enemyBullets[i];if(enemyBullet && enemyBullet.isLive) {ctx.fillStyle = arrColor[rdm1(6)];ctx.fillRect(enemyBullet.x + 8, enemyBullet.y + 11, 3, 3);}}
}//绘制坦克
function drawTank(tank) {if(tank && tank.isLive) {if(tank.direct == 0 || tank.direct == 1) {ctx.beginPath();ctx.fillStyle = tank.color;//左矩形ctx.fillRect(tank.x, tank.y, 5, 30);//右矩形ctx.fillRect(tank.x + 15, tank.y, 5, 30);//中矩形ctx.fillRect(tank.x + 6, tank.y + 5, 8, 20);//中矩形里的圆ctx.fillStyle = "#91DAF2";ctx.arc(tank.x + 10, tank.y + 15, 4, 0, Math.PI * 2, true);ctx.fill();ctx.strokeStyle = "#0000F1";ctx.lineWidth = 1.5;ctx.beginPath();ctx.moveTo(tank.x + 10, tank.y + 15);if(tank.direct == 0) {ctx.lineTo(tank.x + 10, tank.y);} else {ctx.lineTo(tank.x + 10, tank.y + 30);}ctx.stroke();ctx.closePath();} else {ctx.beginPath();ctx.fillStyle = tank.color;//左矩形ctx.fillRect(tank.x, tank.y, 30, 5);//右矩形ctx.fillRect(tank.x, tank.y + 15, 30, 5);//中矩形ctx.fillRect(tank.x + 5, tank.y + 6, 20, 8);//中矩形里的圆ctx.fillStyle = "#91DAF2";ctx.arc(tank.x + 15, tank.y + 10, 4, 0, Math.PI * 2, true);ctx.fill();ctx.strokeStyle = "#0000F1";ctx.lineWidth = 1.5;ctx.beginPath();ctx.moveTo(tank.x + 15, tank.y + 10);if(tank.direct == 3) {ctx.lineTo(tank.x + 30, tank.y + 10);} else {ctx.lineTo(tank.x, tank.y + 10);}ctx.stroke();ctx.closePath();}}}
Bomb 爆炸对象
//爆炸产生位置
function Bomb(x, y) {this.x = x;this.y = y;this.isLive = true;this.blood = 9this.bloodDown = function() {if(this.blood > 0) {this.blood--;} else {this.isLive = false;}}
}
//敌人爆炸
function enterBomb(enemyTank) {heroBullet.isLive = false;enemyTank.isLive = false;var bomb = new Bomb(enemyTank.x, enemyTank.y);bombs.push(bomb);ene--;if(ene == 0) {gameEnd();}gradeadd();
}
//自己爆炸
function heroBomb(i) {enemyBullets[i].isLive = false;hero.isLive = false;var bomb = new Bomb(hero.x, hero.y);bombs.push(bomb);gameEnd();
}
Use 击败和重叠功能
//击败敌机
function isHitEnemyTank() {// console.log(111111);for(var i = 0; i < heroBullets.length; i++) {var heroBullet = heroBullets[i];if(heroBullet && heroBullet.isLive) {for(var j = 0; j < enemyTanks.length; j++) {var enemyTank = enemyTanks[j];if(enemyTank && enemyTank.isLive) {switch(enemyTank.direct) {case 0:case 1:if((heroBullet.x + 3 >= enemyTank.x) &&(heroBullet.x <= enemyTank.x + 20) &&(heroBullet.y + 3 >= enemyTank.y) &&(heroBullet.y <= enemyTank.y + 30)) {enterBomb(enemyTank);}break;case 2:case 3:if((heroBullet.x + 3 >= enemyTank.x) &&(heroBullet.x <= enemyTank.x + 30) &&(heroBullet.y + 3 >= enemyTank.y) &&(heroBullet.y <= enemyTank.y + 20)) {enterBomb(enemyTank);}break;}}}}}
}//击败自己
function isHitHeroTank() {// console.log(111111);for(var i = 0; i < enemyBullets.length; i++) {var enemyBullet = enemyBullets[i];if(enemyBullet && enemyBullet.isLive) {// for(var j = 0; j < enemyTanks.length; j++) {// var enemyTank = enemyTanks[j];if(hero && hero.isLive) {switch(hero.direct) {case 0:case 1:if((enemyBullet.x + 3 >= hero.x) &&(enemyBullet.x <= hero.x + 20) &&(enemyBullet.y + 3 >= hero.y) &&(enemyBullet.y <= hero.y + 30)) {heroBomb(i);}break;case 2:case 3:if((enemyBullet.x + 3 >= hero.x) &&(enemyBullet.x <= hero.x + 30) &&(enemyBullet.y + 3 >= hero.y) &&(enemyBullet.y <= hero.y + 20)) {heroBomb(i);}break;}}// }}}
}//重叠问题
function isTankTogeter() {for(var i = 0; i < enemyTanks.length; i++) {for(var j = 0; j < enemyTanks.length; j++) {var temp = enemyTanks[i];var temp2 = enemyTanks[j];if(!temp.isLive && !temp2.isLive) {continue;}if(i != j) {switch(temp.direct) {case 0:if((temp2.x > temp.x && temp2.x < temp.x + 21 && temp2.y > temp.y && temp2.y < temp.y + 30) ||(temp2.x + 20 > temp.x && temp2.x + 20 < temp.x + 21 && temp2.y > temp.y && temp2.y < temp.y + 30) ||(temp2.x + 20 > temp.x && temp2.x + 20 < temp.x + 21 && temp2.y + 20 > temp.y && temp2.y + 30 < temp.y + 30) ||(temp2.x > temp.x && temp2.x < temp.x + 21 && temp2.y + 20 > temp.y && temp2.y + 20 < temp.y + 30)) {temp.direct = 1;temp2.direct = 2;temp.y += 5;temp2.x -= 5;}breakcase 1:if((temp2.x > temp.x && temp2.x < temp.x + 21 && temp2.y > temp.y && temp2.y < temp.y + 30) ||(temp2.x + 20 > temp.x && temp2.x + 20 < temp.x + 21 && temp2.y > temp.y && temp2.y < temp.y + 30) ||(temp2.x + 20 > temp.x && temp2.x + 20 < temp.x + 21 && temp2.y + 20 > temp.y && temp2.y + 30 < temp.y + 30) ||(temp2.x > temp.x && temp2.x < temp.x + 21 && temp2.y + 20 > temp.y && temp2.y + 20 < temp.y + 30)) {temp.direct = 0;temp2.direct = 3;temp.y -= 5;temp2.x += 5;}breakcase 2:if((temp2.x > temp.x && temp2.x < temp.x + 21 && temp2.y > temp.y && temp2.y < temp.y + 30) ||(temp2.x + 20 > temp.x && temp2.x + 20 < temp.x + 21 && temp2.y > temp.y && temp2.y < temp.y + 30) ||(temp2.x + 20 > temp.x && temp2.x + 20 < temp.x + 21 && temp2.y + 20 > temp.y && temp2.y + 30 < temp.y + 30) ||(temp2.x > temp.x && temp2.x < temp.x + 21 && temp2.y + 20 > temp.y && temp2.y + 20 < temp.y + 30)) {temp.direct = 3;temp2.direct = 0;temp.x += 5;temp2.y -= 5;}break;case 3:if((temp2.x > temp.x && temp2.x < temp.x + 21 && temp2.y > temp.y && temp2.y < temp.y + 30) ||(temp2.x + 20 > temp.x && temp2.x + 20 < temp.x + 21 && temp2.y > temp.y && temp2.y < temp.y + 30) ||(temp2.x + 20 > temp.x && temp2.x + 20 < temp.x + 21 && temp2.y + 20 > temp.y && temp2.y + 30 < temp.y + 30) ||(temp2.x > temp.x && temp2.x < temp.x + 21 && temp2.y + 20 > temp.y && temp2.y + 20 < temp.y + 30)) {temp.direct = 0;temp2.direct = 1;temp.x -= 5;temp2.y += 5;}break;}}}}
}
知识点
定时器setInterval
当想给调用的函数传参时需要加 “”
heroBullets[heroBullets.length - 1].timer = setInterval("heroBullets[" + (heroBullets.length - 1) + "].run()", 50);
js中定时器调用函数时为什么会有引号
定时器越走越快和清理问题
每一次给timer赋值都是在创建新的定时器对象,而且之前的定时器也并没有被清除,所以这时候调用clearInterval(timer)清除的只是最后一个被创建的定时器对象罢了。
解决多次点击/触发定时器越走越快
for循环清理所有定时器
坦克中的继承用法
this.chh = Tank;
this.chh(x, y, direct, color);
js中继承的几种方式
.4187)
定时器越走越快和清理问题
每一次给timer赋值都是在创建新的定时器对象,而且之前的定时器也并没有被清除,所以这时候调用clearInterval(timer)清除的只是最后一个被创建的定时器对象罢了。
解决多次点击/触发定时器越走越快
for循环清理所有定时器
坦克中的继承用法
this.chh = Tank;
this.chh(x, y, direct, color);
js中继承的几种方式