一、總結關鍵點和遇到的問題 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功能,刪除指定元素 複製代碼
程式碼如下:
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 複製代碼 程式碼如下:
標題>
正文{
字型:14px "sans-serif"
}
#Map {
背景顏色:#000000;
}
.show {
浮動:左
}
#guide {
浮動:左;
寬度:200px;
高度:390px;
左邊距:5px ;
背景:#CCCCCC;
內邊距:5px;
}
風格>
window.onload = function ( ) {
//消耗資訊
width = document.getElementById('Map').width;
height = document.getElementById('地圖').height;
ctx = document.getElementById( '地圖').getContext('2d');
//初始頁面
var starImg = new Image ();
starImg.src = "img/star.jpg";
starImg. onload = function () {
ctx.drawImage(starImg, 0, 0, 寬度, 高度);
}
//鍵盤監聽回車開始遊戲 document.body.onkeydowndown = function () { var keycode = event.keyCode; switch (keycode) { case 13: //初始化參數 init () //刷新頁面 setInterval (draw, 30); document.body.onkeydown = gameControl; 休息; } } } function init () { } function init () { } >//玩家與電腦 hero = new Hero (100, 300, 0); 敵人=[]; for (var i = 0; i enemys .push (new Enemy (100 i * 50, 0, 2)); } //修復備份 allTank = nationes.concat (hero); //炸彈 炸彈= []; im = 新圖像(); im2 = 新圖像(); im3 = 新圖像(); 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 (); case 68: hero.moveRight (); break;//右邊 case 74: hero.shot (); 休息; 案例49: hero.addLife () break; }} } //擴充刪除指定元素 Array.prototype.deleteElement = function (obj) { if (obj) { for (var i = 0; i if (this[i] === obj) { this.splice (i, 1); } } } } ; 頭> 按回車鍵開始遊戲
按1鍵增加生命,預設是1
剩餘生命數:1
身體>
複製程式碼程式碼如下:
/** * 由 Alane 於 2018 年 3 月 14 日創立。 */ function draw(){ //偵測子彈與水箱生死 checkDead(); //清空畫布 ctx.clearRect(0,0,500,400); //畫玩家 if(!hero.isdead){ drawTank(hero); }else{ hero.cut(hero); ); } //畫敵人坦克 for (var i = 0; i drawTank(enemys[i]); } drawTank(enemys[i]); } //畫敵人子彈 for(var j=0;jvar temp = enemys[j].bulletsList; for (var i = 0; i drawBullet(temp[i]); } } //畫玩家子彈 var temp = hero.bulletsList; for (var i = 0; i drawBullet(temp[i]); } //畫炸彈 for(var i=0;idrawBown(Bombs[i]); } } function drawTank(tank){ var x = tank. ; var y = tank.y; ctx.fillStyle = tank.color; if(tank.direct == 0 || tank.direct ==2){ ctxx .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 = bulletcolor; >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,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.lifeBombs.deleteElement(obj); } } function checkDead(){ //偵測敵人子彈生死 for(var j=0;jvar temp = enemys[j].bulletsList; for (var i = 0; i var o = temp[i]; if(o.isdead){ temp.deleteElement(o); } } } //偵測玩家子彈生死 var temp = hero.bulletsList; for (var i = 0; i var o = temp[i]; if(o.isdead){ temp.deleteElement(o); } } //偵測敵人坦克生死 for (var i = 0; i var o = enemys[i]; if(o.isdead){ enemys.deleteElement(o); } }
}
程式碼如下: /** * 由 Alane 於 2018 年 3 月 14 日創立。 */ function Bomb(x,y){ this.life = 12 this.x = x; this.y = y;
}
Bomb.prototype.lifeDown = function(){
this.life--;
複製程式碼 程式碼如下:
/** * 由 Alane 於 14-3-7 所創造。 */ /** * direct 0 上 * 1 右 * 2 下 * 3 左 * @param x * @param y * @param direct * @param y * @param direct * @param y * @param direct * @param y * @param direct * @param y * @param direct * @constructor */ //************************************************** **** **** *********************************************/ //坦克父類 function Tank (x, y, 直接) { this.speed = 2; } Tank.prototype.moveUp = function () { //邊界偵測 if (this.y //方向換 this .changeDirect(); 回傳; } this.y -= this.speed; this.direct = 0; } Tank.prototype.moveDown = function () { if (this.y > height - 30) { this.changeDirect (); 回傳; } this.y = this.speed; this.direct = 2; } Tank.prototype.moveLeft = function () { if (this.x this.changeDirect (); 回傳; } this.x -= this.speed; this.direct = 3; } Tank.prototype.moveRight = function () { if (this.x > width - 30) { this.changeDirect (); 回傳; } this.x = this.speed; this.direct = 1; } //變換方向 Tank.prototype.changeDirect = function () { while (true) { var temp = Math.round (Math.隨機() * 3); if (this.direct != temp) { this.direct = temp; 休息; } } //alert("x=" this.x " y=" this.y " direct=" this.direct) } //拍攝子彈 Tank.prototype.shot = function () { if(this.isdead){ return; } if (this.bulletsList.length //新子彈 varBullet = null; switch (this.direct) { case 0: bullet = new Bullet (this.x 10, this.y - 2, 0, this.color); 休息; 情況1: bullet = new Bullet(this.x 32, this.y 10, 1, this.color); 休息; 情況2: bullet = new Bullet(this.x 10, this.y 32, 2, this.color); 休息; 情況3: bullet = new Bullet (this.x - 2, this.y 10, 3, this.color); 休息; } //放入彈夾 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 = 直接; this.bulletsList = []; this.maxBulletSize = 10; this.newlife = null; } Hero.prototype = new Tank(0, 0, 0); Hero.prototype.constructor = 英雄; 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 = '藍色'; this.x = x; this.y = y; this.direct = 直接; this.bulletsList = []; this.maxBulletSize = 1; //計時器,自動移動 this.timer1 = setInterval ((function (context) { return function () { //移動 敵人。原型.move.call (上下) } }) (this), 30); //計時器、射擊 this.timer2 = setInterval ((function (context) { return function () { //射擊 Tank.prototype.shot.呼叫(上下文) } }) (this), 2000); //定時器,變換方向 this.timer3 = setInterval ((function (context) { return function () { //取 Tank.prototype.changeDirect .call (上下) } }) (這個), 3000); } Enemy.prototype = new Tank(0, 0, 0); Enemy.prototype.constructor = Enemy; Enemy.prototype.move = function () { switch (this.direct) { case 0: this.moveUp (); 休息; 情況1: this.moveRight(); 休息; 情況2: this.moveDown();
休息;
情況3:
this.moveLeft();
複製代碼程式碼如下:
/**
* 由 Alane 於 2011 年 3 月 14 日創立。
*/
function Bullet (x, y, direct, color) {
this.isdead = false;
this.x = x;
this.y = y;
this.direct = 直接;
this.speed = 4;
this.color = 顏色;
//計時器,自行運動
this.timer = setInterval ((function (context) {
return function () {
Bullet.prototype.move.call (context)
}
}) (這個), 30);
}
Bullet.prototype.move = function () {
switch (this.direct) {
case 0:
this.y -= this.speed;
休息;
情況1 1 :
this.x = this.speed;
休息;
情況2:
this.y = this.speed;
休息;
情況3:
this. x -= this.speed;
休息;
}
//邊界偵測
if (this.y 寬度|| this.y > 高度|| this.x clearInterval(this.timer);
this.isdead = true;
}
//碰撞偵測敵方偵測坦克
for (var i=0;i
var temp = allTank[i]; if(temp.isdead){ 繼續; } switch ( temp.direct){ 情況0: 情況2:if(this.x>temp.x && this.xtemp .y&& this.yif(this.color == temp.color){ break; } Bombs.push(new Bomb(temp.x-10,temp.y-10)) ; clearInterval(this.timer); this.isdead = true; temp.isdead = true; }打破 情況1: 情況3:if(this. x>temp.x && this.xtemp.y&& this.yif(this.color == temp.color){ break; } Bombs.push(new Bomb(temp.x-10,temp.y-10)); clearInterval(this.timer); this.isdead = true; temp.isdead = true; }休息; } } }
源碼下載