1. 團隊課程設計博客鏈接
https://www.cnblogs.com/choco1ate/p/12172223.html
2.需求分析
(1)人物屬性:
生命值,攜帶炸彈數,移動速度,炸彈威力
(2)通過讀取人物能夠丟炸彈,並且在人物向不同方向移動的時候,人物方向也會隨之改動
(3)道具:
加速道具,增加炸彈攜帶數量,增加炸彈威力
無敵南瓜-吃到后獲得5秒的無敵效果
生命泡泡-吃到該道具后生命值加1
(4)游戲背景音樂和游戲地圖在每次游戲啟動的時候能夠隨機改動
3. 本組課題及本人任務
本組課題:泡泡堂游戲
本人任務:主要編寫Player類以及PlayerAttribute類,添加部分道具以及游戲背景音樂以及部分地圖
本人在項目中的Git提交記錄截圖
4.代碼分析
private static final long serialVersionUID = 1L;
/**玩家鍵盤控制參數,初始均為false*/
private boolean bL = false, bU = false, bD = false, bR = false;
/**玩家當前生命值,初始4*/
public int live=4;
/**玩家是否存活*/
public boolean isalive=true;
/**玩家當前速度,初始10*/
public int speed=10;
/**玩家速度限制最大值*/
public int maxspeed=20;
/**玩家同時可投炸彈數,初始1*/
public int bombnum=1;
/**玩家可投炸彈數限制最大值*/
public int maxbombnum=6;
/**現存的炸彈數*/
int bombexist=0;
/**玩家當前炸彈威力,初始1*/
public int power=1;
/**玩家炸彈威力限制最大值*/
public int maxpower=5;
/**人物方向轉動圖片*/
String pathU;
String pathD;
String pathL;
String pathR;
此部分代碼為玩家的各種初始屬性
Player類的構造函數
public Player(String pathU,String pathD,String pathL,String pathR,int x,int y,int index)//初始化玩家信息
{
this.pathU=pathU;
this.pathD=pathD;
this.pathL=pathL;
this.pathR=pathR;//四個存儲人物各個方向的圖片
imgPath=pathD;
this.index=index;//標記是1P還是2P玩家
x*=80;
y*=80;
this.x=x;
this.y=y;//人物的坐標
this.lastX=x;
this.lastY=y;//人物的上一坐標
this.setBounds(this.x,this.y,80,80);//設置人物大小
setIcon(pathD,this);//設置人物圖片
}
枚舉類 代表上、下、左、右和停止
public enum Direction {
//分別代表上下左右和停止
L, D, U, R, STOP
}
人物移動的實現:
通過一個有參方法進行。該函數通過對人物的坐標更新賦值,從而進行人物的移動,部分代碼如下
public void moveStep(int n)
{
int l;
int loop=80;
for(l=0;l<=loop;l+=speed)
{
switch(n)
{
case 1:
this.setLocation(x-l,y);
break;
case 2:
this.setLocation(x,y-l);
break;
case 3:
this.setLocation(x+l,y);
break;
case 4:
this.setLocation(x,y+l);
break;
default:
break;
}
//如果遇到碰撞物,則不移動
if(meetbox())
{
break;
}
if(l<80)
{
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//如果循環后最后移動值不為80,則修復。強制用戶每次只能移動一格的距離
if(l!=loop)
{
l=80;
switch(n)
{
case 1:
this.setLocation(x-l,y);
break;
case 2:
this.setLocation(x,y-l);
break;
case 3:
this.setLocation(x+l,y);
break;
case 4:
this.setLocation(x,y+l);
break;
default:
break;
}
}
}
通過讀取方向變量dir,調用moveStep函數移動人物
public void move() {
//定義上一位置
this.lastX = x;
//定義上一位置
this.lastY = y;
//在不遇到(碰撞物、邊界、炸彈)的情況下,每40μs移動speed個像素,一共移動80像素(地圖上一個格子的寬度)
switch (dir) {
//向左移動
case L:
if(!invincible)
{
setIcon(pathL);
}
moveStep(1);
break;
//向下移動
case U:
if(!invincible)
{
setIcon(pathU);
}
moveStep(2);
break;
//向右移動
case R:
if(!invincible)
{
setIcon(pathR);
}
moveStep(3);
break;
//向下移動
case D:
if(!invincible)
{
setIcon(pathD);
}
moveStep(4);
break;
case STOP:
//停止
break;
default:
break;
}
//更新x,y坐標值
this.x=this.getX();
this.y=this.getY();
}
meetbox方法來檢測是否遇到了各種障礙物,如果檢測到了障礙物則返回false結果
boolean meetbox()
{
int x = 1120;
int y = 880;
if(this.getX()<0||this.getX()>x||this.getY()<0||this.getY()>y)
{
return true;
}
int w = 15;
int h = 12;
for(int i=0;i<w;i++)
{
for(int j=0;j<h;j++)
{
Box temp=GameFrame.thismap.getBoxbyLocation(i,j);
if(temp.getRect().intersects(this.getRect())&&temp.isExist)
{
if(!temp.isdestroyshowT)
//遇到箱子
{
return true;
}
}
if(temp.isExistBomb&&!temp.isExistPlayer&&temp.getRect().intersects(this.getRect()))
//遇到炸彈
{
return true;
}
}
}
return false;
}
無敵道具效果的實現
class InvincibleThread2 extends Thread
{
@Override
public void run(){
//無敵時間開始
invincible=true;
int loop = 50;
//獲得5秒的閃爍無敵時間
for(int i=0;i<loop;i++)
{
//每0.1秒閃爍一個循環,一共閃爍50次
setIcon("images/default.png");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
setIcon("images/invinciblePlayer.png");
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
setIcon(imgPath);
invincible=false;
}
}
當玩家受到炸彈傷害時,調用這個方法
/**玩家被炸到(受傷)*/
public void beInjured()
{
//生命值-1
this.live--;
//設置玩家屬性面板的生命值
this.pla.setLabel("生命值:"+this.live,this.pla.live);
//如果死了(生命=0)
if(live==0)
{
//玩家死亡
this.isalive=false;
//玩家屬性面板頭像更換
setIcon("images/player"+this.index+"DIED.png",this.pla.photo);
//玩家圖片更換
setIcon("images/player"+this.index+"DIED.png",this);
//提示消息
JOptionPane.showMessageDialog(this,"玩家"+this.index+"陣亡!","GameOver",2);
//總人數減1
GameFrame.NumofAlive--;
//如果總存活數為1,則游戲結束
if(GameFrame.NumofAlive==1)
{
JOptionPane.showMessageDialog(this,"游戲結束!單機確認退出~","GameOver",2);
//顯示主界面
System.exit(0);
}
}
else
{
//定義玩家無敵的線程
Thread th=new InvincibleThread1();
th.start();
}
}
以下代碼為加速道具,加威力道具,加生命道具以及加攜帶炸彈數量道具,通過對相應的屬性增加進行功能的實現
public void plusspeed()
{
//如果當前速度小於最高速度
if(myP.speed<myP.maxspeed)
{
myP.speed+=2;
//更新速度數據
setLabel("速度:"+myP.speed,speed);
}
}
public void plusbombnum()
{
//如果當前炸彈數小於最高炸彈數
if(myP.bombnum<myP.maxbombnum)
{
//炸彈數加一
myP.bombnum++;
//更新炸彈數據
setLabel("泡泡數:"+myP.bombnum,bombnum);
}
}
public void pluspower()
{
if(myP.power<myP.maxpower)
//如果當前威力小於最高威力
{
//威力加一
myP.power++;
//更新威力數據
setLabel("威力:"+myP.power,power);
}
}
public void pluslive()
{
//生命加一
myP.live++;
//刷新生命值數據
setLabel("生命值:"+myP.live,live);
}
背景音樂的線程
public class Music extends Thread
{
public volatile boolean flag = true;
private String fileName;
private final int EXTERNAL_BUFFER_SIZE = 524288;
public Music(int n) {
switch (n)
{
case 1:
this.fileName="gameBgm.wav";
break;
case 2:
this.fileName="Music.wav";
break;
case 3:
this.fileName="MUsic2.wav";
break;
default :
break;
}
}
@Override
public void run() {
//1 獲取你要播放的音樂文件
File soundFile = new File(fileName);
if (!soundFile.exists()) {
System.err.println("Wave file not found:" + fileName);
return;
}
while (flag){
//2、定義一個AudioInputStream用於接收輸入的音頻數據
AudioInputStream audioInputStream = null;
try {
//3、使用AudioSystem來獲取音頻的音頻輸入流(處理(拋出)異常)
audioInputStream = AudioSystem.getAudioInputStream(soundFile);
} catch (UnsupportedAudioFileException e1) {
e1.printStackTrace();
return;
} catch (IOException e1) {
e1.printStackTrace();
return;
}
//4、使用AudioFormat來獲取AudioInputStream的格式
AudioFormat format = audioInputStream.getFormat();
//5、一個源數據行
SourceDataLine auline = null;
//6、獲取受數據行支持的音頻格式DataLine.info
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
try {
//7、獲取與上面類型相匹配的行 寫到源數據行里
auline = (SourceDataLine) AudioSystem.getLine(info);
auline.open(format);
} catch (LineUnavailableException e) {
e.printStackTrace();
return;
} catch (Exception e) {
e.printStackTrace();
return;
}
//9 允許某個數據行執行數據i/o
auline.start();
//10、寫數據
int nBytesRead = 0;
//設置字節數組大小
byte[] abData = new byte[EXTERNAL_BUFFER_SIZE];
try {
//11、從音頻流讀取指定的最大數量的數據字節,並將其放入給定的字節數組中
while (nBytesRead != -1) {
nBytesRead = audioInputStream.read(abData, 0, abData.length);
if (nBytesRead >= 0) {
//12、讀取了之后將數據寫入混頻器,開始播放
auline.write(abData, 0, nBytesRead);
}
}
} catch (IOException e) {
e.printStackTrace();
return;
} finally {
//關閉
auline.drain();
auline.close();
}
}
總結音樂播放的步驟:
總結步驟:
1 獲取你要播放的音樂文件
2、定義一個AudioInputStream用於接收輸入的音頻數據
3、使用AudioSystem來獲取音頻的音頻輸入流(處理(拋出)異常)
4、使用AudioFormat來獲取AudioInputStream的格式
5、創建一個源數據行
6、獲取受數據行支持的音頻格式 DataLine.info 如果采用.getSourceDataLine()方法可以省略)
7、獲取與上面類型相匹配的行 寫到源數據行里 二選一
8、打開具有指定格式的行,這樣可以使行獲得資源並進行操作
9、允許某個數據行執行數據i/o
10、寫數據
11、從音頻流讀取指定的最大數量的數據字節,並將其放入給定的字節數組中。
12、讀取哪個數組
13、讀取了之后將數據寫入混頻器,開始播放