JAVA開發相似冒險島的遊戲Part1

JAVA開發相似冒險島的遊戲Part1

1、總結

 

 

2、JAVA開發相似冒險島的遊戲Part1

初學嘛) ,不過總的來講這個程序仍是頗有意思的。這裏我從新再整理了一下,但願能幫助到其餘想要開發相似程序的朋友,共同進步!

曬一下效果圖:java

效果圖

哈,仍是有模有樣的。左邊是本身寫的冒險島,右邊是真的冒險島。 
畢竟也是個遊戲,方向鍵能夠控制人物移動,而後能夠攻擊,能夠打怪,升級,作任務。網絡

先說一下素材,有一個專門提供冒險島素材的紙娃娃系統,冒險島中各類素材均可以從這個網站中獲取: 
http://www.maplesimulator.com/programs/bannedstory多線程

遊戲中顯示出這樣的界面效果,能夠用PS中的圖層來理解,所謂圖層就是含有文字或圖形等元素的膠片,一張張按順序疊放在一塊兒,組合起來造成頁面的最終效果。函數

而這個膠片,就是JAVA中的容器JPanel。JPanel能夠設置背景圖片,也能夠多個JPanel放置於一個JPanel中,就拿遊戲下方的狀態條來講: 
整個狀態條就是一個JPanel,咱們就新建一個狀態條類測試

public class StatusBar extends JPanel

而後讓他繼承JPanel。 
咱們給這個JPanel設置這樣的一個背景: 
這裏寫圖片描述 
給JPanel設置背景的方法是重寫JPanel的paint函數,看下面代碼應該很清楚。字體

