Java泡泡堂
小虚竹and掘金 人气:0前言
《泡泡堂II》是一个基于java的自制游戏,使用了MVC模式
,分离了模型、视图和控制器,使得项目结构清晰易于扩展,使用配置文件来设置游戏基本配置,扩展地图人物道具等。同时,该程序编写期间用了单例模式、工厂模式、模板模式等设计模式。为了游戏的可玩性,特意设计了平滑碰撞以及机器人。
主要设计
- 设计游戏界面,用swing实现
- 绘制游戏启动界面、结束界面、地图、主角、道具
- 实现泡泡爆炸
- 为了尽量复原泡泡堂游戏,初步实现了机器人功能。该机器人可以判断障碍物释放炸弹、规避炸弹、攻击玩家。
- 实现道具掉落和相应属性加成
- 实现游戏音效和背景音乐
- 平滑碰撞:人物在拐角处移动的时候经常不是刚好对齐的状态,程序会判定玩家碰撞了障碍物所以导致玩家无法拐弯。所以我们在处理这种情况的时候,会让玩家进行平滑的移动使得玩家看上去是滑进去的,增强玩家游戏体验
- 设计单/双人模式
功能截图
游戏启动界面:
道具说明:
游戏开始:
释放炸弹·:
炸弹爆炸效果:
代码实现
游戏启动类
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); //强制刷新,否则监听无效 gameFrame.setVisible(false); gameFrame.setVisible(true); } public static void startNewGame() { GameController.setGameRunning(true); gameFrame.startGame(); changeJPanel("game"); } }
核心监听类
public class GameThread extends Thread{ private boolean running; //表示当前关卡是否在进行 private boolean over = false; //表示游戏是否结束,结束返回开始菜单 private static int sleepTime = 20; //runGame刷新时间 //倒计时变量 private static int allTime = 600*1000; //10分钟 @Override public void run() { while(!over) { running = true;//当前关卡正在进行 //加载元素 loadElement(); //显示人物,流程,自动化 runGame(); //结束当前关 overGame(over); } GameStart.changeJPanel("over"); } //加载元素 private void loadElement() { ElementManager.getManager().loadMap();//加载地图及其元素 } /** * 关卡结束 * 如果over为真则游戏失败返回界面,否则进入下一关 * @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调用,加入拓展 public void linkGame() {} public static int getAllTime() { return allTime; } }
核心线程类
public class GameKeyListener implements KeyListener{ /** * 用栈来解决按键冲突 * 每个栈用来存放不同用户的按键,通过判断按键的code来设置移动方向或者攻击 * */ 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(); } }
总结
通过此次的《泡泡堂对战版》实现,让我对JAVA的相关知识有了进一步的了解,对java这门语言也有了比以前更深刻的认识。
java的一些基本语法,比如数据类型、运算符、程序流程控制和数组等,理解更加透彻。java最核心的核心就是面向对象思想,对于这一个概念,终于悟到了一些。
加载全部内容