◆摘? 要:本文從坦克大戰(zhàn)游戲的設(shè)計(jì)細(xì)節(jié)入手,深入探討了其數(shù)據(jù)結(jié)構(gòu)和算法的設(shè)計(jì),對(duì)于一般程序設(shè)計(jì)具有指導(dǎo)意義,使用的編程語言是html5 + JavaScript,軟件開發(fā)環(huán)境使用Visual Studio Code,運(yùn)行環(huán)境為網(wǎng)頁(yè)。
◆關(guān)鍵詞:游戲軟件;數(shù)據(jù)結(jié)構(gòu);算法設(shè)計(jì)
計(jì)算機(jī)程序設(shè)計(jì)教學(xué)中,有一個(gè)重點(diǎn),那就是二維數(shù)組的構(gòu)建和使用。本文介紹了坦克大戰(zhàn)小游戲的軟件開發(fā)細(xì)節(jié),這些討論可以幫助學(xué)生增加對(duì)這種數(shù)據(jù)結(jié)構(gòu)的理解。很多小游戲的界面主要是一種平面關(guān)系的體現(xiàn),其內(nèi)部數(shù)據(jù)結(jié)構(gòu)和外面界面設(shè)計(jì)基本上都應(yīng)該是二維數(shù)組的應(yīng)用。在進(jìn)行游戲程序設(shè)計(jì)中,其數(shù)據(jù)結(jié)構(gòu)的設(shè)計(jì)占據(jù)了主要的地位,其次其游戲策略決定了其算法的設(shè)計(jì)。本文結(jié)合坦克大戰(zhàn)游戲開發(fā)細(xì)節(jié)討論對(duì)涉及到的部分?jǐn)?shù)據(jù)結(jié)構(gòu)和游戲策略進(jìn)行討論。具體實(shí)現(xiàn)中html文件用來實(shí)現(xiàn)網(wǎng)頁(yè)上的文字、圖片或音頻文件的加載,具體顯示的實(shí)現(xiàn)以及其他功能的實(shí)現(xiàn)是啟用js文件,css文件是用來美化網(wǎng)頁(yè),結(jié)構(gòu)與樣式的分離。
一、總體設(shè)計(jì)
本游戲的總體設(shè)計(jì)目標(biāo)為網(wǎng)頁(yè)上運(yùn)行的坦克打坦克。為了達(dá)成這款游戲的設(shè)計(jì),我們必須考慮和設(shè)計(jì)以下一些因素:游戲開始界面的設(shè)計(jì),不同關(guān)數(shù)的背景圖片顯示,己方坦克的射擊,己方坦克在發(fā)射子彈的時(shí)候的子彈飛行軌跡和是否擊中敵方坦克,自己的坦克被擊中的時(shí)候所產(chǎn)生的判定,游戲之中所獲得的分?jǐn)?shù)的計(jì)算和顯示,還需要判斷坦克之間是否相撞等游戲結(jié)束的判定。
功能和界面設(shè)計(jì)方面主要是簡(jiǎn)潔和自然,主界面上只有一個(gè)start(開始)按鈕可點(diǎn)。然后是選擇模式界面,當(dāng)鼠標(biāo)移到兩個(gè)坦克里,會(huì)出現(xiàn)“簡(jiǎn)單模式”和“困難模式”字樣。點(diǎn)擊后可以進(jìn)入相應(yīng)的界面。徹底搞清楚機(jī)制后可以相應(yīng)增加更多的關(guān)數(shù)。在簡(jiǎn)單模式中,點(diǎn)擊左上角的橙色音量圖標(biāo)開啟音樂(默認(rèn)進(jìn)入游戲是關(guān)閉的),上下左右方向鍵用來控制己方坦克的移動(dòng),空格鍵用來給坦克發(fā)射子彈,當(dāng)分?jǐn)?shù)達(dá)到一定條件時(shí),子彈移動(dòng)速度會(huì)變快和子彈一次出現(xiàn)兩顆等。在簡(jiǎn)單模式中,只要己方坦克碰到敵方坦克或敵方子彈后游戲都會(huì)結(jié)束;在困難模式中啟用了一個(gè)默認(rèn)的護(hù)甲值,兩方坦克都是中兩次子彈后才會(huì)消失,但是兩方坦克相撞的話就馬上結(jié)束游戲。另外在第二關(guān)中我方子彈是斜著飛的。如果我方坦克被子彈擊中一次后,子彈移動(dòng)速度就不會(huì)提升了。
二、數(shù)據(jù)結(jié)構(gòu)和游戲策略探討
在坦克大戰(zhàn)游戲中,我們需要一個(gè)平面的數(shù)據(jù)結(jié)構(gòu)來展示空中的俯視效果,這主要是通過二維數(shù)組來實(shí)現(xiàn)。我們需要不斷的刷新己方飛機(jī)的位置和隨機(jī)出現(xiàn)的敵機(jī)的位置,然后就是雙方的子彈飛行軌道設(shè)計(jì)等。
下面先討論己方坦克的數(shù)據(jù)結(jié)構(gòu)和敵方坦克的數(shù)據(jù)結(jié)構(gòu),首先統(tǒng)一成下面的設(shè)計(jì),然后在分別調(diào)用處理雙方不同的坦克。
用圖片直接表示坦克是比較簡(jiǎn)潔的編程方式,參數(shù)image表示照片,x,y表示出現(xiàn)在屏幕上的位置,width和height表示圖片的寬度和高度,frm表示當(dāng)前第幾幀,dis表示時(shí)間間隔,hp表示坦克的護(hù)甲。圖片不同和出現(xiàn)位置的不同就代表了雙方不同的坦克。
然后是關(guān)于子彈類的設(shè)計(jì),和坦克實(shí)際上是差不多的參數(shù)設(shè)計(jì),不過相比于坦克類,多了一個(gè)speed表示子彈的速度。剛開始是初始速度,當(dāng)達(dá)到一定條件后可以通過修改設(shè)置使得子彈速度變快。另外在復(fù)雜模式的情況下,考慮把子彈變成斜著飛,這樣增添了設(shè)計(jì)的變化性。
在屏幕上畫出坦克時(shí),主要考慮敵方坦克要會(huì)自己移動(dòng),所以在畫己方坦克的時(shí)候this.x和this.y這兩行不要,使之受人工鍵盤控制移動(dòng)即可。
Tank.prototype.draw = function(ctx){
ctx.save();
ctx.translate(this.x, this.y);
ctx.drawImage(this.image, this.frm*this.width, 0, this.width, this.height,
0, 0, this.width, this.height);
ctx.restore();
this.y++;
this.x = this.originX + 20*Math.sin(Math.PI / 100*this.y);
this.dis++;
if (this.dis >= 3){
this.dis = 0;
this.frm++;
if (this.frm >= this.n) this.frm = 0;
}
};
在坦克移動(dòng)的設(shè)計(jì)方面,通過dx和dy兩個(gè)偏移量的設(shè)計(jì),可以達(dá)成移動(dòng),但是要注意不能移動(dòng)到游戲邊界外面去了,所以要設(shè)計(jì)一下意外處理,當(dāng)坦克要移動(dòng)出游戲邊界時(shí),使之原地不動(dòng)。
Tank.prototype.move = function (dx, dy){
this.x += dx;
this.y += dy;
//下面處理脫離游戲界面的坦克
if (this.x < 0 || this.x > 645)
this.x -= dx;
if (this.y < 0 || this.y > 500)
this.y -= dy;
};
在屏幕刷新時(shí)需要不斷地畫下移的坦克,源碼如下:
Tank.prototype.draw = function(ctx){
ctx.save();
ctx.translate(this.x, this.y);
ctx.drawImage(this.image, this.frm*this.width, 0, this.width, this.height,
0, 0, this.width, this.height);
ctx.restore();
this.y++;
this.x = this.originX + 20*Math.sin(Math.PI / 100*this.y);
this.dis++;
if (this.dis >= 3){
this.dis = 0;
this.frm++;
if (this.frm >= this.n) this.frm = 0;
}
};
如果坦克沒有動(dòng),那么重畫的過程只需要把this.y++;和this.x變化的兩個(gè)語句刪除即可。
在檢測(cè)有沒有兩個(gè)坦克相互碰撞的設(shè)計(jì)上,主要考察兩個(gè)坦克圖片邊界位置是否有相互重疊的部分,分為四個(gè)方向的相互重疊先行做一個(gè)通用的函數(shù)設(shè)計(jì),然后調(diào)用它來判斷兩個(gè)坦克之間的邊界是否重疊。
function isColliding(ax, ay, aw, ah, bx, by, bw, bh)
{
if(ay > by + bh || by > ay + ah || ax > bx + bw || bx > ax + aw)
return false;
else
return true;
}
Tank.prototype.hitTestObject = function(tankobj){
if(isColliding(this.x, this.y, this.width, this.height,
tankobj.x, tankobj.y, tankobj.width, tankobj.height))
return true;
else
return false;
};
在第二模式中,需要考慮子彈的左偏,下面是相關(guān)源碼,而子彈右偏時(shí)只需要把this.x-=0.5;中的減法改為加法即可。
Bullet.prototype.draw3 = function (ctx){
ctx.save();? //保存當(dāng)前環(huán)境的狀態(tài)
ctx.translate(this.x, this.y);? //重新映射畫布上的位置
ctx.drawImage(this.image, this.frm*this.width, 0, this.width, this.height,
0, 0, this.width, this.height);
ctx.restore();? //返回之前保存過的路徑狀態(tài)和屬性
this.x-=0.5;
this.y-=this.speed;
this.dis++;
if (this.dis >= 10){
this.dis = 0;
this.frm++;
if (this.frm >= 2) this.frm = 0;
}
};
在游戲過程中,用戶的按鍵決定了坦克的移動(dòng)和射擊等功能,主要是通過每個(gè)鍵碼來進(jìn)行判斷的。鍵碼為32時(shí),說明是空格,于是需要處理子彈的射擊,而鍵碼為37、38、39和40時(shí)分別對(duì)應(yīng)左右上下的處理。
function onkeydown(e) {
if (e.keyCode==32){
if (score < 100)
bullets.push(new Bullet(image4, mytank.x+37, mytank.y-10));
else{
bullets.push(new Bullet(image4, mytank.x+25, mytank.y-10));
bullets.push(new Bullet(image4, mytank.x+49, mytank.y-10));
}
}
else if (e.keyCode==37)
{
if (score < 100)
mytank.move(-10, 0);
else
mytank.move(-20, 0);
}
}
其他幾個(gè)方向的處理源碼非常類似,就不在這里列出了。限于篇幅,諸如主界面設(shè)計(jì)、背景音樂設(shè)計(jì)等其他設(shè)計(jì)細(xì)節(jié)也不深入探討了。
三、總結(jié)
要想設(shè)計(jì)好一款速度快、效果好、界面優(yōu)的游戲,必須對(duì)數(shù)據(jù)結(jié)構(gòu)有著充分的了解和實(shí)際應(yīng)用的能力。二維數(shù)組是游戲開發(fā)中特別有用的一種數(shù)據(jù)結(jié)構(gòu)。通過坦克大戰(zhàn)這個(gè)游戲軟件的討論可以增加對(duì)二維數(shù)組更深的理解。
作者簡(jiǎn)介
馬春江(1963.01-),男,籍貫河南,研究生班,講師,湖北汽車工業(yè)學(xué)院計(jì)算機(jī)系,研究方向?yàn)閿?shù)據(jù)結(jié)構(gòu)、算法設(shè)計(jì)、圖形處理、動(dòng)畫設(shè)計(jì)等。