許益凡 薛益鴿
計(jì)文章編號(hào): 2095-2163(2018)03-0235-06中圖分類(lèi)號(hào): 文獻(xiàn)標(biāo)志碼: A
摘要: 關(guān)鍵詞: (College of Information Engineering, Wenzhou Business College, Wenzhou Zhejiang 325035, China)
Abstract: With the rise of the gaming market, various programming languages are widely used,JAVA language is the most widely used and the highest using proportion. This article takes the extremely classic magic tower game as an example, with the help of the JAVA languages object-oriented characteristic using the programming techniques: package and inherited.
Key words:
作者簡(jiǎn)介: 許益凡(1997-),男,本科生,主要研究方向:JAVA的桌面應(yīng)用、前端塔建; 薛益鴿(1990-),男,碩士,助教,主要研究方向:計(jì)算智能。
通訊作者: 收稿日期: 引言
游戲作為大眾生活必不可少的一項(xiàng)娛樂(lè)方式,近年來(lái)發(fā)展勢(shì)頭良好。開(kāi)發(fā)游戲的語(yǔ)言有很多種,JAVA是當(dāng)前最流行最熱門(mén)的一種編程語(yǔ)言, 因其是一種可以撰寫(xiě)跨平臺(tái)應(yīng)用程序的面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言\[3\],并具有卓越的通用性、高效性和平臺(tái)移植性\[2\],同時(shí),JAVA語(yǔ)言在游戲開(kāi)發(fā)方面,又因?yàn)槠湔Z(yǔ)言嚴(yán)謹(jǐn)、可讀性強(qiáng)、高度的便利性等優(yōu)勢(shì),在游戲開(kāi)發(fā)中具有非常重要的地位\[3\]。
魔塔游戲是一款策略類(lèi)的RPG(角色扮演)游戲。游戲雖不大卻充滿難度,往往一步小小的失誤就會(huì)讓游戲失敗,前功盡棄\[4\]。本文基于JAVA平臺(tái)對(duì)魔塔游戲進(jìn)行設(shè)計(jì)和開(kāi)發(fā)。整個(gè)游戲在Microsoft Windows 7 64位操作系統(tǒng)下,利用 IntelliJ IDEA 2017.2.4 x64開(kāi)發(fā)工具進(jìn)行開(kāi)發(fā)。游戲的菜單欄、操作欄和商品購(gòu)買(mǎi)使用鼠標(biāo)進(jìn)行操作;勇士移動(dòng)使用方向鍵上、下、左、右操作;打開(kāi)/關(guān)閉怪物手冊(cè)用快捷鍵A; 往上跳躍樓層用快捷鍵Z;往下跳躍樓層用快捷鍵X。
1需求分析及流程
1.1功能需求
通過(guò)對(duì)魔塔游戲的多次試玩和深入分析,可以發(fā)現(xiàn),該款游戲雖然像容易通關(guān)的地牢游戲,但每一小小的決定都影響著能否救出公主順利通關(guān)。如圖1所示,此游戲有著繁多的屬性和獨(dú)有的傷害計(jì)算公式,傷害所扣除的血量并不是簡(jiǎn)單地直接攻擊減去防御,而是需要經(jīng)過(guò)傷害次數(shù)的參與計(jì)算。傷害次數(shù)是敵人的血量除去自身攻擊與敵方防御之差。最終損失的血量又是需要次數(shù)乘以敵人的攻擊與自身防御之差,若損失的血量超過(guò)擁有的血量則無(wú)法攻擊此怪物,此時(shí),若不提升自身能力則會(huì)卡在這個(gè)怪物面前,導(dǎo)致游戲失敗。
本文設(shè)計(jì)的游戲主要涉及下列功能模塊:戰(zhàn)斗以及血量的計(jì)算模塊、查看怪物手冊(cè)模塊、商店交易模塊、游戲的存檔與讀取模塊、重新開(kāi)始游戲模塊、退出游戲模塊、與NPC對(duì)話模塊、樓層跳轉(zhuǎn)和金手指系統(tǒng)模塊。功能結(jié)構(gòu)如圖2所示。
1.2游戲整體流程
游戲流程如下所述:游戲開(kāi)始,勇士出現(xiàn)在魔塔的一層,沒(méi)有配備裝備,只被賦予了初始的屬性和3把不同顏色的鑰匙。勇士行動(dòng)中遇到的事件如下:
(1)獲取物品。可提升勇士的攻擊防御等屬性,增加勇士的綜合實(shí)力,同時(shí),有機(jī)會(huì)獲取到特殊物品,特殊物品具有的特殊功能對(duì)游戲進(jìn)展有很大幫助。
(2)遭遇怪物。怪物種類(lèi)繁多,屬性也不近相同,如“小怪物”、“士兵隊(duì)長(zhǎng)”、“蝙蝠怪”、“骷髏怪”、“法師”、“衛(wèi)兵”等等。越高級(jí)的怪物屬性越高,所需勇士的實(shí)力也就越高。其中,魔王是整個(gè)游戲中最強(qiáng)大也是最困難的一個(gè)怪物類(lèi)NPC,魔王強(qiáng)具有強(qiáng)大的屬性和華麗的外觀,鎮(zhèn)守著去往救助公主的通道,只有打敗魔王才可以救出公主完成游戲。
(3)遇到商店。玩家可根據(jù)自身的購(gòu)買(mǎi)能力提升自身的屬性或等級(jí),更好地進(jìn)行游戲。
(4)NPC。在本文設(shè)計(jì)的游戲中,去除了一些購(gòu)買(mǎi)鑰匙的NPC,將其整合在商店和金手指中。同時(shí),保留了公主這個(gè)重要角色并添加了新的劇情:擊殺魔王后需解除公主手上的鎖鏈才能將她救走,則游戲成功,否則游戲失敗。流程如圖3所示。
2程序?qū)崿F(xiàn)
本款游戲具有以下3種特性:可靠性,即程序執(zhí)行時(shí)不會(huì)出現(xiàn)故障;可維護(hù)性,即在不破壞玩家數(shù)據(jù)的前提下進(jìn)行維護(hù)修復(fù)程序BUG;擴(kuò)展性,即在原有基礎(chǔ)上有更多的創(chuàng)新,使用戶不產(chǎn)生使用疲勞,開(kāi)發(fā)新的功能模塊\[5\]。下面,詳細(xì)介紹游戲中各功能的實(shí)現(xiàn)。
2.1游戲界面的實(shí)現(xiàn)
游戲界面是游戲開(kāi)發(fā)的點(diǎn)睛之筆。本文開(kāi)發(fā)的游戲界面在原版的基礎(chǔ)上增加了金手指菜單。游戲界面由以下3個(gè)板塊構(gòu)成:游戲主界面,顯示游戲的內(nèi)容,大部分游戲的操作全在這里操作并顯示;游戲控制面板,顯示勇者的屬性及攜帶的鑰匙數(shù)量,并提供了保存、讀取、退出、重玩游戲的按鈕;游戲菜單欄,集合了所有可進(jìn)行的操作,并添加了可玩性極高的金手指,給初玩者一個(gè)良好的體驗(yàn)。圖4示出了游戲界面。
游戲主頁(yè)面中地圖復(fù)雜多樣、各個(gè)樓層的地圖都不相同,怪物NPC也是層出不窮,因此本文將圖片以數(shù)字形式儲(chǔ)存在一個(gè)二維數(shù)組里,需要時(shí)讀取該數(shù)組并“畫(huà)”在界面上,不同數(shù)字代表不同圖片,即代表不同的角色、不同的游戲環(huán)境。同時(shí),也為勇士行動(dòng)操作提供更好的判斷。第0層地圖的儲(chǔ)存圖的代碼如下:
map1 = new int\[\]\[\]{
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{-1, 1, 3, 3, 3, 3, 1000, 3, 3, 3, 3, 1, -1},
.................. }
魔塔有很多層,每一層創(chuàng)建一個(gè)數(shù)組,同時(shí),創(chuàng)建了一個(gè)專門(mén)用來(lái)儲(chǔ)存初始地圖的類(lèi)MapPackage,以方便調(diào)用讀取顯示。以下是部分代碼(第0、1層):
public classSaveMap {
public Map map;
public int\[\]\[\] map1;
public SaveMap() {
map1 = new int\[\]\[\]{
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1},
{-1, 1, 3, 3, 3, 3, 1000, 3, 3, 3, 3, 1, -1},
....................... };
為了在界面上畫(huà)出地圖,本文首先把所有圖片和怪物分類(lèi)存入數(shù)組,然后根據(jù)所在樓層找到相應(yīng)的二維數(shù)組,使用2個(gè)嵌套的for循環(huán)對(duì)其進(jìn)行遍歷,將數(shù)字與實(shí)例化的圖片對(duì)象進(jìn)行匹配,一個(gè)一個(gè)“畫(huà)”在界面上。部分代碼如下:
importjavax.swing.*;
public class Map {
public MapPackge mapPackge;
public Map() {
mapPackge = new MapPackge();
map = new int\[13\]\[13\];
wall = Toolkit.getDefaultToolkit().getImage("image/05.jpg");
////NPC
NPC01a = Toolkit.getDefaultToolkit().getImage("image/NPC01a.png");
Princess = Toolkit.getDefaultToolkit().getImage("image/Princess.png");
////怪物ID、屬性、圖片
monsters\[0\] = new Monster(49, "黃金B(yǎng)OSS", 1000, 500, 250, 22, 19, Toolkit.getDefaultToolkit().getImage("image/Yellow.png"));
.............
for (int i = 0; i < 13; i++) {
for (int j = 0; j < 13; j++) {
map\[i\]\[j\] = mapPackge.map1\[i\]\[j\];
} }}
public void DrawMap(Graphics g, JPanel i) {
for (int z = 1; z < 12; z++) {
for (int j = 1; j < 12; j++) {
if (map\[z\]\[j\] == 1) {
g.drawImage(wall, (j - 1) * 32, (z - 1) * 32, 32, 32, (ImageObserver) i);
}
...........
if (map\[z\]\[j\] >= 1000 && map\[z\]\[j\] < 2000) {
g.drawImage(road, (j - 1) * 32, (z - 1) * 32, 32, 32, (ImageObserver) i);
g.drawImage(up, (j - 1) * 32, (z - 1) * 32, 32, 32, (ImageObserver) i);
}......}} }}
2.2地圖改變的實(shí)現(xiàn)
隨著游戲的進(jìn)行,地圖上的一些門(mén)被判定為已經(jīng)打開(kāi),一些怪物被判定為已經(jīng)被擊殺,因此,保存的原地圖包不能被讀取。為了原地圖進(jìn)行區(qū)分,需創(chuàng)建一個(gè)新的類(lèi)SaveMap用以儲(chǔ)存改變后的地圖包。由于SaveMap的結(jié)構(gòu)與MapPackage相同,這里就不對(duì)代碼進(jìn)行詳細(xì)描述。
2.3勇士移動(dòng)的實(shí)現(xiàn)
游戲的主要目的是讓勇士動(dòng)起來(lái)去突破艱難險(xiǎn)阻,擊殺怪物解救公主。由于勇士在行動(dòng)中會(huì)遇到不同的事物,從而使得勇士在行動(dòng)的每一步都要進(jìn)行判斷。本文運(yùn)用常見(jiàn)的動(dòng)作監(jiān)聽(tīng)事件,并將所有判斷的代碼打包在新的類(lèi)KeyPress里。部分代碼如下:
public classKeyPress {
public void Keypress(MSprite mSprite, Map map, Fight fight, FloorJump floorJump, SaveMap saveMap, int a, int b) {
if (mSprite.m_posY >= 0 && mSprite.m_posY <= 320 && mSprite.m_posX >= 0 && mSprite.m_posX <= 320) {
if (map.map\[mSprite.m_posY / 32 + 1 + a\]\[mSprite.m_posX / 32 + 1 + b\] == 100 && mSprite.YKey > 0) {
map.map\[mSprite.m_posY / 32 + 1 + a\]\[mSprite.m_posX / 32 + 1 + b\] = 0;
mSprite.YKey--;
} } } }
上述代碼在主菜單MCanvas的動(dòng)作監(jiān)聽(tīng)中被調(diào)用,在按下不同按鍵的同時(shí)進(jìn)行判斷操作。創(chuàng)建動(dòng)作監(jiān)聽(tīng)的部分代碼如下:
@Override
public void keyPressed(KeyEvent e) {
if (VK1 == "UP") {
VK1 = "";
shop.addHP.setVisible(false);
}
if (VK == "") {
if (e.getKeyCode() == KeyEvent.VK_DOWN) {
int a = 1;
int b = 0;
keyPress.Keypress(mSprite, map, fight, floorJump, saveMap, a, b);
} else if (e.getKeyCode() == KeyEvent.VK_UP) {
int a = -1;
int b = 0;
keyPress.Keypress(mSprite, map, fight, floorJump, saveMap, a, b);
if (map.map\[mSprite.m_posY / 32\]\[mSprite.m_posX / 32 + 1\] == 46) {
if (VK1 == "UP") {
VK1 = "";
shop.addHP.setVisible(false);
} else {
VK1 = "UP";
shop.addHP.setVisible(true);
}}
2.4戰(zhàn)斗過(guò)程的實(shí)現(xiàn)
勇士移動(dòng)的路上布滿了怪物的防線,同時(shí),前面提到的戰(zhàn)斗公式也需要用代碼實(shí)現(xiàn)。為此,創(chuàng)造一個(gè)動(dòng)作類(lèi)Fight用來(lái)進(jìn)行戰(zhàn)斗部分的操作。首先,遇到怪物時(shí)調(diào)用該類(lèi)并打包在KeyPress中。然后,從怪物類(lèi)Monster中提取所遭遇怪物的屬性與勇士類(lèi)MSprite的現(xiàn)有數(shù)據(jù)進(jìn)行計(jì)算判斷,如果符合公式則扣除血量,如果不符合則無(wú)法進(jìn)行操作,勇士無(wú)法繼續(xù)前進(jìn)。部分代碼如下:
public voidFighting(MSprite sprite, int monID, Map map, int a, int b) {
for (int i = 0; i < 1; i++) {
if (monID <= 70 && monID >= 49) {
xmonster = map.monsters\[monID - 49\];
monster = new Monster(monID, null, xmonster.HP, xmonster.DF, xmonster.ATK, xmonster.coin, xmonster.Exp, null);
HP = sprite.HP;
MHP = monster.HP;
if (sprite.ATK > monster.DF) {
while (MHP > 0) {
if (sprite.DF < monster.ATK && MHP > 0) {
MHP -= (sprite.ATK - monster.DF);
HP -= (monster.ATK - sprite.DF);
} else if (sprite.DF >= monster.ATK && MHP > 0) {
MHP = 0;
} }}
if (sprite.ATK > monster.DF && HP > 0) {
while (monster.HP > 0) {
if (sprite.DF >= monster.ATK && sprite.ATK > monster.DF) {
monster.HP = 0;
map.map\[sprite.m_posY / 32 + 1 + a\]\[sprite.m_posX / 32 + 1 + b\] = 0;
sprite.coin += monster.coin;
}
if (sprite.DF < monster.ATK && monster.HP > 0) {
monster.HP -= (sprite.ATK - monster.DF);
sprite.HP -= (monster.ATK - sprite.DF);
}
if (monster.HP <= 0) {
map.map\[sprite.m_posY / 32 + 1 + a\]\[sprite.m_posX / 32 + 1 + b\] = 0;
sprite.coin += monster.coin;
sprite.exp += monster.Exp;
} }} } }2.5樓層跳轉(zhuǎn)的實(shí)現(xiàn)
跳轉(zhuǎn)樓層后每個(gè)樓層的出發(fā)點(diǎn)都不同,并且要確保跳轉(zhuǎn)的樓層已經(jīng)保存在SaveMap類(lèi)中,否則會(huì)出現(xiàn)地圖重復(fù)或者不更新的問(wèn)題。本文采用如下方法解決以上問(wèn)題,將勇士放在要去的樓層的上一個(gè)樓層的樓梯上,根據(jù)KeyPress里打包的判斷,勇士會(huì)自動(dòng)到下一層的出發(fā)點(diǎn),這樣就能找到所要到達(dá)樓層的出發(fā)點(diǎn)。部分代碼如下:
public classFloorJump {
public void Jump(int i, Map map, SaveMap saveMap, MSprite mSprite) {
if (i >= 1000 && i < 2000) {
int z = i - 1000;
mSprite.floor = z + 1;
for (int j = 0; j < 13; j++) {
for (int h = 0; h < 13; h++) {
Group\[z\]\[j\]\[h\] = map.map\[j\]\[h\];
map.map\[j\]\[h\] = Group\[z + 1\]\[j\]\[h\];
}}
int x = 0, y = 0;
for (int j = 1; j < 13; j++) {
for (int h = 1; h < 13; h++) {
if (Group\[z + 1\]\[j\]\[h\] >= 2000 && Group\[z + 1\]\[j\]\[h\] < 3000) {
x = h;
y = j;
}} }
2.6游戲存取的實(shí)現(xiàn)
魔塔雖然是一個(gè)小游戲,但作為經(jīng)典的地牢闖關(guān)類(lèi)游戲,想要闖過(guò)層層關(guān)卡,取得游戲成功也并非容易實(shí)現(xiàn),想要闖關(guān)成功需要花費(fèi)大量時(shí)間,因此,游戲的存檔與讀取就顯得尤為重要。本文使用的存檔技術(shù)是JAVA比較常見(jiàn)的IO流技術(shù)。將游戲所有能讀取到且需要改變的屬性數(shù)值,通過(guò)IO流保存在一個(gè)外部文件中,并在需要時(shí)判斷是否存在,如果存在則讀取這些屬性,從而達(dá)到地圖和數(shù)據(jù)的保存功能。保存代碼和讀取代碼如下:
importjava.io.DataOutputStream;
import java.io.FileOutputStream;
public class Save {
private SaveMap saveMap = new SaveMap();
try {
DataOutputStream localDataOutPutStream = new DataOutputStream(new FileOutputStream("save" + a + ".dat"));
for (int i = 0; i < Group.length; i++) {
for (int j = 0; j < 13; j++) {
for (int k = 0; k < 13; k++) {
Group\[i\]\[j\]\[k\] = mCanvas.map.map\[j\]\[k\];
localDataOutPutStream.writeInt(Group\[i\]\[j\]\[k\]);
} } }
} catch (Exception e) {
e.printStackTrace();
} } }
importjavax.swing.*;
public class Read {
try {
DataInputStream localDataInputStream = new DataInputStream(new FileInputStream("save" + a + ".dat"));
for (int i = 0; i < Group.length; i++) {
for (int j = 0; j < 13; j++) {
for (int k = 0; k < 13; k++) {
Group\[i\]\[j\]\[k\] = localDataInputStream.readInt();
mCanvas.map.map\[j\]\[k\] = Group\[i\]\[j\]\[k\];
} } }
mSprite.m_posX = localDataInputStream.readInt();
localDataInputStream.close();
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "讀取失敗!此存檔為空或損壞!", "讀取失敗", JOptionPane.WARNING_MESSAGE);
} } }
3結(jié)束語(yǔ)
魔塔是一個(gè)十分經(jīng)典的RPG(角色扮演)游戲,(下轉(zhuǎn)封三)本文在實(shí)現(xiàn)原魔塔游戲功能的基礎(chǔ)上創(chuàng)建了新的功能。經(jīng)過(guò)測(cè)試,該游戲能夠運(yùn)行流暢,體驗(yàn)良好。通過(guò)設(shè)計(jì)該款游戲,體驗(yàn)到了JAVA在制作游戲方面的便捷性。
參考文獻(xiàn)
[1] 孫衛(wèi)琴. Java面向?qū)ο缶幊蘙M]. 電子工業(yè)出版社, 2006.
[2] 牛芳. 異構(gòu)存儲(chǔ)系統(tǒng)中的節(jié)點(diǎn)失效并行化修復(fù)研究[D]. 中國(guó)科學(xué)技術(shù)大學(xué), 2014.
[3] 楊豐盛. Android應(yīng)用開(kāi)發(fā)揭秘[M]. 機(jī)械工業(yè)出版社, 2010.
[4] 龐萍.基于java的魔塔游戲的設(shè)計(jì)與實(shí)現(xiàn)[J]. 電腦知識(shí)與技術(shù),2016,12(34):246-247.
[5] Julian Gold. 面向?qū)ο蟮挠螒蜷_(kāi)發(fā)[M]. 電子工業(yè)出版社, 2005.