public void paint(Graphics g){ g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\狀態條\\狀態條.png"),0,0,this); }

接着,在這個JPanel上還要顯示等級的數字、血條、藍條、經驗條,以及一個系統按鈕(不過其實這個按鈕並不屬於StatusBar類,咱們只是讓他顯示在這個位置)。 
這些都是StatusBar類的成員。咱們只須要在固定的位置將相應的成員顯示就能夠了。 
先說這個顯示等級的數字,很顯然就是一個JLabel嘛,不過我用的不是JLabel。 
依舊是在paint函數中,加入這樣一段:動畫

g.setColor(Color.WHITE); g.setFont(new Font(Font.DIALOG_INPUT, Font.BOLD, 25));//設置字體 g.drawString(Integer.toString(lv), 50,38);//等級

直接把字畫在JPanel上的相應位置就能夠了,顏色、字體、大小、字的內容、橫縱座標,就這些參數。能夠查看API文檔,這裏就不細說。網站

血條藍條經驗條,這三個條條是大部分網絡遊戲都有的東西,主要用來直觀的顯示玩家狀態,拿血條舉例子,看最前面的效果圖,當前血量800,總血量1000,因此血條的長度就是整個血槽的4/5,這樣一想,用進度條這種組件去實現血條藍條是很可行的,然而我查了才發現,JAVA中(至少是標準庫吧。。)並無進度條這個組件,因此仍是要本身去實現它。 
仍是在paint函數中:this

g.setColor(Color.RED); //血條 g.fillRect(141, 23, (int)((double)hp/allhp*length), 15); g.setColor(Color.BLUE); //藍條 g.fillRect(340, 23, (int)((double)mp/allmp*length), 15); g.setColor(Color.YELLOW); //經驗條 g.fillRect(539, 23, (int)((double)exp/allexp*length), 15);

根據當前血量和總血量的比例去繪製相應長度相應顏色的矩形,這樣一來動態的血條藍條經驗條的主體部分就完成了,咱們還須要在上面顯示具體的血量和血總量: 
這裏寫圖片描述 
有了前面繪製等級的經驗,咱們使用g.drawString函數就能夠了,不過爲了顯示的美觀嗎,咱們還須要考慮一些問題,文字的顯示是與血條右邊對其的,而繪製文字的參數中提供的參數是這樣的一組x,y並不能符合咱們右對其的要求。 
這裏寫圖片描述 
因此我用了另一種很機智(shabi)的方法:spa

//數字比例6.7619 /[比例3 String hptxt="["+Integer.toString(hp)+"/"+Integer.toString(allhp)+"]"; int hptxtnuml=Integer.toString(hp).length()+Integer.toString(allhp).length();//血量數字顯示的數字長度 int hptxtx=272-(int)(hptxtnuml*6.7619);//相應橫座標 String mptxt="["+Integer.toString(mp)+"/"+Integer.toString(allmp)+"]"; int mptxtnuml=Integer.toString(mp).length()+Integer.toString(allmp).length();//藍量數字顯示的數字長度 int mptxtx=471-(int)(mptxtnuml*6.7619);//相應橫座標 String exptxt="["+Integer.toString(exp)+"/"+Integer.toString(allexp)+"]"; int exptxtnuml=Integer.toString(exp).length()+Integer.toString(allexp).length();//藍量數字顯示的數字長度 int exptxtx=670-(int)(exptxtnuml*6.7619);//相應橫座標

先把要繪製的字符串準備好,而後計算一下文本長度(這個長度是指真的長度。。顯示出來要幾個座標。。別問我怎麼測得,我用尺子量的。。),最後換算出相應的座標繪製。

至此,狀態條的顯示就完成了。在個人設計中,人物的屬性(血,藍,經驗,等級,攻擊力等)是直接放在狀態條類裏做爲成員變量的,換句話說,你玩遊戲玩的不是那我的物,你的全部數據都不存在人物的類裏,而是存在狀態條類裏,人物攻擊力高,一下秒掉怪物,那只是配合着狀態條裏的屬性顯示給玩家看而已~

咱們先把狀態條放到一邊,待會再來用它。

如今咱們要實現方向鍵控制人物移動。 
依舊是前面圖層的思想,人物也是一個圖層。咱們建立一我的物類:

public class Obj extends JPanel

先來分析一下人物的動做,人物有個朝向,臉朝左,臉朝右,站着不動的時候有站着不動的姿式,走路的時候有走路的姿式,按上鍵若是能抓到繩子,有爬繩子的姿式,按下有趴着的姿式。因此Obj類中須要有一個變量來控制人物的狀態,(向左走、向右走、向左趴下等):

private int zhuangtai=2;//人物狀態

而後在繪製背景圖片的時候,switch(zhuangtai)來決定該繪製哪張圖片就能夠了。 
咱們能夠寫一個設置狀態的函數,以便控制人物的時候使用:

public void setzhuangtai(int a)//設置狀態 { zhuangtai=a; }

這裏說明一下,人物站立的時候,並非一張靜止的圖片,而是提早準備好的GIF圖像,站立的時候顯示的就是天然擺手的動畫分鏡頭以下: 
這裏寫圖片描述 
固然若是不想使用GIF圖像的話,能夠新建一個線程,專門在站立狀態控制人物擺手動畫的切換。 
相似攻擊的動畫顯示也是如此。 
這裏寫圖片描述 
咱們如今先來設想一下控制人物移動的思路: 
首先人物這個圖層(JPanel)是顯示在窗口上的,主類繼承JFream 
而後人物做爲主類的成員,就和建立一個JButton同樣的寫法

private Obj ren=new Obj(0,0);//建立角色

用兩個整形變量x,y來表明人物的座標 
接下來就是將,xy做爲參數使用setBounds函數設置人物的顯示座標以及顯示大小。 
對主窗口註冊鍵盤監聽和觸發事件: 
咱們按下方向鍵後,好比說方向右鍵,調用Obj類中的setzhuangtai函數,修改人物狀態,再將x+5 (這個值本身定,值越大移動的越快)接着再次使用setBounds從新設置人物的顯示座標。而後調用repaint進行重繪。 
這樣就能夠實現人物的移動了。我這裏沒有附具體代碼,由於我不是這樣寫的。。(噗!!)

在實際測試中,這樣去實現人物移動會引入一個很麻煩的問題——當你設置了遊戲地圖以後,控制人物移動會出現背景跟不上人物,有殘影的情形,具體的解決方案是使用雙緩衝技術,不過我有另外的思路解決了這個問題,何樂而不爲呢:

人物移動的時候,人物這個圖層後面的背景老是拖着以前的背景,使得顯示着很不協調,個人解決方案是設置人物這個圖層的大小的時候,讓他充滿整個屏幕,這樣的話,人物移動時,背景就是整個屏幕,而不用考慮舊背景的殘影問題了。不過這樣設計的話,人物移動的方式就要改變了,不能再是移動人物類的對象在主窗口中顯示的位置,由於人物類的對象在主窗口中已是全屏顯示了,不能再改變,改變的只能是人物的圖片,在人物類的這個JPanel中內部的位置。因此x,y這個座標也是存於人物類中。

private int zhuangtai=2;//人物狀態 //屏幕座標 private int x; private int y; public void setzhuangtai(int a)//設置狀態 { zhuangtai=a; } public void setxy(int m_x,int m_y) { x=m_x; y=m_y; } //人物在屏幕中移動 public void movex(int m_x) { x+=m_x; } public void movey(int m_y) { y+=m_y; }

顯示的地方就經過x和y的值來調整位置,不過仍是有一個地方要注意,顯示圖片時的座標參數是指以左上角爲起點的,可是每一張圖片的大小都不同,因此若是不加換算的話就會出現這樣的問題: 
這裏寫圖片描述這裏寫圖片描述 
這裏寫圖片描述這裏寫圖片描述 
人物的顯示座標天然是要以腳爲座標,因此咱們對於每一張圖都要測定它的偏移(很幸運的是紙娃娃系統能夠直接導出偏移的值),而後顯示的時候根據偏移去計算換算後的座標。

paint函數以下:

public void paint(Graphics g)
    { switch(zhuangtai) { case 0:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\走路\\向右走0.png"),x-15,y-68,this);break; case 1:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\走路\\向右走1.png"),x-15,y-68,this);break; case 2:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\走路\\向右走2.png"),x-15,y-68,this);break; case 3:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\走路\\向右走3.png"),x-15,y-68,this);break; case 4:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\走路\\向左走0.png"),x-76,y-68,this);break; case 5:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\走路\\向左走1.png"),x-76,y-68,this);break; case 6:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\走路\\向左走2.png"),x-76,y-68,this);break; case 7:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\走路\\向左走3.png"),x-76,y-68,this);break; case 8:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\站立\\面朝右站.gif"),x-40,y-86,this);break; case 9:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\站立\\面朝左站.gif"),x-24,y-86,this);break; case 10:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\跳躍\\向右跳.png"),x-40,y-90,this);break; case 11:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\跳躍\\向左跳.png"),x-25,y-90,this);break; case 12:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\趴下\\面朝左趴下.png"),x-106,y-40,this);break; case 13:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\趴下\\面朝右趴下.png"),x,y-40,this);break; case 14:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\左\\0.png"),x-165,y-110,this);break; case 15:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\左\\1.png"),x-165,y-110,this);break; case 16:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\左\\2.png"),x-165,y-110,this);break; case 17:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\左\\3.png"),x-165,y-110,this);break; case 18:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\左\\4.png"),x-165,y-110,this);break; case 19:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\左\\5.png"),x-165,y-110,this);break; case 20:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\左\\6.png"),x-165,y-110,this);break; case 21:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\左\\7.png"),x-165,y-110,this);break; case 22:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\左\\8.png"),x-165,y-110,this);break; case 23:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\右\\0.png"),x-115,y-110,this);break; case 24:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\右\\1.png"),x-115,y-110,this);break; case 25:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\右\\2.png"),x-115,y-110,this);break; case 26:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\右\\3.png"),x-115,y-110,this);break; case 27:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\右\\4.png"),x-115,y-110,this);break; case 28:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\右\\5.png"),x-115,y-110,this);break; case 29:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\右\\6.png"),x-115,y-110,this);break; case 30:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\右\\7.png"),x-115,y-110,this);break; case 31:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\技能\\輕舞飛揚\\右\\8.png"),x-115,y-110,this);break; case 32:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\爬繩子\\0.png"),x-27,y-70,this);break; case 33:g.drawImage(Toolkit.getDefaultToolkit().getImage("F:\\Code\\JAVA\\MapleStory\\圖片素材\\主角\\爬繩子\\1.png"),x-27,y-70,this);break; } g.drawString("屏幕位置:"+"x="+Integer.toString(x)+"y="+Integer.toString(y),645,525); }

控制移動的代碼示例:

if(e.getKeyCode()==KeyEvent.VK_LEFT)//左 { if(Drop!=2) { Direction=0; if(Drop==0) ren.setzhuangtai(movetime++%4+4); if(map.AllowMoveL()==1) { //if(ren.getx()<=600||map.getrenx()>mapxmax-200) if(ren.getx()>=200||map.getrenx()<210) { ren.movex(-5); map.moverenx(-5); } else { map.movex(5); map.moverenx(-5); NpcOffsetx-=5; NPCshow(); } } MainWindow.this.repaint(); new Thread(DropTest).start(); } }

畢竟移動的時候不能只考慮人物的移動,玩遊戲的時候,人物始終會顯示在屏幕上,而不會走出窗口,當人物座標臨近窗口時,移動的就不是人物,而是地圖的背景了(地圖的圖片是比窗口大的多的,窗口只有800*600,只顯示地圖的一部分,當玩家向右走,走到接近窗口邊緣時,地圖的圖片向左移動,玩家座標不變,動做保持向右走的姿式)。這樣能夠保證人物始終在窗口內移動。 
這裏寫圖片描述 
這裏寫圖片描述 
一樣的NPC的位置也要改變,NPC是對於地圖的獨立存在,可是相對於地圖,NPC是靜止的。 
這裏寫圖片描述 
NPC的顯示,咱們須要明確一下兩點,第一,NPC顯示在哪張地圖上(或是說NPC顯示在哪一個場景中),第二,該NPC在該地圖上的座標是多少。 
這裏所需的NPC在地圖上的座標而不是NPC在窗口上的座標,並非說要將NPC做爲地圖類的成員,在地圖這個JPaenl中顯示(這樣作不可取,緣由後面說),而是由於NPC與地圖是相對靜止的,咱們有了NPC在地圖上的座標,也有了地圖在窗口的顯示座標,就能夠將NPC在地圖上的顯示座標換算出來,咱們還能夠設置一個NPC座標的偏移參數,地圖移動時,就改變這個偏移值,而後NPC顯示的時候就將偏移值包含進去。這樣一來會更加直觀,後期若先要添加新的NPC也十分方便。 
那麼咱們把全部的NPC顯示專門放到一個函數裏:

