Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Guangjun Jia
FlappyBird
Commits
d7b64a56
Commit
d7b64a56
authored
Jul 10, 2020
by
kingyu
Committed by
kingyuluk
Jul 10, 2020
Browse files
feat: 具备原版的游戏功能
parent
b9468532
Changes
49
Hide whitespace changes
Inline
Side-by-side
src/com/bird/main/GameForeground.java
0 → 100644
View file @
d7b64a56
package
com.bird.main
;
import
java.awt.Graphics
;
import
java.awt.image.BufferedImage
;
import
java.util.ArrayList
;
import
java.util.List
;
import
com.bird.util.Constant
;
import
com.bird.util.GameUtil
;
/**
* 前景类, 游戏中的遮挡层 包含多朵云
*
* @author Kingyu
*
*/
public
class
GameForeground
{
private
List
<
Cloud
>
clouds
=
new
ArrayList
<>();
// 云朵的容器
private
BufferedImage
[]
cloudImgs
;
// 图片资源
private
int
cloudDir
;
// 云的方向
private
long
time
;
// 控制云的逻辑运算周期
public
static
final
int
CLOUD_INTERVAL
=
100
;
//云朵刷新的逻辑运算的周期
public
GameForeground
()
{
clouds
=
new
ArrayList
<>();
//云朵的容器
// 读入图片资源
cloudImgs
=
new
BufferedImage
[
Constant
.
CLOUD_IMAGE_COUNT
];
for
(
int
i
=
0
;
i
<
Constant
.
CLOUD_IMAGE_COUNT
;
i
++)
{
cloudImgs
[
i
]
=
GameUtil
.
loadBUfferedImage
(
Constant
.
CLOUDS_IMG_PATH
[
i
]);
}
// 初始化云朵的属性
cloudDir
=
Cloud
.
DIR_LEFT
;
time
=
System
.
currentTimeMillis
();
// 获取当前时间,用于控制云的逻辑运算周期
}
// 绘制方法
public
void
draw
(
Graphics
g
,
Bird
bird
)
{
cloudLogic
();
for
(
int
i
=
0
;
i
<
clouds
.
size
();
i
++)
{
clouds
.
get
(
i
).
draw
(
g
,
bird
);
}
}
// 云朵的控制
private
void
cloudLogic
()
{
// 100ms运算一次
if
(
System
.
currentTimeMillis
()
-
time
>
CLOUD_INTERVAL
)
{
time
=
System
.
currentTimeMillis
();
// 重置time
// 如果屏幕的云朵的数量小于允许的最大数量,根据给定的概率随机添加云朵
if
(
clouds
.
size
()
<
Constant
.
MAX_CLOUD_COUNT
)
{
try
{
if
(
GameUtil
.
isInProbability
(
Constant
.
CLOUD_BORN_PERCENT
,
100
))
{
// 根据给定的概率添加云朵
int
index
=
GameUtil
.
getRandomNumber
(
0
,
Constant
.
CLOUD_IMAGE_COUNT
);
// 随机选取云朵图片
// 云朵刷新的坐标
int
x
=
Constant
.
FRAME_WIDTH
;
// 从屏幕左侧开始刷新
// 根据当前的方向调整云朵刷新的x坐标(暂时无用)
// if (cloudDir == Cloud.DIR_LEFT) {
// } else {
// x = -1 * cloudImgs[index].getWidth();
// }
// y坐标随机在上1/3屏选取
int
y
=
GameUtil
.
getRandomNumber
(
Constant
.
TOP_BAR_HEIGHT
,
Constant
.
FRAME_HEIGHT
/
3
);
//向容器中添加云朵
Cloud
cloud
=
new
Cloud
(
cloudImgs
[
index
],
cloudDir
,
x
,
y
);
clouds
.
add
(
cloud
);
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
// 添加云朵
// 若云朵飞出屏幕则从容器中移除
for
(
int
i
=
0
;
i
<
clouds
.
size
();
i
++)
{
// 遍历容器中的云朵
Cloud
tempCloud
=
clouds
.
get
(
i
);
if
(
tempCloud
.
isOutFrame
())
{
clouds
.
remove
(
i
);
i
--;
}
}
/*
* 功能已实现,但不太符合现实,暂时注释掉 // 云朵随机改变方向, try { if (GameUtil.isInProbability(1,
* Constant.CLOUD_DIRCHANGE)) { // 新的云彩方向 int newDir =
* GameUtil.getRandomNumber(Cloud.DIR_LEFT, Cloud.DIR_RIGHT + 1); if (newDir !=
* cloudDir) { // 风向改变,调整云彩状态 cloudDir = newDir; for (int i = 0; i <
* clouds.size(); i++) { // 遍历容器中的云朵 Cloud tempCloud = clouds.get(i);
* tempCloud.setDir(newDir); } } System.out.println(cloudDir); } } catch
* (Exception e) { e.printStackTrace(); }
*/
}
}
}
\ No newline at end of file
src/com/bird/main/GameFrame.java
0 → 100644
View file @
d7b64a56
package
com.bird.main
;
import
static
com
.
bird
.
util
.
Constant
.
FRAME_HEIGHT
;
import
static
com
.
bird
.
util
.
Constant
.
FRAME_WIDTH
;
import
static
com
.
bird
.
util
.
Constant
.
FRAME_X
;
import
static
com
.
bird
.
util
.
Constant
.
FRAME_Y
;
import
static
com
.
bird
.
util
.
Constant
.
GAME_INTERVAL
;
import
static
com
.
bird
.
util
.
Constant
.
GAME_TITLE
;
import
java.awt.Frame
;
import
java.awt.Graphics
;
import
java.awt.event.KeyEvent
;
import
java.awt.event.KeyListener
;
import
java.awt.event.WindowAdapter
;
import
java.awt.event.WindowEvent
;
import
java.awt.image.BufferedImage
;
import
com.bird.util.MusicUtil
;
/**
* 主窗口类,游戏窗口和绘制的相关内容
*
* @author Kingyu
*
*/
public
class
GameFrame
extends
Frame
implements
Runnable
{
private
static
final
long
serialVersionUID
=
1L
;
// 保持版本的兼容性
private
static
int
gameState
;
// 游戏状态
public
static
final
int
STATE_READY
=
0
;
// 游戏未开始
public
static
final
int
STATE_START
=
1
;
// 游戏开始
public
static
final
int
STATE_OVER
=
2
;
// 游戏结束
private
GameBackground
background
;
// 游戏背景对象
private
GameForeground
foreground
;
// 游戏前景对象
private
Bird
bird
;
// 小鸟对象
private
GameElementLayer
gameElement
;
// 游戏元素对象
private
GameReady
ready
;
// 游戏未开始时对象
// 在构造器中初始化
public
GameFrame
()
{
initFrame
();
// 初始化游戏窗口
setVisible
(
true
);
// 窗口默认为不可见,设置为可见
initGame
();
// 初始化游戏对象
}
// 初始化游戏窗口
private
void
initFrame
()
{
setSize
(
FRAME_WIDTH
,
FRAME_HEIGHT
);
// 设置窗口大小
setTitle
(
GAME_TITLE
);
// 设置窗口标题
setLocation
(
FRAME_X
,
FRAME_Y
);
// 窗口初始位置
setResizable
(
false
);
// 设置窗口大小不可变
// 添加关闭窗口事件(监听窗口发生的事件,派发给参数对象,参数对象调用对应的方法)
addWindowListener
(
new
WindowAdapter
()
{
@Override
public
void
windowClosing
(
WindowEvent
e
)
{
System
.
exit
(
0
);
// 结束程序
}
});
addKeyListener
(
new
BirdKeyListener
());
// 添加按键监听
}
// 用于接收按键事件的对象的内部类
class
BirdKeyListener
implements
KeyListener
{
// 按键按下,根据游戏当前的状态调用不同的方法
public
void
keyPressed
(
KeyEvent
e
)
{
int
keycode
=
e
.
getKeyChar
();
switch
(
gameState
)
{
case
STATE_READY:
if
(
keycode
==
KeyEvent
.
VK_SPACE
)
{
bird
.
BirdUp
();
bird
.
BirdDown
();
setGameState
(
STATE_START
);
bird
.
startTiming
();
}
break
;
case
STATE_START:
if
(
keycode
==
KeyEvent
.
VK_SPACE
)
{
bird
.
BirdUp
();
bird
.
BirdDown
();
}
break
;
case
STATE_OVER:
if
(
keycode
==
KeyEvent
.
VK_SPACE
)
{
resetGame
();
}
break
;
}
}
// 定义重新开始游戏的方法
private
void
resetGame
()
{
setGameState
(
STATE_READY
);
gameElement
.
reset
();
bird
.
reset
();
}
// 按键松开,更改按键状态标志
public
void
keyReleased
(
KeyEvent
e
)
{
int
keycode
=
e
.
getKeyChar
();
if
(
keycode
==
KeyEvent
.
VK_SPACE
)
{
bird
.
keyReleased
();
}
}
public
void
keyTyped
(
KeyEvent
e
)
{
}
}
// 初始化游戏中的各个对象
private
void
initGame
()
{
background
=
new
GameBackground
();
gameElement
=
new
GameElementLayer
();
foreground
=
new
GameForeground
();
ready
=
new
GameReady
();
MusicUtil
.
load
();
// 装载音乐资源
bird
=
new
Bird
();
setGameState
(
STATE_READY
);
// 启动用于刷新窗口的线程
new
Thread
(
this
).
start
();
}
// 系统线程:屏幕内容的绘制,窗口事件的监听与处理
// 项目中存在两个线程:系统线程;自定义线程,调用repaint()。
// 两个线程会抢夺系统资源,可能会出现一次刷新周期所绘制的内容,并没有在一次刷新周期内完成
// (双缓冲)单独定义一张图片,将需要绘制的内容绘制到这张图片,再一次性地将图片绘制到窗口
private
BufferedImage
bufImg
=
new
BufferedImage
(
FRAME_WIDTH
,
FRAME_HEIGHT
,
BufferedImage
.
TYPE_4BYTE_ABGR
);
/**
* 绘制需要屏幕内容 当repaint()方法被调用时,JVM会调用update() 不要主动调用update 参数g是系统提供的画笔,由系统进行实例化
*
* 单独启动一个线程,不断地快速调用repaint(),让系统对整个窗口进行重绘
*
*/
public
void
update
(
Graphics
g
)
{
Graphics
bufG
=
bufImg
.
getGraphics
();
// 获得图片画笔
// 使用图片画笔将需要绘制的内容绘制到图片
background
.
draw
(
bufG
,
bird
);
// 背景层
foreground
.
draw
(
bufG
,
bird
);
// 前景层
if
(
gameState
==
STATE_READY
)
{
// 游戏未开始
ready
.
draw
(
bufG
);
bird
.
draw
(
bufG
);
// 鸟
}
else
{
// 游戏结束
gameElement
.
draw
(
bufG
,
bird
);
// 游戏元素层
bird
.
draw
(
bufG
);
// 鸟
}
g
.
drawImage
(
bufImg
,
0
,
0
,
null
);
// 一次性将图片绘制到屏幕上
}
@Override
public
void
run
()
{
while
(
true
)
{
repaint
();
// 通过调用repaint(),让JVM调用update()
try
{
Thread
.
sleep
(
GAME_INTERVAL
);
}
catch
(
InterruptedException
e
)
{
e
.
printStackTrace
();
}
}
}
//获取、设置游戏状态的方法
public
static
int
getGameState
()
{
return
gameState
;
}
public
static
void
setGameState
(
int
gameState
)
{
GameFrame
.
gameState
=
gameState
;
}
}
\ No newline at end of file
src/com/bird/main/GameReady.java
0 → 100644
View file @
d7b64a56
package
com.bird.main
;
import
java.awt.Graphics
;
import
java.awt.image.BufferedImage
;
import
com.bird.util.Constant
;
import
com.bird.util.GameUtil
;
/**
* 游戏未开始的内容
*
* @author Kingyu
*
*/
public
class
GameReady
{
private
BufferedImage
titleImg
;
private
BufferedImage
noticeImg
;
private
int
flash
;
public
GameReady
()
{
titleImg
=
GameUtil
.
loadBUfferedImage
(
Constant
.
TITLE_IMG_PATH
);
noticeImg
=
GameUtil
.
loadBUfferedImage
(
Constant
.
NOTICE_IMG_PATH
);
}
public
void
draw
(
Graphics
g
)
{
int
x
=
Constant
.
FRAME_WIDTH
-
titleImg
.
getWidth
()
>>
1
;
int
y
=
Constant
.
FRAME_HEIGHT
/
5
<<
1
;
g
.
drawImage
(
titleImg
,
x
,
y
,
null
);
final
int
COUNT
=
30
;
if
(
flash
++
>
COUNT
)
{
x
=
Constant
.
FRAME_WIDTH
-
noticeImg
.
getWidth
()
>>
1
;
y
=
Constant
.
FRAME_HEIGHT
/
5
*
3
;
g
.
drawImage
(
noticeImg
,
x
,
y
,
null
);
if
(
flash
==
COUNT
*
2
)
flash
=
0
;
}
}
}
src/com/bird/main/GameTime.java
0 → 100644
View file @
d7b64a56
package
com.bird.main
;
import
java.io.DataInputStream
;
import
java.io.DataOutputStream
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.FileOutputStream
;
import
com.bird.util.Constant
;
import
com.bird.util.MusicUtil
;
/**
* 游戏计时类
*
* @author Kingyu
*
*/
public
class
GameTime
{
public
static
final
int
HOVER_BARRIER_TIME
=
10
;
// 出现悬浮管道的时间
public
static
final
int
MOVING_BARRIER_TIME
=
20
;
// 出现移动管道的时间
private
int
timeState
;
// 计时器的状态
public
static
final
int
STATE_READY
=
0
;
// 计时就绪
public
static
final
int
STATE_START
=
1
;
// 计时开始
public
static
final
int
STATE_OVER
=
2
;
// 计时结束
private
long
startTime
=
0
;
// 开始时间 ms
private
long
endTime
=
0
;
// 开始时间 ms
private
long
score
=
0
;
// 分数
private
long
bestScore
;
// 最高分数
public
GameTime
()
{
timeState
=
STATE_READY
;
bestScore
=
-
1
;
try
{
loadBestTime
();
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
// 装载最高纪录
private
void
loadBestTime
()
throws
Exception
{
File
file
=
new
File
(
Constant
.
SCORE_FILE_PATH
);
if
(
file
.
exists
())
{
DataInputStream
dis
=
new
DataInputStream
(
new
FileInputStream
(
file
));
bestScore
=
dis
.
readLong
();
dis
.
close
();
}
}
// 保存最高纪录
public
void
saveBestTime
(
long
time
)
throws
Exception
{
File
file
=
new
File
(
Constant
.
SCORE_FILE_PATH
);
DataOutputStream
dos
=
new
DataOutputStream
(
new
FileOutputStream
(
file
));
dos
.
writeLong
(
time
);
dos
.
close
();
}
public
long
getBestScore
()
{
return
bestScore
;
}
public
void
setStartTime
(
long
startTime
)
{
this
.
startTime
=
startTime
;
}
public
void
setOverTime
(
long
endTime
)
{
this
.
endTime
=
endTime
;
}
/**
* 游戏用时,毫秒
*
* @return
*/
public
long
getTime
()
{
if
(
timeState
==
STATE_READY
)
{
return
startTime
;
}
else
if
(
timeState
==
STATE_START
)
{
return
(
System
.
currentTimeMillis
()
-
startTime
);
}
else
{
return
(
endTime
-
startTime
);
}
}
//游戏时间转换为秒
public
long
getTimeInSeconds
()
{
return
getTime
()
/
1000
;
}
// 计时器是否就绪
public
boolean
isReadyTiming
()
{
return
timeState
==
STATE_READY
;
}
// 开始计时
public
void
startTiming
()
{
startTime
=
System
.
currentTimeMillis
();
timeState
=
STATE_START
;
}
// 结束计时
public
void
endTiming
()
{
endTime
=
System
.
currentTimeMillis
();
timeState
=
STATE_OVER
;
// 判断本次得分是否为最高分
long
score
=
TimeToScore
();
if
(
bestScore
<
score
)
bestScore
=
score
;
try
{
saveBestTime
(
bestScore
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
private
static
final
int
FIRST_SCORE_TIME
=
6700
;
// 从游戏开始到通过第一根水管的所需时间
private
static
final
int
PER_SCORE_TIME
=
2850
;
// 通过后续每一根水管的间隔的所需时间
//将游戏时间转换为通过水管的数量
public
long
TimeToScore
()
{
long
time
=
getTime
();
long
temp
=
score
;
if
(
time
>=
FIRST_SCORE_TIME
&&
time
<
FIRST_SCORE_TIME
+
PER_SCORE_TIME
)
{
score
=
1
;
}
else
if
(
time
>=
FIRST_SCORE_TIME
+
PER_SCORE_TIME
)
{
score
=
(
int
)
(
time
-
FIRST_SCORE_TIME
)
/
PER_SCORE_TIME
+
1
;
}
if
(
score
-
temp
>
0
)
{
MusicUtil
.
playScore
();
//每次得分播放音效
}
return
score
;
}
/**
* 是否正在计时
*
* @return
*/
public
boolean
isTiming
()
{
return
timeState
==
STATE_START
;
}
//重置计时器
public
void
reset
()
{
timeState
=
STATE_READY
;
startTime
=
0
;
endTime
=
0
;
}
}
src/com/bird/main/Pipe.java
0 → 100644
View file @
d7b64a56
package
com.bird.main
;
import
java.awt.Graphics
;
import
java.awt.Rectangle
;
import
java.awt.image.BufferedImage
;
import
com.bird.util.Constant
;
import
com.bird.util.GameUtil
;
/**
* 水管类
*
* @author Kingyu
*
*/
public
class
Pipe
{
private
static
BufferedImage
[]
imgs
;
// 水管的图片,static保证图片只加载一次
static
{
// 静态代码块,类加载的时候,初始化图片
final
int
PIPE_IMAGE_COUNT
=
3
;
imgs
=
new
BufferedImage
[
PIPE_IMAGE_COUNT
];
for
(
int
i
=
0
;
i
<
PIPE_IMAGE_COUNT
;
i
++)
{
imgs
[
i
]
=
GameUtil
.
loadBUfferedImage
(
Constant
.
PIPE_IMG_PATH
[
i
]);
}
}
// 所有水管的宽高
public
static
final
int
PIPE_WIDTH
=
imgs
[
0
].
getWidth
();
public
static
final
int
PIPE_HEIGHT
=
imgs
[
0
].
getHeight
();
public
static
final
int
PIPE_HEAD_WIDTH
=
imgs
[
1
].
getWidth
();
public
static
final
int
PIPE_HEAD_HEIGHT
=
imgs
[
1
].
getHeight
();
private
int
x
,
y
;
// 水管的坐标,相对于元素层
private
int
width
,
height
;
// 水管的宽,高
private
boolean
visible
;
// 水管可见状态,true为可见,false表示可归还至对象池
// 水管的类型
private
int
type
;
public
static
final
int
TYPE_TOP_NORMAL
=
0
;
public
static
final
int
TYPE_TOP_HARD
=
1
;
public
static
final
int
TYPE_BOTTOM_NORMAL
=
2
;
public
static
final
int
TYPE_BOTTOM_HARD
=
3
;
public
static
final
int
TYPE_HOVER_NORMAL
=
4
;
public
static
final
int
TYPE_HOVER_HARD
=
5
;
// 水管的速度
public
static
final
int
MIN_SPEED
=
1
;
public
static
final
int
MAX_SPEED
=
2
;
private
int
speed
;
private
Rectangle
pipeRect
;
// 水管的碰撞矩形
// 构造器
public
Pipe
()
{
this
.
speed
=
MIN_SPEED
;
this
.
width
=
PIPE_WIDTH
;
pipeRect
=
new
Rectangle
();
pipeRect
.
width
=
PIPE_WIDTH
;
}
/**
* 设置水管参数
*
* @param x
* @param y
* @param height
* @param type
* @param visible
*/
public
void
setAttribute
(
int
x
,
int
y
,
int
height
,
int
type
,
boolean
visible
)
{
this
.
x
=
x
;
this
.
y
=
y
;
this
.
height
=
height
;
this
.
type
=
type
;
this
.
visible
=
visible
;
setRectangle
(
this
.
x
,
this
.
y
,
this
.
height
);
}
/**
* 设置碰撞矩形参数
*
* @param x
* @param y
* @param height
*/
public
void
setRectangle
(
int
x
,
int
y
,
int
height
)
{
pipeRect
.
x
=
x
;
pipeRect
.
y
=
y
;
pipeRect
.
height
=
height
;
}
// 判断水管是否位于窗口
public
boolean
isVisible
()
{
return
visible
;
}
// 绘制方法
public
void
draw
(
Graphics
g
,
Bird
bird
)
{
switch
(
type
)
{
case
TYPE_TOP_NORMAL:
drawTopNormal
(
g
);
break
;
case
TYPE_BOTTOM_NORMAL:
drawBottomNormal
(
g
);
break
;
case
TYPE_HOVER_NORMAL:
drawHoverNormal
(
g
);
break
;
}
// //绘制碰撞矩形
// g.setColor(Color.black);
// g.drawRect((int) pipeRect.getX(), (int) pipeRect.getY(), (int) pipeRect.getWidth(), (int) pipeRect.getHeight());
if
(
bird
.
isDead
())
{
return
;
}
pipeLogic
();
}
// 绘制从上往下的普通水管
private
void
drawTopNormal
(
Graphics
g
)
{
// 拼接的个数
int
count
=
(
height
-
PIPE_HEAD_HEIGHT
)
/
PIPE_HEIGHT
+
1
;
// 取整+1
// 绘制水管的主体
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
g
.
drawImage
(
imgs
[
0
],
x
,
y
+
i
*
PIPE_HEIGHT
,
null
);
}
// 绘制水管的顶部
g
.
drawImage
(
imgs
[
1
],
x
-
((
PIPE_HEAD_WIDTH
-
width
)
>>
1
),
height
-
Constant
.
TOP_PIPE_LENGTHENING
-
PIPE_HEAD_HEIGHT
,
null
);
// 水管头部与水管主体的宽度不同,x坐标需要处理
}
// 绘制从下往上的普通水管
private
void
drawBottomNormal
(
Graphics
g
)
{
// 拼接的个数
int
count
=
(
height
-
PIPE_HEAD_HEIGHT
-
Constant
.
GROUND_HEIGHT
)
/
PIPE_HEIGHT
+
1
;
// 绘制水管的主体
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
g
.
drawImage
(
imgs
[
0
],
x
,
Constant
.
FRAME_HEIGHT
-
PIPE_HEIGHT
-
Constant
.
GROUND_HEIGHT
-
i
*
PIPE_HEIGHT
,
null
);
}
// 绘制水管的顶部
g
.
drawImage
(
imgs
[
2
],
x
-
((
PIPE_HEAD_WIDTH
-
width
)
>>
1
),
Constant
.
FRAME_HEIGHT
-
height
,
null
);
}
// 绘制悬浮的普通水管
private
void
drawHoverNormal
(
Graphics
g
)
{
// 拼接的个数
int
count
=
(
height
-
2
*
PIPE_HEAD_HEIGHT
)
/
PIPE_HEIGHT
+
1
;
// 绘制水管的上顶部
g
.
drawImage
(
imgs
[
2
],
x
-
((
PIPE_HEAD_WIDTH
-
width
)
>>
1
),
y
,
null
);
// 绘制水管的主体
for
(
int
i
=
0
;
i
<
count
;
i
++)
{
g
.
drawImage
(
imgs
[
0
],
x
,
y
+
i
*
PIPE_HEIGHT
+
PIPE_HEAD_HEIGHT
,
null
);
}
// 绘制水管的下底部
int
y
=
this
.
y
+
height
-
PIPE_HEAD_HEIGHT
;
g
.
drawImage
(
imgs
[
1
],
x
-
((
PIPE_HEAD_WIDTH
-
width
)
>>
1
),
y
,
null
);
}
/**
* 水管的运动逻辑
*/
private
void
pipeLogic
()
{
x
-=
speed
;
pipeRect
.
x
-=
speed
;
if
(
x
<
-
1
*
PIPE_HEAD_WIDTH
)
{
// 水管完全离开了窗口
visible
=
false
;
}
}
/**
* 判断当前水管是否完全出现在窗口中
*
* @return 若完全出现则返回true,否则返回false
*/
public
boolean
isInFrame
()
{
return
x
+
width
<
Constant
.
FRAME_WIDTH
;
}
// 各参数的写入与获取
public
void
setX
(
int
x
)
{
this
.
x
=
x
;
}
public
void
setY
(
int
y
)
{
this
.
y
=
y
;
}
public
void
setType
(
int
type
)
{
this
.
type
=
type
;
}
public
void
setHeight
(
int
height
)
{
this
.
height
=
height
;
}
public
void
setVisible
(
boolean
visible
)
{
this
.
visible
=
visible
;
}
public
int
getX
()
{
return
x
;
}
// 获取水管的碰撞矩形
public
Rectangle
getPipeRect
()
{
return
pipeRect
;
}
}
\ No newline at end of file
src/com/bird/main/PipePool.java
0 → 100644
View file @
d7b64a56
package
com.bird.main
;
import
java.util.ArrayList
;
import
java.util.List
;
import
com.bird.util.Constant
;
/**
* 为了避免反复地创建和销毁对象,使用对象池来提前创建好一些对象,使用时从对象池中获得,使用完毕归还
*
* @author Kingyu
*
*/
public
class
PipePool
{
private
static
List
<
Pipe
>
pool
=
new
ArrayList
<>();
// 用于管理池中所有对象的容器
public
static
final
int
INIT_PIPE_COUNT
=
(
Constant
.
FRAME_WIDTH
/
(
Pipe
.
PIPE_HEAD_WIDTH
+
GameElementLayer
.
HORIZONTAL_INTERVAL
)
+
2
)
*
2
;
// 根据窗口宽度算得对象池中对象的初始个数
public
static
final
int
MAX_PIPE_COUNT
=
50
;
// 对象池中对象的最大个数,自行定义
//初始化水管容器
static
{
for
(
int
i
=
0
;
i
<
INIT_PIPE_COUNT
;
i
++)
{
pool
.
add
(
new
Pipe
());
}
}
/**
* 从对象池中获取一个对象
*
* @return
*/
public
static
Pipe
get
()
{
int
size
=
pool
.
size
();
if
(
size
>
0
)
{
return
pool
.
remove
(
size
-
1
);
// 移除并返回最后一个
}
else
{
return
new
Pipe
();
// 空对象池,返回一个新对象
}
}
/**
* 归还对象给容器
*
* @param pipe
*/
public
static
void
giveBack
(
Pipe
pipe
)
{
if
(
pool
.
size
()
<
MAX_PIPE_COUNT
)
{
pool
.
add
(
pipe
);
}
}
}
src/com/bird/util/Constant.java
0 → 100644
View file @
d7b64a56
package
com.bird.util
;
import
java.awt.Color
;
import
java.awt.Font
;
/**
* 常量类
*
* @author Kingyu 后续优化可写入数据库或文件中,便于修改
*/
public
class
Constant
{
// 窗口尺寸
public
static
final
int
FRAME_WIDTH
=
420
;
public
static
final
int
FRAME_HEIGHT
=
640
;
// 游戏标题
public
static
final
String
GAME_TITLE
=
"Flappy Bird"
;
// 窗口位置
public
static
final
int
FRAME_X
=
1200
;
public
static
final
int
FRAME_Y
=
100
;
// 图像资源路径
public
static
final
String
BG_IMG_PATH
=
"sources/img/background.png"
;
// 背景图片
// 小鸟图片
public
static
final
String
[][]
BIRDS_IMG_PATH
=
{
{
"sources/img/0.png"
,
"sources/img/1.png"
,
"sources/img/2.png"
,
"sources/img/3.png"
,
"sources/img/4.png"
,
"sources/img/5.png"
,
"sources/img/6.png"
,
"sources/img/7.png"
},
{
"sources/img/up.png"
,
"sources/img/up.png"
,
"sources/img/up.png"
,
"sources/img/up.png"
,
"sources/img/up.png"
,
"sources/img/up.png"
,
"sources/img/up.png"
,
"sources/img/up.png"
},
{
"sources/img/down_0.png"
,
"sources/img/down_1.png"
,
"sources/img/down_2.png"
,
"sources/img/down_3.png"
,
"sources/img/down_4.png"
,
"sources/img/down_5.png"
,
"sources/img/down_6.png"
,
"sources/img/down_7.png"
},
{
"sources/img/dead.png"
,
"sources/img/dead.png"
,
"sources/img/dead.png"
,
"sources/img/dead.png"
,
"sources/img/dead.png"
,
"sources/img/dead.png"
,
"sources/img/dead.png"
,
"sources/img/dead.png"
,
}
};
// 云朵图片
public
static
final
String
[]
CLOUDS_IMG_PATH
=
{
"sources/img/cloud_0.png"
,
"sources/img/cloud_1.png"
};
// 水管图片
public
static
final
String
[]
PIPE_IMG_PATH
=
{
"sources/img/pipe.png"
,
"sources/img/pipe_top.png"
,
"sources/img/pipe_bottom.png"
};
public
static
final
String
TITLE_IMG_PATH
=
"sources/img/title.png"
;
public
static
final
String
NOTICE_IMG_PATH
=
"sources/img/start.png"
;
public
static
final
String
SCORE_IMG_PATH
=
"sources/img/score.png"
;
public
static
final
String
OVER_IMG_PATH
=
"sources/img/over.png"
;
public
static
final
String
AGAIN_IMG_PATH
=
"sources/img/again.png"
;
public
static
final
String
SCORE_FILE_PATH
=
"sources/score"
;
// 分数文件路径
// 游戏背景色
public
static
final
Color
BG_COLOR
=
new
Color
(
0x4bc4cf
);
// 游戏刷新率
public
static
final
int
GAME_INTERVAL
=
1000
/
60
;
// 标题栏高度
public
static
final
int
TOP_BAR_HEIGHT
=
20
;
// 地面高度
public
static
final
int
GROUND_HEIGHT
=
35
;
// 上方管道加长
public
static
final
int
TOP_PIPE_LENGTHENING
=
100
;
public
static
final
int
CLOUD_BORN_PERCENT
=
6
;
// 云朵生成的概率,单位为百分比
public
static
final
int
CLOUD_IMAGE_COUNT
=
2
;
// 云朵图片的个数
public
static
final
int
MAX_CLOUD_COUNT
=
7
;
// 云朵的最大数量
public
static
final
int
CLOUD_DIRCHANGE
=
50
;
// 云朵随机改变方向的概率,越大表示概率越小
public
static
final
Font
TIME_FONT
=
new
Font
(
"华文琥珀"
,
Font
.
BOLD
,
32
);
// 字体
public
static
final
Font
SCORE_FONT
=
new
Font
(
"华文琥珀"
,
Font
.
BOLD
,
24
);
// 字体
}
\ No newline at end of file
src/com/bird/util/GameUtil.java
0 → 100644
View file @
d7b64a56
package
com.bird.util
;
import
java.awt.Font
;
import
java.awt.FontMetrics
;
import
java.awt.image.BufferedImage
;
import
java.io.FileInputStream
;
import
java.io.IOException
;
import
javax.imageio.ImageIO
;
import
sun.font.FontDesignMetrics
;
/**
* 工具类,游戏中用到的工具都在此类
*
* @author Kingyu
*
*/
public
class
GameUtil
{
private
GameUtil
()
{
}
// 私有化,不让其他类实例化该类
/**
* 装载图片的方法
*
* @param imgPath 图片路径
* @return
*/
public
static
BufferedImage
loadBUfferedImage
(
String
imgPath
)
{
try
{
return
ImageIO
.
read
(
new
FileInputStream
(
imgPath
));
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
return
null
;
}
/**
* 判断任意概率的概率性事件是否发生
*
* @param numerator 分子,不小于0的值
* @param denominator 分母,不小于0的值
* @return 概率性事件发生返回true,否则返回false
*/
public
static
boolean
isInProbability
(
int
numerator
,
int
denominator
)
throws
Exception
{
// 分子分母不小于0
if
(
numerator
<=
0
||
denominator
<=
0
)
{
throw
new
Exception
(
"传入了非法的参数"
);
}
//分子大于分母,一定发生
if
(
numerator
>=
denominator
)
{
return
true
;
}
return
getRandomNumber
(
1
,
denominator
+
1
)
<=
numerator
;
}
/**
* 返回指定区间的一个随机数
*
* @param min 区间最小值,包含
* @param max 区间最大值,不包含
* @return 该区间的随机数
*/
public
static
int
getRandomNumber
(
int
min
,
int
max
)
{
return
(
int
)
(
Math
.
random
()
*
(
max
-
min
)
+
min
);
}
/**
* 获得指定字符串在指定字体的宽高
*/
public
static
int
getStringWidth
(
Font
font
,
String
str
)
{
FontMetrics
fm
=
FontDesignMetrics
.
getMetrics
(
font
);
return
fm
.
stringWidth
(
str
);
}
public
static
int
getStringHeight
(
Font
font
,
String
str
)
{
FontMetrics
fm
=
FontDesignMetrics
.
getMetrics
(
font
);
return
fm
.
getHeight
();
}
}
src/com/bird/util/MusicUtil.java
0 → 100644
View file @
d7b64a56
package
com.bird.util
;
import
java.applet.Applet
;
import
java.applet.AudioClip
;
import
java.io.File
;
import
java.net.MalformedURLException
;
/**
* 音乐工具类
*
* @author Kingyu wav音频:JDK提供的类可直接解码 mp3音频:JDK没有提供支持,需要使用第三方的工具包
*
*/
public
class
MusicUtil
{
private
static
AudioClip
fly
;
private
static
AudioClip
crash
;
private
static
AudioClip
score
;
// 装载音乐资源
public
static
void
load
()
{
try
{
fly
=
Applet
.
newAudioClip
(
new
File
(
"sources/wav/fly.wav"
).
toURL
());
crash
=
Applet
.
newAudioClip
(
new
File
(
"sources/wav/crash.wav"
).
toURL
());
score
=
Applet
.
newAudioClip
(
new
File
(
"sources/wav/score.wav"
).
toURL
());
}
catch
(
MalformedURLException
e
)
{
e
.
printStackTrace
();
}
}
//wav播放
public
static
void
playFly
()
{
fly
.
play
();
}
public
static
void
playCrash
()
{
crash
.
play
();
}
public
static
void
playScore
()
{
score
.
play
();
}
}
Prev
1
2
3
Next
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment