一、需求分析
泡泡堂(Bubble Trouble)是一款经典的在线闯关小游戏,最初由Kresimir Cvitanovic开发。在这款游戏中,玩家控制一个小人,通过射击气泡来消灭敌人。游戏的目标是在每个关卡中消灭所有敌人,避免被气泡碰到或被敌人攻击。游戏的关卡设计相对简单,但随着关卡的深入,敌人的数量和难度会逐渐增加。
泡泡堂的玩法非常简单,但却具有一定的挑战性,玩家需要在尽可能短的时间内消灭所有敌人,同时避免受到伤害。游戏中的气泡可以分裂成更小的气泡,使得玩家需要更加灵活地躲避和射击。
这款游戏在网络上非常受欢迎,因其简单直观的操作和有趣的玩法而备受喜爱。泡泡堂的游戏机制吸引了许多玩家,成为了经典的在线闯关游戏之一。
【预期实现效果】:
二、分析实现
1、前期准备
CrazyArcade
泡泡堂游戏,一款用Java编写的JavaSwing游戏程序
。 使用了MVC模式
,分离了模型、视图和控制器。同时,使用配置文件来设置游戏基本配置,扩展地图人物道具等。同时,该程序编写期间用了单例模式、工厂模式、模板模式等设计模式。
项目架构:
- frame:包含如游戏开始、游戏中、结束的(界面(JFrame)
- main:包含主程序开始入口和其它游戏控制
- model:
- loader:包含资源加载器用于读取配置文件等信息,使用了单例设计模式
- manager:包含元素管理器、工厂等,用于控制游戏各元素
- vo:各种实体类,包括玩家、NPC、炸弹、方块等等
- pro:配置文件目录,包含人物、道具、地图、方块、游戏设置等配置文件
- thread:配合游戏一起执行的各种线程,如音乐、键盘监听、游戏控制等
- utiil:工具包
2、地图生成
地图主要是生成游戏的场景,然后在画布上显示该场景。在开发很多类型的游戏中,地图系统都需要良好的设计,直观的说,需要的地图系统仅仅是一个2D数组,然后用最快的方式将数组影射到屏幕上。
本游戏设计的这个地图是用一些0,1等数组表示一种物体。然后在用流对该文件进行读取,把内容读取到一个数组里面。不同的数字就会在场景中显示出不同的物体。
#0:障碍物方块,1:道路方块,2:可摧毁方块,61:玩家1,73:玩家2(若是单人模式则表示为电脑),8:电脑
#00:空气墙,01:树,02:草垛,03:蓝色小房子,04:黄色小房子
#11:绿色方块,12:灰色地板
#21:橙色方块,22:红色方块,23:木头方块,24:深蓝色铁方块,25:浅蓝色铁方块,
#26:红色路障方块,27:警示牌方块,28:纸箱子方
【墙体素材】
3、玩家移动
该对战过程处理主要是对键盘的监听。当按下键盘的方向键的时候,监听器将捕获该动作,然后将该动作的信息封装为一个消息,在服务器端再对消息进行转发给所有玩家。玩家收到消息,对消息进行解析,得用户的操作,然后改变玩家对应单元格的代码,然后去刷新整个画布,玩家的位置将做出变化。其中在移动玩家的时候,要先判断前方是否有障碍物或者是否已经到达了边界,是的话将不能进行移动。
@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;
}
}
}
在每次泡泡爆炸后,都要进行一次判断,是否有玩家被炸到。如果有就将该玩家的状态设置为’DEAD’,然后判断整个小组是否所有成员都已经淘汰
有以下三种情况:
- 玩家被其他玩家淘汰;
- 当前玩家淘汰所有人赢得游戏;
- 游戏对局还存在多人未被淘汰,游戏超时;
//玩家失败
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");
}
}
4、机器人NPC
为了尽量复原泡泡堂游戏,笔者初步实现了机器人功能。该机器人可以判断障碍物释放炸弹、规避炸弹、攻击玩家。
控制npc放炸弹:
设计了一个简单拿的寻路算法,主要是遍历上下左右四个方向。指导原则是前方有炸弹不能走,靠经其他npc不走;主动靠近真人玩家;死路也不走。
判断是否可以打通的路,有路就放个炸弹,炸掉障碍物,开辟道路。
private void autoAddBubble() {
GameMap gameMap = ElementManager.getManager().getGameMap();
List<Integer> loc = GameMap.getIJ(getX(), getY());
Vector<MoveTypeEnum> oldPath = new Vector<>();
oldPath.addAll(path);
gameMap.setBlockSquareType(loc, GameMap.SquareType.BUBBLE);
boolean find = findSafePath();
gameMap.setBlockSquareType(loc, GameMap.SquareType.FLOOR);
if(find&&getBubbleLargest()-getBubbleNum()>0&&Math.random()<0.1) {
addBubble();
} else {
path.clear();
path.addAll(oldPath);
}
return;
}
5、平滑碰撞
人物在拐角处移动的时候经常不是刚好对齐的状态,程序会判定玩家碰撞了障碍物所以导致玩家无法拐弯。所以我们在处理这种情况的时候,会让玩家进行平滑的移动使得玩家看上去是滑进去的,增强玩家游戏体验
//判断前面是否能走过去
public boolean judgeForward(MoveTypeEnum m) {
GameMap gameMap = ElementManager.getManager().getGameMap();
boolean go = false;
List<Integer> ijList = GameMap.getIJ(getX(), getY());
//暂时判断前面是地板即可前进,TODO判断前面是Bubble
switch(m) {
case LEFT: if(gameMap.blockIsWalkable(ijList.get(0), ijList.get(1)-1)) go = true;
break;
case RIGHT: if(gameMap.blockIsWalkable(ijList.get(0), ijList.get(1)+1)) go = true;
break;
case TOP: if(gameMap.blockIsWalkable(ijList.get(0)-1, ijList.get(1))) go = true;
break;
case DOWN: if(gameMap.blockIsWalkable(ijList.get(0)+1, ijList.get(1))) go = true;
break;
case STOP: go=true;
break;
}
return go;
}
地板暂时相对滑动,保证玩家不会被固定的坐标束缚,导致很难进入一个路口,提高体验感。
三、界面展示
1、主界面
2、游戏过程
3、游戏支持
【道具类型】
【爆炸效果】
【道具说明】