public void NPCshow()//顯示NPC { switch(mapNo) { case 0: { for(int i=0;i<7;i++) npc[i].setVisible(true); npc[0].setBounds(2350-NpcOffsetx,720+NpcOffsety,123,101);//希納斯 npc[1].setBounds(1800-NpcOffsetx,707+NpcOffsety,48,109);//南哈特 npc[2].setBounds(732-NpcOffsetx,515+NpcOffsety,97,103);//米哈爾 npc[3].setBounds(465-NpcOffsetx,705+NpcOffsety,97,109);//伊莉娜 npc[4].setBounds(788-NpcOffsetx,695+NpcOffsety,78,122);//奧茲 npc[5].setBounds(302-NpcOffsetx,420+NpcOffsety,77,116);//伊卡爾特 npc[6].setBounds(663-NpcOffsetx,635+NpcOffsety,77,119);//胡克 break; } case 1: { for(int i=0;i<7;i++) npc[i].setVisible(false); for(Monster tempgw:gw[0]) tempgw.setBounds(map.getx(), map.gety(), mapxmax, mapymax); //gw[0][0].setBounds(map.getx(), map.gety(), mapxmax, mapymax); //gw[0][1].setBounds(map.getx(), map.gety(), mapxmax, mapymax); break; } case 2: { for(int i=0;i<7;i++) npc[i].setVisible(false); for(Monster tempgw:gw[1]) tempgw.setBounds(map.getx(), map.gety(), mapxmax, mapymax); break; } case 3: { for(int i=0;i<7;i++) npc[i].setVisible(false); for(Monster tempgw:gw[2]) tempgw.setBounds(map.getx(), map.gety(), mapxmax, mapymax); break; } } }

