1. Summarize key points and problems encountered
1. Inheritance in JavaScript, it is best for the parent class to only provide method sharing and the attributes are written to their respective subclasses to avoid the confusion of the constructors of the parent class and the subclass.
2. Prototype simulates inherited code and should be written before all method definitions. Otherwise, the prototype object is changed and the method becomes undefined, such as:
The code copy is as follows:
Hero.prototype = new Tank (0, 0, 0);
Hero.prototype.constructor = Hero;
Hero.prototype.addLife = function(){
this.lifetimes++;
document.querySelector("#life").innerHTML = hero.lifetimes;
}
3. When canvas drawing graphics, except for rectangles, everything else must be added with ctx.beginPath(); and ctx.closePath(); otherwise unexpected errors will occur.
4. The concat function can merge arrays, or return an element to a new array
5. Image will be loaded after the src attribute is assigned, but if the image is not loaded, it will cause failure, so use the onload event to handle it.
6. Expand the Array function and delete the specified element
The code copy is as follows:
//Extend to delete the specified element
Array.prototype.deleteElement = function (obj) {
if (obj) {
for (var i = 0; i < this.length; i++) {
if (this[i] === obj) {
this.splice (i, 1);
}
}
}
}
7. Timer settings, the first parameter of the setInterval("fun", 1000) method can be a string, such as "hero.say()", similar to eval, will execute this string of code, so it can give the function The upper parameter and also specifies the running context of this function. But if the pass in is a handle to a function, it cannot take parameters and the context cannot be specified. In addition to the first method to solve it, I used closures to solve this problem
The code copy is as follows:
//Timer, exercise by yourself
this.timer = setInterval ((function (context) {
return function () {
Bullet.prototype.move.call (context)
}
}) (this), 30);
I saved the current execution environment and called the call method to execute manually.
8. The functional design of the method, in addition to the functions, should include conditional detection of this function, such as move, which should include under what circumstances can be moved and wherever you can't move. This detection should not be placed externally.
9. When writing code, you should not think about design or optimization. You should first realize the functions, then talk about optimization, or design and then implement them. Be clear in thinking, don’t be confused, and focus on one thing.
10. Javascript does not have sleep function, you can create a variable as a buffer to achieve the purpose of interval execution
2. Code implementation
1. This program is divided into Bomb.js, Bullet.js, Draw.js, Tank.js, index.html, img, music,
2. Final effect
3. Code
1.index.html
The code copy is as follows:
<!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 () {
//Canvas information
width = document.getElementById ('Map').width;
height = document.getElementById ('Map').height;
ctx = document.getElementById ('Map').getContext ('2d');
//Initial page
var starImg = new Image ();
starImg.src = "img/star.jpg";
starImg.onload = function () {
ctx.drawImage (starImg, 0, 0, width, height);
}
//Keyboard monitoring and return to start the game
document.body.onkeydown = function () {
var keycode = event.keyCode;
switch (keycode) {
case 13:
//Initialization parameters
init ()
//Refresh the page
setInterval (draw, 30);
document.body.onkeydown = gameControl;
break;
}
}
}
function init () {
//Players and computers
hero = new Hero (100, 300, 0);
enemys = [];
for (var i = 0; i < 3; i++) {
enemys.push (new Enemy (100 + i * 50, 0, 2));
}
//Merge array
allTank = enemys.concat (hero);
//bomb
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;//left
case 83:
hero.moveDown ();
break;//Next
case 87:
hero.moveUp ();
break;//On
case 68:
hero.moveRight ();
break;//right
case 74:
hero.shot ();
break;
case 49:
hero.addLife ()
break;
}
}
//Extend to delete the specified element
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>
<canvas id="Map">
</canvas>
<audio id="music" autoplay="autoplay">
<source src="music/111.wav">
</audio>
</div>
<div id="guide">
<p>Press Enter to start the game</p>
<p>Press 1 key to increase life, the default is 1</p>
<p>Remaining life count:<label id="life">1</label></p>
<div id="data">
</div>
</div>
</body>
</html>
2.Draw.js
The code copy is as follows:
/**
* Created by Alane on 14-3-18.
*/
function draw(){
//Detection of life and death of bullets and tanks
checkDead();
//Clear the canvas
ctx.clearRect(0,0,500,400);
//Draw the player
if(!hero.isdead){
drawTank(hero);
}else{
hero.cutLife();
}
//Draw enemy tanks
for (var i = 0; i < enemys.length; i++) {
drawTank(enemys[i]);
}
//Draw enemy bullets
for(var j=0;j<enemys.length;j++){
var temp = enemys[j].bulletsList;
for (var i = 0; i < temp.length; i++) {
drawBullet(temp[i]);
}
}
//Draw the player's bullet
var temp = hero.bulletsList;
for (var i = 0; i < temp.length; i++) {
drawBullet(temp[i]);
}
//Draw bomb
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(){
//Detection of the life and death of the enemy bullet
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);
}
}
}
//Detection of player bullet life and death
var temp = hero.bulletsList;
for (var i = 0; i < temp.length; i++) {
var o = temp[i];
if(o.isdead){
temp.deleteElement(o);
}
}
//Detection of the life and death of the enemy tank
for (var i = 0; i < enemys.length; i++) {
var o = enemys[i];
if(o.isdead){
enemys.deleteElement(o);
}
}
}
Bomb.js
The code copy is as follows:
/**
* 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
The code copy is as follows:
/**
* Created by Alane on 14-3-7.
*/
/**
* direct 0 on
* 1 Right
* 2
* 3 left
* @param x
* @param y
* @param direct
* @constructor
*/
//*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** ***************************************************/
//Tank parent class
function Tank (x, y, direct) {
this.speed = 2;
}
Tank.prototype.moveUp = function () {
//Border detection
if (this.y < 0) {
//Change direction
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;
}
//Change direction
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)
}
//Shot bullets
Tank.prototype.shot = function () {
if(this.isdead){
return;
}
if (this.bulletsList.length < this.maxBulletSize) {
// Create a new bullet
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;
}
//Put in the magazine
this.bulletsList.push (bullet);
}
}
//*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** ***************************************************/
//Player
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;
}
//*************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** ***************************************************/
//Enemy tank
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;
//Timer, automatic movement
this.timer1 = setInterval ((function (context) {
return function () {
//move
Enemy.prototype.move.call (context);
}
}) (this), 30);
//Timer, shooting
this.timer2 = setInterval ((function (context) {
return function () {
//shooting
Tank.prototype.shot.call (context);
}
}) (this), 2000);
//Timer, change direction
this.timer3 = setInterval ((function (context) {
return function () {
//shooting
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
The code copy is as follows:
/**
* 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;
//Timer, exercise by yourself
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;
}
//Border detection
if (this.y < 0 || this.x > width || this.y > height || this.x < 0) {
clearInterval (this.timer);
this.isdead = true;
}
//Collision detection detects enemy tanks
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;
}
}
}
Source code download