Java實現(xiàn)泡泡堂對戰(zhàn)版游戲的示例代碼
前言
《泡泡堂II》是一個基于java的自制游戲,使用了MVC模式
,分離了模型、視圖和控制器,使得項目結(jié)構(gòu)清晰易于擴展,使用配置文件來設(shè)置游戲基本配置,擴展地圖人物道具等。同時,該程序編寫期間用了單例模式、工廠模式、模板模式等設(shè)計模式。為了游戲的可玩性,特意設(shè)計了平滑碰撞以及機器人。
主要設(shè)計
- 設(shè)計游戲界面,用swing實現(xiàn)
- 繪制游戲啟動界面、結(jié)束界面、地圖、主角、道具
- 實現(xiàn)泡泡爆炸
- 為了盡量復(fù)原泡泡堂游戲,初步實現(xiàn)了機器人功能。該機器人可以判斷障礙物釋放炸彈、規(guī)避炸彈、攻擊玩家。
- 實現(xiàn)道具掉落和相應(yīng)屬性加成
- 實現(xiàn)游戲音效和背景音樂
- 平滑碰撞:人物在拐角處移動的時候經(jīng)常不是剛好對齊的狀態(tài),程序會判定玩家碰撞了障礙物所以導(dǎo)致玩家無法拐彎。所以我們在處理這種情況的時候,會讓玩家進行平滑的移動使得玩家看上去是滑進去的,增強玩家游戲體驗
- 設(shè)計單/雙人模式
功能截圖
游戲啟動界面:
道具說明:
游戲開始:
釋放炸彈·:
炸彈爆炸效果:
代碼實現(xiàn)
游戲啟動類
public class GameStart { private static GameFrame gameFrame; //游戲啟動入口 public static void main(String[] args) { // 資源加載 try { ElementLoader.getElementLoader().readGamePro(); ElementLoader.getElementLoader().readImagePro(); ElementLoader.getElementLoader().readCharactorsPro(); ElementLoader.getElementLoader().readBubblePro(); ElementLoader.getElementLoader().readSquarePro(); } catch (IOException e) { System.out.println("資源加載失敗"); e.printStackTrace(); } //初始化 gameFrame = new GameFrame(); //界面顯示 gameFrame.setVisible(true); //音樂播放 GameMusicPlayer musicPlayer = new GameMusicPlayer(); musicPlayer.start(); } /** * 界面切換 * @param panelName 界面名稱 */ public static void changeJPanel(String panelName){ if(panelName == "game") { GameController.setGameRunning(true); gameFrame.addListener(); } else { GameController.setGameRunning(false); gameFrame.removeListener(); } gameFrame.changePanel(panelName); //強制刷新,否則監(jiān)聽無效 gameFrame.setVisible(false); gameFrame.setVisible(true); } public static void startNewGame() { GameController.setGameRunning(true); gameFrame.startGame(); changeJPanel("game"); } }
核心監(jiān)聽類
public class GameThread extends Thread{ private boolean running; //表示當前關(guān)卡是否在進行 private boolean over = false; //表示游戲是否結(jié)束,結(jié)束返回開始菜單 private static int sleepTime = 20; //runGame刷新時間 //倒計時變量 private static int allTime = 600*1000; //10分鐘 @Override public void run() { while(!over) { running = true;//當前關(guān)卡正在進行 //加載元素 loadElement(); //顯示人物,流程,自動化 runGame(); //結(jié)束當前關(guān) overGame(over); } GameStart.changeJPanel("over"); } //加載元素 private void loadElement() { ElementManager.getManager().loadMap();//加載地圖及其元素 } /** * 關(guān)卡結(jié)束 * 如果over為真則游戲失敗返回界面,否則進入下一關(guān) * @param over */ private void overGame(Boolean over) { ElementManager.getManager().overGame(over); } //顯示人物,游戲流程,自動化 private void runGame() { allTime = 600*1000; while(running) { Map<String, List<SuperElement>> map = ElementManager.getManager().getMap(); Set<String> set = map.keySet(); for(String key:set) { List<SuperElement> list = map.get(key); for(int i=list.size()-1; i>=0; i--) { list.get(i).update(); if(!list.get(i).isAlive()) list.remove(i); } } //添加游戲的流程控制linkGame()? //玩家與炸彈碰撞死亡 playerBoom(); //可破壞物與炸彈碰撞 fragilityBoom(); //電腦與炸彈碰撞死亡 npcBoom(); //電腦與道具碰撞效果,暫時不開啟 //npcMagicBox(); //玩家與道具碰撞效果 playerMagicBox(); //檢測是否玩家全部死亡 defeat(); //控制runGame進程 allTime = allTime - sleepTime; try { sleep(20); } catch (InterruptedException e) { // TODO: handle exception e.printStackTrace(); } } } private void defeat() { boolean allDead = true; int surviveP = 0; int winner = 2;//0為玩家1,1為玩家2,2為電腦獲勝 List<SuperElement> playerList = ElementManager.getManager().getElementList("player"); List<SuperElement> npcList = ElementManager.getManager().getElementList("npc"); for(SuperElement se:playerList) { if(!((Player)se).isDead()) { surviveP++; } } for(SuperElement npc:npcList) { if(!((Npc)npc).isDead()) { allDead = false; } } //玩家失敗 if(surviveP==0||(allTime<=0 && !allDead)) { running = false; over = true; OverJPanel.getResult().setText("defeated"); } //玩家勝利 if(allDead&&surviveP==1) { running = false; over = true; for(SuperElement se:playerList) { if(!((Player)se).isDead()) { surviveP++; winner = ((Player)se).getPlayerNum(); } } OverJPanel.getResult().setText("player "+(winner+1)+" win"); } //時間到,兩個玩家都活著 if(allTime<=0&&surviveP==2&&allDead) { running = false; over = true; int score1 = ((Player)playerList.get(0)).score; int score2 = ((Player)playerList.get(0)).score; if(score1==score2) { OverJPanel.getResult().setText("defeated"); } else if(score1>score2) { OverJPanel.getResult().setText("player 1 win"); } else { OverJPanel.getResult().setText("player 2 win"); } } } //玩家與炸彈碰撞判斷 private void playerBoom() { List<SuperElement> playerList = ElementManager.getManager().getElementList("player"); List<SuperElement> explodeList = ElementManager.getManager().getElementList("explode"); for(int i=0; i<playerList.size(); i++) { for(int j=0; j<explodeList.size(); j++) { if(explodeList.get(j).crash(playerList.get(i))){ Player player = (Player) playerList.get(i); player.setHealthPoint(-1);//生命值-1 } } } } //npc與炸彈碰撞判斷 private void npcBoom() { List<SuperElement> playerList = ElementManager.getManager().getElementList("player"); List<SuperElement> npcList = ElementManager.getManager().getElementList("npc"); List<SuperElement> explodeList = ElementManager.getManager().getElementList("explode"); for(int i=0; i<npcList.size(); i++) { for(int j=0; j<explodeList.size(); j++) { if(explodeList.get(j).crash(npcList.get(i))){ Npc npc = (Npc) npcList.get(i); npc.setDead(true); npc.setX(-100); npc.setY(-100); BubbleExplode e = (BubbleExplode)explodeList.get(j); if(e.getPlayerNum()<2)//目前只有玩家計分 ((Player)playerList.get(e.getPlayerNum())).setScore(((Player)playerList.get(e.getPlayerNum())).getScore()+50); } } } } //障礙物與炸彈碰撞判斷 private void fragilityBoom() { List<SuperElement> playerList = ElementManager.getManager().getElementList("player"); List<SuperElement> explodes = ElementManager.getManager().getElementList("explode"); List<SuperElement> fragility = ElementManager.getManager().getElementList("fragility"); for(int i=0; i<fragility.size(); i++) { for(int j=0; j<explodes.size(); j++) { if(explodes.get(j).crash(fragility.get(i))) { MapFragility mapFragility = (MapFragility)fragility.get(i); mapFragility.setDestoried(true); BubbleExplode e = (BubbleExplode)explodes.get(j); if(e.getPlayerNum()<2)//目前只有玩家計分 ((Player)playerList.get(e.getPlayerNum())).setScore(((Player)playerList.get(e.getPlayerNum())).getScore()+10); } } } } //玩家與道具碰撞判斷 private void playerMagicBox() { List<SuperElement> playerList = ElementManager.getManager().getElementList("player"); List<SuperElement> magicBoxList = ElementManager.getManager().getElementList("magicBox"); for(int i=0; i<playerList.size(); i++) { for(int j=magicBoxList.size()-1; j>=0; j--) { if(magicBoxList.get(j).crash(playerList.get(i))){ MagicBox magicBox = (MagicBox) magicBoxList.get(j); magicBox.setCharacterIndex(i);//誰吃方塊 magicBox.setEaten(true);//方塊被吃 ((Player)playerList.get(i)).setScore(((Player)playerList.get(i)).getScore()+30); } } } } //玩家與道具碰撞判斷 private void npcMagicBox() { List<SuperElement> npcList = ElementManager.getManager().getElementList("npc"); List<SuperElement> magicBoxList = ElementManager.getManager().getElementList("magicBox"); for(int i=0; i<npcList.size(); i++) { for(int j=magicBoxList.size()-1; j>=0; j--) { if(magicBoxList.get(j).crash(npcList.get(i))){ MagicBox magicBox = (MagicBox) magicBoxList.get(j); magicBox.setCharacterIndex(i+2);//誰吃方塊 magicBox.setEaten(true);//方塊被吃 } } } } //runGame調(diào)用,加入拓展 public void linkGame() {} public static int getAllTime() { return allTime; } }
核心線程類
public class GameKeyListener implements KeyListener{ /** * 用棧來解決按鍵沖突 * 每個棧用來存放不同用戶的按鍵,通過判斷按鍵的code來設(shè)置移動方向或者攻擊 * */ private List<?> list; private Stack<Integer> p1PressStack = new Stack<>(); private Stack<Integer> p2PressStack = new Stack<>(); @Override public void keyPressed(KeyEvent e) { list = ElementManager.getManager().getElementList("player"); Player player1 = (Player) list.get(0); int code = e.getKeyCode(); switch (code) { case 10://炸彈鍵 if(player1.isKeepAttack())//不允許一直按著炸彈鍵,每次只能放一個炸彈 player1.setAttack(false); else { player1.setKeepAttack(true); player1.setAttack(true); } break; case 37://左右上下 case 38: case 39: case 40: if(!p1PressStack.contains(code)) { p1PressStack.push(code); } player1.setMoveType(MoveTypeEnum.codeToMoveType(code)); break; default://其它按鍵無視 break; } if(GameController.isTwoPlayer()) { Player player2 = (Player) list.get(1); switch (code) { case 32: if(player2.isKeepAttack()) player2.setAttack(false); else { player2.setKeepAttack(true); player2.setAttack(true); } break; case 65: case 87: case 68: case 83: if(!p2PressStack.contains(code)) { p2PressStack.push(code); } player2.setMoveType(MoveTypeEnum.codeToMoveType(code)); break; default: break; } } } @Override public void keyReleased(KeyEvent e) { List<?> list = ElementManager.getManager().getElementList("player"); int code = e.getKeyCode(); Player player1 = (Player) list.get(0); if(!player1.isDead()) { switch (code) { case 10: player1.setAttack(false); player1.setKeepAttack(false); break; case 37: case 38: case 39: case 40: if(p1PressStack.peek()!=code) { p1PressStack.remove(new Integer(code)); } else { p1PressStack.pop(); if(p1PressStack.size()==0) { player1.setMoveType(MoveTypeEnum.STOP); } else { player1.setMoveType(MoveTypeEnum.codeToMoveType(p1PressStack.peek())); } } break; default: break; } } if(GameController.isTwoPlayer()) { Player player2 = (Player) list.get(1); if(!player2.isDead()) { switch (code) { case 32: player2.setAttack(false); player2.setKeepAttack(false); break; case 65: case 87: case 68: case 83: if(p2PressStack.peek()!=code) { p2PressStack.remove(new Integer(code)); } else { p2PressStack.pop(); if(p2PressStack.size()==0) { player2.setMoveType(MoveTypeEnum.STOP); } else { player2.setMoveType(MoveTypeEnum.codeToMoveType(p2PressStack.peek())); } } break; default: break; } } } } @Override public void keyTyped(KeyEvent arg0) { // TODO 自動生成的方法存根 } public void clearKeyStatcks() { p1PressStack.clear(); p2PressStack.clear(); } }
總結(jié)
通過此次的《泡泡堂對戰(zhàn)版》實現(xiàn),讓我對JAVA的相關(guān)知識有了進一步的了解,對java這門語言也有了比以前更深刻的認識。
java的一些基本語法,比如數(shù)據(jù)類型、運算符、程序流程控制和數(shù)組等,理解更加透徹。java最核心的核心就是面向?qū)ο笏枷?,對于這一個概念,終于悟到了一些。
以上就是Java實現(xiàn)泡泡堂對戰(zhàn)版游戲的示例代碼的詳細內(nèi)容,更多關(guān)于Java泡泡堂的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決Springboot配置excludePathPatterns不生效的問題
這篇文章主要介紹了解決Springboot配置excludePathPatterns不生效的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-10-10java實現(xiàn)隨機森林RandomForest的示例代碼
本篇文章主要介紹了java實現(xiàn)隨機森林RandomForest的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08Spring實戰(zhàn)之使用@Resource配置依賴操作示例
這篇文章主要介紹了Spring實戰(zhàn)之使用@Resource配置依賴操作,結(jié)合實例形式分析了Spring使用@Resource配置依賴具體步驟、實現(xiàn)及測試案例,需要的朋友可以參考下2019-12-12Java中動態(tài)地改變數(shù)組長度及數(shù)組轉(zhuǎn)Map的代碼實例分享
這篇文章主要介紹了Java中動態(tài)地改變數(shù)組長度及數(shù)組轉(zhuǎn)map的代碼分享,其中轉(zhuǎn)Map利用到了java.util.Map接口,需要的朋友可以參考下2016-03-03Java實現(xiàn)實時監(jiān)控目錄下文件變化的方法
今天小編就為大家分享一篇關(guān)于Java實現(xiàn)實時監(jiān)控目錄下文件變化的方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03