整個函數的原理很簡單,先switch地圖編號,讓其餘地圖的NPC不顯示,而後再對當前地圖的NPC一個一個的設置位置。

咱們把遊戲理解成一個一個的圖層組成的場景,那麼圖層與圖層之間是有疊放次序的,處於最頂端的圖層會將後面的圖層蓋住。 
最頂端的圖層是系統菜單(以下圖),而後是狀態條上面的系統按鈕,再而後是狀態條,再再而後是NPC的圖層,再再再而後是玩家控制的人物圖層,最後是地圖。 
咱們看這張圖,很清楚的反應了他們的關係: 
這裏寫圖片描述 
前面說到NPC不能是做爲地圖類中的成員,雖說把NPC放到地圖類中去顯示會很方便,可是這樣一來NPC就沒有交互性了,遊戲中如何與NPC交互?固然是要點她。。若是把NPC做爲地圖類的成員,那麼就永遠也點不到NPC了,由於人物的圖層(JPanel)大小是佔滿整個屏幕(這在前面已經說到),這樣就像是一個JButton處在一個透明的JPaenl後面,看獲得點不到。

如今來作系統按鈕,遊戲中的按鈕天然不能用JButton,由於太醜了嘛,咱們仍是用JPanel來作,就和人物的顯示同樣,按鈕也分三個狀態,一個是未激活,再是激活,再是按下,激活就是鼠標放到按鈕上的時候按鈕邊緣發光。 
這裏寫圖片描述這裏寫圖片描述這裏寫圖片描述 
從左到右一目瞭然,要作到這些,只須要給它註冊和設置鼠標監聽事件:

public void mouseEntered(MouseEvent e)//鼠標進入 public void mousePressed(MouseEvent e)//鼠標按下 public void mouseReleased(MouseEvent e)//鼠標釋放 public void mouseExited(MouseEvent e)//鼠標離開

將系統菜單的是否顯示,以及系統按鈕的狀態切換分別寫到這些事件之中。同理,系統菜單上的按鈕也是如此。

至此,咱們的遊戲已是初見雛形,能夠控制人物作各類動做,也有狀態欄去顯示人物的狀態,還有一個菜單提供各項操做選擇。

該對遊戲的完整性更進一步了,如今的地圖,說到底也只是一張圖片,設置成地面的圖片,人物就在地面上走,設置整天空的圖片,人物就在天空上走,咱們要對地圖和人物進一步編寫,引入「重力下墜」和「地面」。

先說下墜,這個動做執行起來人物是什麼樣的呢?如圖: 
這裏寫圖片描述 
人物保持跳躍的動做,從高空落下,座標值y值不斷增長,期間咱們按左鍵 ,右鍵,可使得人物臉的朝向改變,而且能夠往左或者右飄動,也能夠在空中的時候發動攻擊(我嘗試了N遍才截到這個圖!!)。 
這裏寫圖片描述 
因此很顯然啊,這個墜落,是一個多線程的函數,始玩家y座標持續增大,而且設置玩家的狀態動畫爲「跳躍」。

if(droping==0)//確保同一時間只執行一個下墜線程 { droping=1;//表明已經在下墜 jumping=1;//下墜中禁止跳躍 if(Drop!=2) Drop=map.ToGround(); while(Drop==1) { if(attacking==0) ren.setzhuangtai(Direction==0?11:10); try { Thread.sleep(3); } catch(Exception e1) { e1.printStackTrace(); } if(ren.gety()<=430/*||map.getreny()>=mapymax-200*/) ren.movey(1); else { map.movey(-1); NpcOffsety-=1; } map.movereny(1); Drop=map.ToGround(); NPCshow(); MainWindow.this.repaint(); if(attacking==0) ren.setzhuangtai(Direction==0?9:8); } droping=0;//下墜結束 jumping=0;//跳躍結束 //MainWindow.this.repaint(); }

