# 基于JAVA实现简易版泡泡堂小游戏
# 一、简介——童年记忆
《泡泡堂》是由韩国游戏公司Nexon开发的一款休闲游戏(Casual Game),于2003年在中国大陆上线,由盛大网络运营。游戏讲述了在哈巴森林的一个村落的村民们利用神奇的水泡来打猎和采集宝石,故事由为拯救村民和夺回被海盗抢去的宝石而展开。
该游戏设有8位基本角色、2位隐藏角色和在基本角色上进阶的新角色。卡通的人物形象、多种道具、饰品和搞怪表情,是一款适合任何年龄的休闲类网游。
借JAVA课程大作业的契机,我决定用JAVA来做一个简易版的泡泡堂!
![](http://www.writebug.com/myres/static/uploads/2021/10/19/5a80ade90be91e90e294687e4b5ba696.writebug)
# 二、程序设计原理、目的,算法说明
## 2.1 场景布局
泡泡堂采用乐高积木的风格,每个单元布局一个道具或者场景元素,因而布局比较规整,用代码实现也比较方便。
![](http://www.writebug.com/myres/static/uploads/2021/10/19/389e4fac527c0a1f9b857c7bbe1c8ce0.writebug)
用大小13\*15数组的方式存放地图元素信息,每个单元格需要放置什么样元素,repaint回调函数依据大小13\*15数组信息进行场景的绘制。
![](http://www.writebug.com/myres/static/uploads/2021/10/19/6319cbe7824477f78484e9239c51c27e.writebug)
数组实现方式:
```java
// 地图信息
static int [][]MAP_INFO = { {GRASS,BOXO,BOXR,BOXO,BOXR,BUSH,ROAD,ROAD2,BOX,BUSH,HomeY,BOXR,HomeY,GRASS,HomeY},
{GRASS,HomeR,BOX,HomeR,BOX,Tree,BOX,ROAD2,ROAD,Tree,BOXR,BOXO,GRASS,GRASS,GRASS},
.......
}
// 图片读取
imgrule=ImageIO.read(new File("image/rule/rule.png"));
imgmap[GRASS]=ImageIO.read(new File("image/map/grass.png"));
......
```
**实现结果**
![](http://www.writebug.com/myres/static/uploads/2021/10/19/07c63ceaa68483929514293c089019cd.writebug)
## 2.2 人物
人物的动作需要多帧来完成,多帧的图片也用二维数组来存储。例如:
```java
BabyMove = ImageIO.read(new File("image/player/Role1.png"));
int imgWidth = BabyMove.getWidth();
int imgHeight = BabyMove.getHeight();
// 读入移动的24帧图像
for(int i=0;i<4;i++){
for(int j=0;j<6;j++){
ImageBuffer[i][j] = BabyMove.getSubimage(j*imgWidth/6, i*imgHeight/4, imgWidth/6, imgHeight/4);
}
}
```
![](http://www.writebug.com/myres/static/uploads/2021/10/19/128b7af03b52434fcd5e59cd03e541b5.writebug)
实现动画帧的播放,从而获得更加真实的效果。
## 2.3 炸弹
泡泡角色通过释放炸弹摧毁乐高积木来获取道具以及杀死对手。炸弹释放后不会立即爆炸,而是在释放若干时间后自行爆炸。炸弹的爆炸有连锁反应。
![](http://www.writebug.com/myres/static/uploads/2021/10/19/03bc0f266ab4a701e761d609f2164d28.writebug)
炸弹的定时效果由线程定时器控制,定时器会在后面介绍。
炸弹的水波也用贴图动画的形式实现,为了获得的爆炸效果,炸弹的贴图动态程度更高一些。如果仔细看的话,不同点的爆炸纹理是不同的。
![](http://www.writebug.com/myres/static/uploads/2021/10/19/2aa56a15102b36c43c9c6b6a8fea482e.writebug)
**炸弹的算法**在这里需要一提。
炸弹的水波蔓延用**宽度搜索**的方式进行实现,在宽度搜索的过程中,如果当前距离小于水波长度,依次执行以下内容:
- 判定当前区域,如果是房子或者树不可蔓延,并且停止继续蔓延;如果是盒子则进行炸毁,如果是人物则杀死人物
- 如果是炮弹的话,都刷新炮弹的时间。从而实现连环爆炸
- 根据当前位置进行贴图
- 继续蔓延直至超出炸弹能力
## 2.4 道具
玩家通过拾取不同的道具,从而获得不同能力的增长。例如拾取跑鞋可以加速,拾取炸弹可以增加释放炸弹的数量,拾取药水可以增加炸弹压力等等。拾取宠物可以获得坐骑,坐骑可以抵一条命。
![](http://www.writebug.com/myres/static/uploads/2021/10/19/7f54e6a397f982df6c0bf3f07c39dc3d.writebug)
如果捡到坐骑的话,人物动画切换坐骑动画。在实现过程中,就是repaint函数画人物时,检测Role类成员变量pet的值,根据pet的情况进行绘图。
![](http://www.writebug.com/myres/static/uploads/2021/10/19/c5b6a2f4f06b3c1b6d9813496a5d996a.writebug)
## 2.5 被炸弹炸死
当人物被水波炸到时,会被水波困住,然后爆炸死亡,游戏结束。
![](http://www.writebug.com/myres/static/uploads/2021/10/19/38371b00e226aac1a3ab510097a1802b.writebug)
![](http://www.writebug.com/myres/static/uploads/2021/10/19/2bdfb34ace1c5e7034fc9ce7ff1e2ed1.writebug)
爆炸死亡时会有动画,为获得更好的游戏效果。动画同样用播放图片数组来实现帧的效果。
## 2.6 线程定时器
在本工程中有专门的一个TimerTest的类,内含大量的静态函数继承Timertask的函数,供游戏中需要定时器的功能,例如人物移动动画,开场动画,炸弹定时器,音乐定时器等等,有着广泛的应用。以下是炸弹定时器的一个例子。
```java
// 炸弹定时器
static class MyTimerBump extends TimerTask {
void SetInterLinkBump(int ci,int cj, int init_i, int init_j){
if(cj > init_j)
Map.BUMB_INFO[cj][ci] = 4;
else if(cj == init_j && ci >= init_i){
Map.BUMB_INFO[cj][ci] = 4;
}
else
Map.BUMB_INFO[cj][ci] = 3;
int wavelength = Map.mapcanvas.Role1.getWaveLength();
for(int i=1;i<=wavelength;i++){
if(ci+i<15 && Map.BUMB_INFO[cj][ci+i]>4){
SetInterLinkBump(ci+i,cj,init_i,init_j);
}
if(ci-i>=0 && Map.BUMB_INFO[cj][ci-i]>4){
SetInterLinkBump(ci-i,cj,init_i,init_j);
}
if(cj-i>=0 && Map.BUMB_INFO[cj-i][ci]>4){
SetInterLinkBump(ci,cj-i,init_i,init_j);
}
if(cj+i<13 && Map.BUMB_INFO[cj+i][ci]>4){
SetInterLinkBump(ci,cj+i,init_i,init_j);
}
}
return;
}
```
## 2.7 音乐播放
背景音乐与特效音乐(鼠标点击、炸弹爆炸),都由线程来负责。
```java
public class Game extends JFrame{
Game(){
new Timer().schedule(new TimerTest.StartMusic(), 0);
Map map=new Map();
}
}
```
音乐分两种,一种是循环播放(背景音乐),另一种是只播放一次(例如炸弹)。代码只是一个参数的差别。
```java
// 线程播放炸弹声音
static class BumbSound extends TimerTask {
public void run() {
try
{
URL MusicURL;
File MusicFile = new File("sound/explode.wav");
MusicURL = MusicFile.toURL();
AudioClip clip=(AudioClip) Applet.newAudioClip( MusicURL);
clip.play();
}catch(Exception mue){
System.out.println(mue.toString());
}
}
}
```
# 三、程序流程框图、调用函数关系、文件列表
## 3.1 程序流程框图
![](http://www.writebug.com/myres/static/uploads/2021/10/19/ec7232106f750563d534c0b154b2e807.writebug)
## 3.2 重要函数与类介绍
- **Game类**:启动游戏
- **Map类**:存储地图信息
```java
//炸弹信息
static int [][] BUMB_INFO
//炸弹归属信息
static int [][] BUMB_ROLE
//爆炸水波信息
static int [][] EXPLOSION_INFO
//道具信息
static int [][] GIFT_INFO
//初始化
static void Init()
//构造函数
public Map()
```
- **MapCanvas**:画布类,图片读取,根据信息画布绘制
```java
// 角色信息
Player Role1 = new Player(1,Role1_x,Role1_y);
Player Role2 = new Player(2,Role2_x,Role2_y);
// 地图信息
static int [][]MAP_INFO
// 初始化
void I