代碼中很容易看出來的,墜落的條件是Drop的值,每下落一點,就調用地圖的ToGround函數,檢測是否到達地面,而後給Drop從新賦值。 
來看一下ToGround函數,比較長因此只貼一部分:

public int ToGround()//到地面 { switch(mapNo) { case 0: { if(reny==790 ||(renx>=597&&renx<=736&&reny==757) ||(renx>=600&&renx<=710&&reny==727) ||(renx>=589&&renx<=694&&reny==695) ||(renx>=586&&renx<=686&&reny==668) ||(renx>=589&&renx<=677&&reny==643) ||(renx>=594&&renx<=674&&reny==619) ||(renx>=600&&renx<=682&&reny==595)) return 0; break; }…

很顯然,人物座標處於這些範圍,就不用繼續掉了,這裏的座標說的是人物的腳的座標。 
而這些範圍,就是一條條的線,表明地面,或者臺階,或者別的能站的地方如圖(這張地圖費了我3個小時啊!!): 
這裏寫圖片描述
在全部可能發生墜落的操做後面都啓動一個墜落線程。相似的地圖元素還有傳送門,繩子,爬梯,牆壁,均可以在地圖類中用相似的函數來寫,以便於檢測。 
一樣的,跳躍也是一個線程,先是將y值減少,到了最高點再執行墜落線程:

if(jumping==0)//確保同一時間只能執行一個跳躍線程 { jumping=1;//表明正在跳躍 Drop=1;//表明人物以離開地面 droping=1;//跳躍中途禁止啓動下墜線程 for(int i=0;i<100;i++) { try { Thread.sleep(3); } catch(Exception e1) { e1.printStackTrace(); } if(ren.gety()>=100||map.getreny()<=100) ren.movey(-1); else { map.movey(1); NpcOffsety+=1; } map.movereny(-1); NPCshow(); MainWindow.this.repaint(); } try { Thread.sleep(5); } catch(Exception e1) { e1.printStackTrace(); } droping=0;//容許下墜 new Thread(DropTest).start();//啓動下墜 }

至此,人物終於能夠在地圖上自由走動了。 
玩過冒險島的人都知道,換地圖的動做就是在光門的位置按上鍵,因此對上鍵註冊鍵盤監聽,而後檢測是否在光門上,在哪一個光門上,以及切換到哪一個地圖。 
如下是部分代碼:

public void gomap(int x,int y)//進入地圖 切換地圖時,要給地圖一個初始位置 { map.setmapNo(mapNo);//切換地圖 switch(mapNo) { case 0: { mapymax=941; mapxmax=3095; //new Thread(newMonster).start(); break; } case 1: { mapymax=535; mapxmax=2170; new Thread(newMonster).start(); break; } … ren.setxy((x>mapxmax-403?x-mapxmax+806:(x<806?x:403)),y>mapymax-315?y-mapymax+629:(y<629?y:315)); //map.moverenx(x>mapxmax-403?mapxmax-806:(x<806?0:x-403)); //map.moverenx(x); // map.movereny(y); map.setrenxy(x, y); //map.movex(-x); map.setxy(-(x>mapxmax-403?mapxmax-806:(x<806?0:x-403)),-(y>mapymax-315?mapymax-629:(y<629?0:y-315))); //map.movex(x>mapxmax-403?mapxmax-806:(x<806?0:x-403)); //map.movey(-y); NpcOffsetx=x>mapxmax-403?mapxmax-806:(x<806?0:x-403); NpcOffsety=-(y>mapymax-315?mapymax-629:(y<629?0:y-315)); menu.setVisible(false); for(ButtonBase temp:menubutton)//菜單按鈕 temp.setVisible(false);; MainWindow.this.repaint(); new Thread(DropTest).start(); }

切換地圖以後讓人物從空中掉下了就OK了。

接下來就是怪物系統: 
這裏寫圖片描述 
怪物的主體和人物基本一致,不一樣的是,怪物的屬性,好比hp,攻擊力等都是存在於怪物本身的類中。全部的怪物由一個怪物線程進行控制。怪物能作什麼呢?能夠左右移動,能夠攻擊,能夠被玩家打,被打死了玩家能得到經驗。

private int MonsterID; private int zhuangtai=0; private int Target;//表明是否有攻擊目標 private int HP;//怪物血量 private int Defense;//防護力 private int DHP=0;//掉血量 private int x=1370; private int y=400; private int bx; private int by; private int xmax; private int xmin; private int Offsetx; private int Offsety;

若是怪物沒有被攻擊過,那麼它會漫無目的的隨機在本身的移動範圍內移動,若是玩家攻擊了怪物,那麼怪物就會朝着玩家移動。遊戲中對於屬性,有不少能夠設置的地方,最簡單的就是一個攻擊力一個血量,不過爲了加強遊戲的多樣性(keng qian)吧,陸續出來了防護力,迴避率,恢復速度,XX屬性抗性等等等。。我這裏也就是隨便設定了一下攻防計算規則。 
對於怪物的顯示位置,咱們能夠將其與NPC的顯示位置類比,怪物靜止的時候,就和NPC同樣,與地圖是相對靜止的,怪物還能夠在本身的活動範圍本身移動,因此在NPC顯示的基礎上,再去進一步的增長一個移動函數就能夠了。 
爲了節約資源,我將一張地圖上的全部怪物共用的同一個線程,首先獲取地圖號,而後用一個循環來遍歷該地圖內全部的怪物,切換地圖以後,就開始執行這張地圖上的怪物的線程,線程根據怪物的狀態,有沒有被攻擊,有沒有目標來控制怪物作出各類動做。

任務系統 
這裏寫圖片描述 
任務是遊戲的精髓部分,它拉着玩家去展開遊戲劇情,這裏說一下個人單線任務設計的思路。一個整型變量來表明任務的進度,給NPC註冊鼠標監聽,點擊NPC後,根據當前的進度,以及NPC的編號,來肯定NPC對話框中的顯示內容。 
示例以下面的代碼,先switch NPC編號,而後再switch 任務進度,最後就能夠獲得具體的內容了:

case 8: { g.drawString("聖地中處處都是黑魔法師的傀儡", 165, 30); g.drawString("這對女皇來講是很大的威脅", 165, 45); g.drawString("不能殺掉太多,否則會引發黑魔法師注意的", 165, 60); g.drawString("(請再和我對話)", 165, 75); break; } case 9: { g.drawString("往左一直走,前往 [前線1]、[前線2]", 165, 30); g.drawString("消滅痩刺客、胖刺客各 30 只吧!", 165, 45); break; } case 10: { g.drawString("你確認已經消滅了 痩刺客、胖刺客各 30 只嗎?", 165, 30); g.drawString("(打開任務窗口能夠查看任務完成狀況)", 165, 45); break; } case 11: { g.drawString("你完成的很好!", 165, 30); g.drawString("(請再和我對話)", 165, 45); break; }

這裏寫圖片描述 
而後顯示對話框的時候就能夠判斷,當前進度,這個NPC是否有內容,若是有的話,就顯示提早設置好的,沒有的話就顯示默認值。 
這裏寫圖片描述 
至此,一個單機的冒險島的主要功能就基本實現了,剩下的就是多添幾張地圖,多加幾任務之類的了。

注:第一次寫博客。。(其實由於以前發了一次這個,不過寫的是亂七八糟,因此刪了重寫,此次算是正式的第一次!)前面寫的很詳細,後面比較簡略。。這篇博客我還會慢慢修改(排版什麼的還不熟嘛)。 
而後是,大二結束的時候JAVA課程設計,我最後把它改爲了聯網版,能夠局域網一塊兒玩。。空了我再整理一下也發出來(因此這個是Part1嘛)。

項目文件下載地址: 
http://download.csdn.net/detail/jdk999/9042405

 

參考:

相關文章
相關標籤/搜索