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
2968bb8e
Commit
2968bb8e
authored
Jul 15, 2020
by
kingyu
Committed by
kingyuluk
Jul 15, 2020
Browse files
feat: 优化记分方法
BREAKING CHANGE: 移除了计时器,更改了游戏的记分方法,现在记分更准确了
parent
e2cb276d
Changes
20
Show whitespace changes
Inline
Side-by-side
CHANGELOG.md
View file @
2968bb8e
...
...
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. See
[
standard-version
](
https://github.com/conventional-changelog/standard-version
)
for commit guidelines.
### [1.2.2](https://github.com/kingyuluk/FlappyBird/compare/v1.2.1...v1.2.2) (2020-07-12)
### ⚠ BREAKING CHANGES
*
移除了计时器,更改了游戏的记分方法,现在记分更准确了
### Features
*
优化记分方法 (
[
9aa9275
](
https://github.com/kingyuluk/FlappyBird/commit/9aa927537dc9180942c4a982ea46edd840d7c769
)
)
### [1.2.1](https://github.com/kingyuluk/FlappyBird/compare/v1.2.0...v1.2.1) (2020-07-12)
...
...
FlappyBird.jar
0 → 100644
View file @
2968bb8e
File added
README.md
View file @
2968bb8e
...
...
@@ -8,21 +8,26 @@ Flappy Bird for desktop platforms.
## Overview
本项目为Flappy bird的桌面平台版,具备原版的所有功能,且相较于原版优化了游戏难度并加入移动型水管,让游戏更具可玩性。
### 游戏玩法
游戏只需空格键即可操作,每局游戏随机刷新所有元素,小鸟受到重力作用会不断下坠,敲击空格键小鸟就会振翅向上飞,游戏过程中需要玩家控制小鸟不断飞行,并注意躲避随机生成的水管,每飞过一对水管就会得分,飞行过程中如果撞到水管或掉落在地则游戏结束。
本项目为Flappy bird的桌面平台版,具备原版的所有功能,且相较于原版优化了游戏难度并加入移动型水管,增加可玩性。
## How to play
直接运行FlappyBird.jar即可开始游戏。
游戏使用空格键操作。
每局游戏随机刷新所有元素,小鸟受到重力作用会不断下坠,敲击空格键使小鸟振翅向上飞,游戏过程中需要玩家控制小鸟不断飞行,并注意躲避随机生成的水管,每飞过一对水管就会得分,飞行过程中如果撞到水管或掉落在地则游戏结束。
## 游戏界面
### 游戏启动


### 运行示例


### 游戏结束


## Package Contents
...
...
@@ -34,6 +39,9 @@ Flappy Bird for desktop platforms.
## Change Log
v1.2.2 - July 12, 2020
*
移除了计时器,优化了游戏的记分方式,现在记分更准确了
v1.2.1 - July 12, 2020
*
使用AudioClip类的方法播放连续的短音频可能会导致线程冲突使游戏卡顿
...
...
@@ -55,4 +63,8 @@ v1.0.0 - July 10, 2020
*
sun包在不同操作系统和不同版本的JDK中可能发生变化,因此无法确保工作在所有JAVA平台上
## Contact
*
email:
<kingyuluk@mail.dlut.edu.cn>
*
email:
<kingyuluk@hotmail.com>
## License
*
图片与音效资源皆来源于网络,仅供学习交流
examples
/over.png
→
resources/readme_img
/over.png
View file @
2968bb8e
File moved
examples
/play.gif
→
resources/readme_img
/play.gif
View file @
2968bb8e
File moved
examples
/start.png
→
resources/readme_img
/start.png
View file @
2968bb8e
File moved
src/com/bird/main/Bird.java
View file @
2968bb8e
...
...
@@ -9,17 +9,19 @@ import com.bird.util.Constant;
import
com.bird.util.GameUtil
;
import
com.bird.util.MusicUtil
;
import
static
com
.
bird
.
util
.
GameUtil
.
drawTitle
;
/**
* 小鸟类,小鸟的绘制与飞行逻辑都在此类
*
* @author Kingyu
*
*/
public
class
Bird
{
public
static
final
int
IMG_COUNT
=
8
;
// 图片数量
public
static
final
int
STATE_COUNT
=
4
;
// 状态数
private
BufferedImage
[][]
birdImgs
;
// 小鸟的图片数组对象
private
int
x
,
y
;
// 小鸟的坐标
private
final
BufferedImage
[][]
birdImages
;
// 小鸟的图片数组对象
private
final
int
x
;
private
int
y
;
// 小鸟的坐标
int
wingState
;
// 翅膀状态
...
...
@@ -37,20 +39,20 @@ public class Bird {
public
static
final
int
STATE_FALL
=
3
;
public
static
final
int
STATE_DEAD
=
4
;
private
Rectangle
birdRect
;
// 碰撞矩形
private
final
Rectangle
birdRect
;
// 碰撞矩形
public
static
final
int
RECT_DESCALE
=
2
;
// 补偿碰撞矩形宽高的参数
private
GameTime
timing
;
//
飞行时间
private
final
GameScore
countScore
;
//
计分器
// 在构造器中对资源初始化
public
Bird
()
{
timing
=
Game
Tim
e
.
getInstance
();
// 计
时
器
countScore
=
Game
Scor
e
.
getInstance
();
// 计
分
器
// 读取小鸟图片资源
birdIm
g
s
=
new
BufferedImage
[
STATE_COUNT
][
IMG_COUNT
];
birdIm
age
s
=
new
BufferedImage
[
STATE_COUNT
][
IMG_COUNT
];
for
(
int
j
=
0
;
j
<
STATE_COUNT
;
j
++)
{
for
(
int
i
=
0
;
i
<
IMG_COUNT
;
i
++)
{
birdIm
g
s
[
j
][
i
]
=
GameUtil
.
loadB
U
fferedImage
(
Constant
.
BIRDS_IMG_PATH
[
j
][
i
]);
birdIm
age
s
[
j
][
i
]
=
GameUtil
.
loadB
u
fferedImage
(
Constant
.
BIRDS_IMG_PATH
[
j
][
i
]);
}
}
...
...
@@ -58,33 +60,30 @@ public class Bird {
x
=
Constant
.
FRAME_WIDTH
>>
2
;
y
=
Constant
.
FRAME_HEIGHT
>>
1
;
int
ImgWidth
=
birdIm
g
s
[
state
][
0
].
getWidth
();
int
ImgHeight
=
birdIm
g
s
[
state
][
0
].
getHeight
();
int
ImgWidth
=
birdIm
age
s
[
state
][
0
].
getWidth
();
int
ImgHeight
=
birdIm
age
s
[
state
][
0
].
getHeight
();
// 初始化碰撞矩形
int
rectX
=
x
-
ImgWidth
/
2
;
int
rectY
=
y
-
ImgHeight
/
2
;
int
rectWidth
=
ImgWidth
;
int
rectHeight
=
ImgHeight
;
birdRect
=
new
Rectangle
(
rectX
+
RECT_DESCALE
,
rectY
+
RECT_DESCALE
*
2
,
rectWidth
-
RECT_DESCALE
*
3
,
rectHeight
-
RECT_DESCALE
*
4
);
// 碰撞矩形的坐标与小鸟相同
birdRect
=
new
Rectangle
(
rectX
+
RECT_DESCALE
,
rectY
+
RECT_DESCALE
*
2
,
ImgWidth
-
RECT_DESCALE
*
3
,
ImgHeight
-
RECT_DESCALE
*
4
);
// 碰撞矩形的坐标与小鸟相同
}
// 绘制方法
public
void
draw
(
Graphics
g
)
{
fly
();
int
state_index
=
state
>
STATE_FALL
?
STATE_FALL
:
state
;
// 图片资源索引
int
state_index
=
Math
.
min
(
state
,
STATE_FALL
)
;
// 图片资源索引
// 小鸟中心点计算
int
halfImgWidth
=
birdIm
g
s
[
state_index
][
0
].
getWidth
()
>>
1
;
int
halfImgHeight
=
birdIm
g
s
[
state_index
][
0
].
getHeight
()
>>
1
;
int
halfImgWidth
=
birdIm
age
s
[
state_index
][
0
].
getWidth
()
>>
1
;
int
halfImgHeight
=
birdIm
age
s
[
state_index
][
0
].
getHeight
()
>>
1
;
if
(
speed
>
0
)
image
=
birdIm
g
s
[
STATE_UP
][
0
];
image
=
birdIm
age
s
[
STATE_UP
][
0
];
g
.
drawImage
(
image
,
x
-
halfImgWidth
,
y
-
halfImgHeight
,
null
);
// x坐标于窗口1/4处,y坐标位窗口中心
if
(
state
==
STATE_DEAD
)
{
drawGameover
(
g
);
}
else
if
(
state
==
STATE_FALL
)
{
}
else
{
drawGameOver
(
g
);
}
else
if
(
state
!=
STATE_FALL
)
{
drawScore
(
g
);
}
// 绘制矩形
...
...
@@ -96,7 +95,6 @@ public class Bird {
public
static
final
double
g
=
9.8
;
// 重力加速度
public
static
final
double
T
=
0.2
;
// 小鸟的下落函数执行的时间
private
double
h
;
// 小鸟y轴的位移量
private
double
speed
=
0
;
// 小鸟的初速度
private
boolean
keyFlag
=
true
;
// 按键状态,true为已释放,使当按住按键时不会重复调用方法
...
...
@@ -117,25 +115,20 @@ public class Bird {
private
void
fly
()
{
// 翅膀状态,实现小鸟振翅飞行
wingState
++;
image
=
birdIm
gs
[
state
>
STATE_FALL
?
STATE_FALL
:
state
][
wingState
/
10
%
IMG_COUNT
];
image
=
birdIm
ages
[
Math
.
min
(
state
,
STATE_FALL
)
][
wingState
/
10
%
IMG_COUNT
];
switch
(
state
)
{
case
STATE_NORMAL:
break
;
case
STATE_UP:
break
;
case
STATE_DOWN:
// 物理公式
speed
-=
g
*
T
;
h
=
speed
*
T
-
g
*
T
*
T
/
2
;
// 小鸟y轴的位移量
double
h
=
speed
*
T
-
g
*
T
*
T
/
2
;
y
=
(
int
)
(
y
-
h
);
birdRect
.
y
=
(
int
)
(
birdRect
.
y
-
h
);
// 控制坠落的边界,若y坐标 > 窗口的高度 - 地面的高度 - 小鸟图片的高度则死亡
if
(
birdRect
.
y
>=
Constant
.
FRAME_HEIGHT
-
Constant
.
GROUND_HEIGHT
-
(
birdIm
g
s
[
state
][
0
].
getHeight
()
>>
1
))
{
y
=
Constant
.
FRAME_HEIGHT
-
Constant
.
GROUND_HEIGHT
-
(
birdIm
g
s
[
state
][
0
].
getHeight
()
>>
1
);
birdRect
.
y
=
Constant
.
FRAME_HEIGHT
-
Constant
.
GROUND_HEIGHT
-
(
birdIm
g
s
[
state
][
0
].
getHeight
()
>>
1
);
if
(
birdRect
.
y
>=
Constant
.
FRAME_HEIGHT
-
Constant
.
GROUND_HEIGHT
-
(
birdIm
age
s
[
state
][
0
].
getHeight
()
>>
1
))
{
y
=
Constant
.
FRAME_HEIGHT
-
Constant
.
GROUND_HEIGHT
-
(
birdIm
age
s
[
state
][
0
].
getHeight
()
>>
1
);
birdRect
.
y
=
Constant
.
FRAME_HEIGHT
-
Constant
.
GROUND_HEIGHT
-
(
birdIm
age
s
[
state
][
0
].
getHeight
()
>>
1
);
birdFall
();
}
...
...
@@ -149,16 +142,18 @@ public class Bird {
birdRect
.
y
=
(
int
)
(
birdRect
.
y
-
h
);
// 控制坠落的边界,若y坐标 > 窗口的高度 - 地面的高度 - 小鸟图片的高度则死亡
if
(
birdRect
.
y
>=
Constant
.
FRAME_HEIGHT
-
Constant
.
GROUND_HEIGHT
-
(
birdIm
g
s
[
state
][
0
].
getHeight
()
>>
1
))
{
if
(
birdRect
.
y
>=
Constant
.
FRAME_HEIGHT
-
Constant
.
GROUND_HEIGHT
-
(
birdIm
age
s
[
state
][
0
].
getHeight
()
>>
1
))
{
y
=
Constant
.
FRAME_HEIGHT
-
Constant
.
GROUND_HEIGHT
-
(
birdIm
g
s
[
state
][
0
].
getHeight
()
>>
1
);
birdRect
.
y
=
Constant
.
FRAME_HEIGHT
-
Constant
.
GROUND_HEIGHT
-
(
birdIm
g
s
[
state
][
0
].
getHeight
()
>>
1
);
y
=
Constant
.
FRAME_HEIGHT
-
Constant
.
GROUND_HEIGHT
-
(
birdIm
age
s
[
state
][
0
].
getHeight
()
>>
1
);
birdRect
.
y
=
Constant
.
FRAME_HEIGHT
-
Constant
.
GROUND_HEIGHT
-
(
birdIm
age
s
[
state
][
0
].
getHeight
()
>>
1
);
GameFrame
.
setGameState
(
GameFrame
.
STATE_OVER
);
// 改变游戏状态
birdDead
();
}
break
;
case
STATE_NORMAL:
case
STATE_UP:
case
STATE_DEAD:
break
;
}
...
...
@@ -199,8 +194,6 @@ public class Bird {
public
void
birdFall
()
{
state
=
STATE_FALL
;
MusicUtil
.
playCrash
();
// 播放音效
// 结束计时
timing
.
endTiming
();
}
// 小鸟死亡
...
...
@@ -208,28 +201,23 @@ public class Bird {
state
=
STATE_DEAD
;
// 加载游戏结束的资源
if
(
overImg
==
null
)
{
overImg
=
GameUtil
.
loadB
U
fferedImage
(
Constant
.
OVER_IMG_PATH
);
scoreImg
=
GameUtil
.
loadB
U
fferedImage
(
Constant
.
SCORE_IMG_PATH
);
againImg
=
GameUtil
.
loadB
U
fferedImage
(
Constant
.
AGAIN_IMG_PATH
);
overImg
=
GameUtil
.
loadB
u
fferedImage
(
Constant
.
OVER_IMG_PATH
);
scoreImg
=
GameUtil
.
loadB
u
fferedImage
(
Constant
.
SCORE_IMG_PATH
);
againImg
=
GameUtil
.
loadB
u
fferedImage
(
Constant
.
AGAIN_IMG_PATH
);
}
countScore
.
isSaveScore
();
// 判断是否保存纪录
}
public
boolean
isDead
()
{
return
state
==
STATE_FALL
||
state
==
STATE_DEAD
;
}
// 开始计时的方法
public
void
startTiming
()
{
if
(
timing
.
isReadyTiming
())
timing
.
startTiming
();
}
// 绘制实时分数
private
void
drawScore
(
Graphics
g
)
{
g
.
setColor
(
Color
.
white
);
g
.
setFont
(
Constant
.
TIM
E_FONT
);
String
str
=
Long
.
toString
(
timing
.
TimeTo
Score
());
int
x
=
Constant
.
FRAME_WIDTH
-
GameUtil
.
getStringWidth
(
Constant
.
TIM
E_FONT
,
str
)
>>
1
;
g
.
setFont
(
Constant
.
CURRENT_SCOR
E_FONT
);
String
str
=
Long
.
toString
(
countScore
.
get
Score
());
int
x
=
Constant
.
FRAME_WIDTH
-
GameUtil
.
getStringWidth
(
Constant
.
CURRENT_SCOR
E_FONT
,
str
)
>>
1
;
g
.
drawString
(
str
,
x
,
Constant
.
FRAME_HEIGHT
/
10
);
}
...
...
@@ -238,7 +226,7 @@ public class Bird {
private
int
flash
=
0
;
// 图片闪烁参数
// 绘制游戏结束的显示
private
void
drawGame
o
ver
(
Graphics
g
)
{
private
void
drawGame
O
ver
(
Graphics
g
)
{
// 绘制结束标志
int
x
=
Constant
.
FRAME_WIDTH
-
overImg
.
getWidth
()
>>
1
;
int
y
=
Constant
.
FRAME_HEIGHT
/
4
;
...
...
@@ -254,28 +242,26 @@ public class Bird {
g
.
setFont
(
Constant
.
SCORE_FONT
);
x
=
(
Constant
.
FRAME_WIDTH
-
scoreImg
.
getWidth
()
/
2
>>
1
)
+
SCORE_LOCATE
;
// 位置补偿
y
+=
scoreImg
.
getHeight
()
>>
1
;
String
str
=
Long
.
toString
(
timing
.
TimeTo
Score
());
String
str
=
Long
.
toString
(
countScore
.
get
Score
());
x
-=
GameUtil
.
getStringWidth
(
Constant
.
SCORE_FONT
,
str
)
>>
1
;
y
+=
GameUtil
.
getStringHeight
(
Constant
.
SCORE_FONT
,
str
);
g
.
drawString
(
str
,
x
,
y
);
// 绘制最高分数
if
(
timing
.
getBestScore
()
>
0
)
{
str
=
Long
.
toString
(
timing
.
getBestScore
());
if
(
countScore
.
getBestScore
()
>
0
)
{
str
=
Long
.
toString
(
countScore
.
getBestScore
());
x
=
(
Constant
.
FRAME_WIDTH
+
scoreImg
.
getWidth
()
/
2
>>
1
)
-
SCORE_LOCATE
;
// 位置补偿
x
-=
GameUtil
.
getStringWidth
(
Constant
.
SCORE_FONT
,
str
)
>>
1
;
g
.
drawString
(
str
,
x
,
y
);
}
// 绘制继续游戏
final
int
COUNT
=
30
;
// 控制闪烁间隔的参数
if
(
flash
++
>
COUNT
)
{
x
=
Constant
.
FRAME_WIDTH
-
againImg
.
getWidth
()
>>
1
;
y
=
Constant
.
FRAME_HEIGHT
/
5
*
3
;
g
.
drawImage
(
againImg
,
x
,
y
,
null
);
if
(
flash
==
COUNT
*
2
)
// 绘制继续游戏,使图像闪烁
final
int
COUNT
=
30
;
// 闪烁周期
if
(
flash
++
>
COUNT
)
drawTitle
(
againImg
,
g
);
if
(
flash
==
COUNT
*
2
)
// 重置闪烁参数
flash
=
0
;
}
}
// 重置小鸟
public
void
reset
()
{
...
...
@@ -283,10 +269,10 @@ public class Bird {
y
=
Constant
.
FRAME_HEIGHT
>>
1
;
// 小鸟坐标
speed
=
0
;
// 小鸟速度
int
ImgHeight
=
birdIm
g
s
[
state
][
0
].
getHeight
();
int
ImgHeight
=
birdIm
age
s
[
state
][
0
].
getHeight
();
birdRect
.
y
=
y
-
ImgHeight
/
2
+
RECT_DESCALE
*
2
;
// 小鸟碰撞矩形坐标
timing
.
reset
();
//
计时
器
countScore
.
reset
();
//
重置计分
器
flash
=
0
;
}
...
...
src/com/bird/main/Cloud.java
View file @
2968bb8e
...
...
@@ -3,41 +3,31 @@ package com.bird.main;
import
java.awt.Graphics
;
import
java.awt.image.BufferedImage
;
import
com.bird.util.Constant
;
/**
* 云朵类
*
* @author Kingyu
*
*/
public
class
Cloud
{
private
int
dir
;
// 方向
public
static
final
int
DIR_NONE
=
0
;
public
static
final
int
DIR_LEFT
=
1
;
public
static
final
int
DIR_RIGHT
=
2
;
private
int
speed
;
// 速度
private
int
x
,
y
;
// 坐标
private
final
int
speed
;
// 速度
private
int
x
;
// 坐标
private
final
int
y
;
private
BufferedImage
img
;
private
final
BufferedImage
img
;
// 云朵图片缩放的比例 1.0~2.0
private
double
scale
;
private
int
scaleImageWidth
;
private
int
scaleImageHeight
;
private
final
int
scaleImageWidth
;
private
final
int
scaleImageHeight
;
// 构造器
public
Cloud
(
BufferedImage
img
,
int
dir
,
int
x
,
int
y
)
{
public
Cloud
(
BufferedImage
img
,
int
x
,
int
y
)
{
super
();
this
.
img
=
img
;
this
.
dir
=
dir
;
this
.
x
=
x
;
this
.
y
=
y
;
this
.
speed
=
2
;
//云朵的速度
scale
=
1
+
Math
.
random
();
// Math.random()返回0.0~1.0的随机值
// 云朵图片缩放的比例 1.0~2.0
double
scale
=
1
+
Math
.
random
();
// Math.random()返回0.0~1.0的随机值
// 缩放云朵图片
scaleImageWidth
=
(
int
)
(
scale
*
img
.
getWidth
());
scaleImageHeight
=
(
int
)
(
scale
*
img
.
getWidth
());
...
...
@@ -46,11 +36,9 @@ public class Cloud {
// 绘制方法
public
void
draw
(
Graphics
g
,
Bird
bird
)
{
int
speed
=
this
.
speed
;
if
(
dir
==
DIR_NONE
)
//云彩不动
speed
=
0
;
x
=
(
dir
==
DIR_LEFT
)
?
x
-
speed
:
x
+
speed
;
// 方向逻辑
if
(
bird
.
isDead
())
speed
=
1
;
x
-=
speed
;
g
.
drawImage
(
img
,
x
,
y
,
scaleImageWidth
,
scaleImageHeight
,
null
);
}
...
...
@@ -60,21 +48,7 @@ public class Cloud {
* @return 飞出则返回true,否则返回false
*/
public
boolean
isOutFrame
()
{
boolean
result
=
false
;
if
(
dir
==
DIR_LEFT
)
{
if
(
x
<
-
1
*
scaleImageWidth
)
{
return
true
;
}
}
else
if
(
dir
==
DIR_RIGHT
)
{
if
(
x
>
Constant
.
FRAME_WIDTH
)
{
return
true
;
}
}
return
result
;
return
x
<
-
1
*
scaleImageWidth
;
}
// 改变方向
public
void
setDir
(
int
dir
)
{
this
.
dir
=
dir
;
}
}
src/com/bird/main/GameBackground.java
View file @
2968bb8e
...
...
@@ -14,9 +14,9 @@ import com.bird.util.GameUtil;
*/
public
class
GameBackground
{
private
static
BufferedImage
BackgroundImg
;
// 背景图片
private
static
final
BufferedImage
BackgroundImg
;
// 背景图片
private
int
speed
;
// 背景层的速度
private
final
int
speed
;
// 背景层的速度
private
int
layerX
;
// 背景层的坐标
// 在构造器中初始化
...
...
@@ -26,11 +26,9 @@ public class GameBackground {
}
static
{
//读取背景图片
BackgroundImg
=
GameUtil
.
loadB
U
fferedImage
(
Constant
.
BG_IMG_PATH
);
BackgroundImg
=
GameUtil
.
loadB
u
fferedImage
(
Constant
.
BG_IMG_PATH
);
}
public
static
final
int
BG_IMAGE_HEIGHT
=
BackgroundImg
.
getHeight
();
//图片的高度
// 绘制方法
public
void
draw
(
Graphics
g
,
Bird
bird
)
{
// 绘制背景色
...
...
src/com/bird/main/GameElementLayer.java
View file @
2968bb8e
...
...
@@ -11,11 +11,10 @@ import com.bird.util.GameUtil;
* 游戏中各种元素层的类
*
* @author Kingyu
*
*/
public
class
GameElementLayer
{
private
List
<
Pipe
>
pipes
;
// 水管的容器
private
final
List
<
Pipe
>
pipes
;
// 水管的容器
// 构造器
public
GameElementLayer
()
{
...
...
@@ -71,35 +70,27 @@ public class GameElementLayer {
}
else
{
// 判断最后一对水管是否完全进入游戏窗口,若进入则添加水管
Pipe
lastPipe
=
pipes
.
get
(
pipes
.
size
()
-
1
);
// 获得容器中最后一个水管
if
(
lastPipe
.
isInFrame
())
{
// 根据游戏分数难度递增
if
(
GameTime
.
getInstance
().
TimeToScore
()
<
Constant
.
HOVER_MOVING_SCORE
)
{
if
(
lastPipe
.
isInFrame
())
{
if
(
pipes
.
size
()
>=
Constant
.
FULL_PIPE
-
2
)
// 若窗口中可容纳的水管已满,说明小鸟已飞到第一对水管的位置,开始记分
GameScore
.
getInstance
().
setScore
(
bird
);
try
{
if
(
GameUtil
.
isInProbability
(
2
,
5
))
{
// 40%的概率生成悬浮的普通水管
addHoverPipe
(
lastPipe
);
}
else
{
addNormalPipe
(
lastPipe
);
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
int
currentScore
=
(
int
)
GameScore
.
getInstance
().
getScore
()
+
1
;
// 获取当前分数
// 移动水管刷新的概率随当前分数递增,当得分大于19后全部刷新移动水管
if
(
GameUtil
.
isInProbability
(
currentScore
,
20
))
{
if
(
GameUtil
.
isInProbability
(
1
,
4
))
// 生成移动水管和移动悬浮水管的概率
addMovingHoverPipe
(
lastPipe
);
else
addMovingNormalPipe
(
lastPipe
);
}
else
{
try
{
if
(
GameUtil
.
isInProbability
(
1
,
4
))
{
// 1/4的概率生成普通水管
if
(
GameUtil
.
isInProbability
(
1
,
2
))
// 生成普通水管和悬浮水管的概率
if
(
GameUtil
.
isInProbability
(
1
,
2
))
// 生成静止普通水管和静止悬浮水管的概率
addNormalPipe
(
lastPipe
);
else
addHoverPipe
(
lastPipe
);
}
else
{
if
(
GameUtil
.
isInProbability
(
1
,
3
))
// 生成移动水管和移动悬浮水管的概率
addMovingHoverPipe
(
lastPipe
);
else
addMovingNormalPipe
(
lastPipe
);
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
}
}
}
...
...
@@ -113,9 +104,9 @@ public class GameElementLayer {
int
topHeight
=
GameUtil
.
getRandomNumber
(
MIN_HEIGHT
,
MAX_HEIGHT
+
1
);
// 随机生成水管高度
int
x
=
lastPipe
.
getX
()
+
HORIZONTAL_INTERVAL
;
// 新水管的x坐标 = 最后一对水管的x坐标 + 水管的间隔
Pipe
top
=
PipePool
.
get
(
"Pipe"
);
//从水管对象池中获取对象
Pipe
top
=
PipePool
.
get
(
"Pipe"
);
//
从水管对象池中获取对象
//设置x, y, height, type属性
//
设置x, y, height, type属性
top
.
setAttribute
(
x
,
-
Constant
.
TOP_PIPE_LENGTHENING
,
topHeight
+
Constant
.
TOP_PIPE_LENGTHENING
,
Pipe
.
TYPE_TOP_NORMAL
,
true
);
...
...
@@ -130,7 +121,7 @@ public class GameElementLayer {
/**
* 添加悬浮水管
*
* @param lastPipe
* @param lastPipe
传入最后一根水管以获取x坐标
*/
private
void
addHoverPipe
(
Pipe
lastPipe
)
{
...
...
@@ -158,7 +149,7 @@ public class GameElementLayer {
/**
* 添加移动的悬浮水管
*
* @param lastPipe
* @param lastPipe
传入最后一根水管以获取x坐标
*/
private
void
addMovingHoverPipe
(
Pipe
lastPipe
)
{
...
...
@@ -186,7 +177,7 @@ public class GameElementLayer {
/**
* 添加移动的普通水管
*
* @param lastPipe
* @param lastPipe
传入最后一根水管以获取x坐标
*/
private
void
addMovingNormalPipe
(
Pipe
lastPipe
)
{
int
topHeight
=
GameUtil
.
getRandomNumber
(
MIN_HEIGHT
,
MAX_HEIGHT
+
1
);
// 随机生成水管高度
...
...
@@ -205,32 +196,28 @@ public class GameElementLayer {
}
/**
* 判断元素和小鸟是否发生碰撞
,若发生碰撞返回true,否则返回false
* 判断元素和小鸟是否发生碰撞
*
* @param bird
* @return
* @param bird 传入小鸟对象
*/
public
boolean
isCollideBird
(
Bird
bird
)
{
public
void
isCollideBird
(
Bird
bird
)
{
// 若鸟已死则不再判断
if
(
bird
.
isDead
())
{
return
false
;
return
;
}
// 遍历水管容器
for
(
int
i
=
0
;
i
<
pipes
.
size
();
i
++)
{
Pipe
pipe
=
pipes
.
get
(
i
);
for
(
Pipe
pipe
:
pipes
)
{
// 判断碰撞矩形是否有交集
if
(
pipe
.
getPipeRect
().
intersects
(
bird
.
getBirdRect
()))
{
bird
.
birdFall
();
//有交集则小鸟坠落
return
true
;
bird
.
birdFall
();
//
有交集则小鸟坠落
return
;
}
}
return
false
;
}
// 重置元素层
public
void
reset
()
{
for
(
int
i
=
0
;
i
<
pipes
.
size
();
i
++)
{
Pipe
pipe
=
pipes
.
get
(
i
);
for
(
Pipe
pipe
:
pipes
)
{
PipePool
.
giveBack
(
pipe
);
}
pipes
.
clear
();
...
...
src/com/bird/main/GameForeground.java
View file @
2968bb8e
...
...
@@ -12,13 +12,11 @@ import com.bird.util.GameUtil;
* 前景类, 游戏中的遮挡层 包含多朵云
*
* @author Kingyu
*
*/
public
class
GameForeground
{
private
List
<
Cloud
>
clouds
=
new
ArrayList
<>()
;
// 云朵的容器
private
final
List
<
Cloud
>
clouds
;
// 云朵的容器
private
BufferedImage
[]
cloudImgs
;
// 图片资源
private
int
cloudDir
;
// 云的方向
private
final
BufferedImage
[]
cloudImages
;
// 图片资源
private
long
time
;
// 控制云的逻辑运算周期
public
static
final
int
CLOUD_INTERVAL
=
100
;
//云朵刷新的逻辑运算的周期
...
...
@@ -27,21 +25,18 @@ public class GameForeground {
clouds
=
new
ArrayList
<>();
//云朵的容器
// 读入图片资源
cloudIm
g
s
=
new
BufferedImage
[
Constant
.
CLOUD_IMAGE_COUNT
];
cloudIm
age
s
=
new
BufferedImage
[
Constant
.
CLOUD_IMAGE_COUNT
];
for
(
int
i
=
0
;
i
<
Constant
.
CLOUD_IMAGE_COUNT
;
i
++)
{
cloudIm
g
s
[
i
]
=
GameUtil
.
loadB
U
fferedImage
(
Constant
.
CLOUDS_IMG_PATH
[
i
]);
cloudIm
age
s
[
i
]
=
GameUtil
.
loadB
u
fferedImage
(
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
);
for
(
Cloud
cloud
:
clouds
)
{
cloud
.
draw
(
g
,
bird
);
}
}
...
...
@@ -55,18 +50,14 @@ public class GameForeground {
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
(
cloudIm
g
s
[
index
],
cloudDir
,
x
,
y
);
Cloud
cloud
=
new
Cloud
(
cloudIm
age
s
[
index
],
x
,
y
);
clouds
.
add
(
cloud
);
}
}
catch
(
Exception
e
)
{
...
...
@@ -84,15 +75,6 @@ public class GameForeground {
}
}
/*
* 功能已实现,但不太符合现实,暂时注释掉 // 云朵随机改变方向, 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(); }
*/
}
}
}
src/com/bird/main/GameFrame.java
View file @
2968bb8e
...
...
@@ -20,7 +20,6 @@ import java.awt.image.BufferedImage;
* 主窗口类,游戏窗口和绘制的相关内容
*
* @author Kingyu
*
*/
public
class
GameFrame
extends
Frame
implements
Runnable
{
...
...
@@ -72,7 +71,6 @@ public class GameFrame extends Frame implements Runnable {
bird
.
birdUp
();
bird
.
birdDown
();
setGameState
(
STATE_START
);
// 游戏状态改变
bird
.
startTiming
();
// 计时器开始计时
}
break
;
case
STATE_START:
...
...
@@ -127,12 +125,11 @@ public class GameFrame extends Frame implements Runnable {
// 系统线程:屏幕内容的绘制,窗口事件的监听与处理
// 两个线程会抢夺系统资源,可能会出现一次刷新周期所绘制的内容,并没有在一次刷新周期内完成
// (双缓冲)单独定义一张图片,将需要绘制的内容绘制到这张图片,再一次性地将图片绘制到窗口
private
BufferedImage
bufImg
=
new
BufferedImage
(
FRAME_WIDTH
,
FRAME_HEIGHT
,
BufferedImage
.
TYPE_4BYTE_ABGR
);
private
final
BufferedImage
bufImg
=
new
BufferedImage
(
FRAME_WIDTH
,
FRAME_HEIGHT
,
BufferedImage
.
TYPE_4BYTE_ABGR
);
/**
* 绘制游戏内容 当repaint()方法被调用时,JVM会调用update(),参数g是系统提供的画笔,由系统进行实例化
* 单独启动一个线程,不断地快速调用repaint(),让系统对整个窗口进行重绘
*
*/
public
void
update
(
Graphics
g
)
{
Graphics
bufG
=
bufImg
.
getGraphics
();
// 获得图片画笔
...
...
@@ -141,16 +138,18 @@ public class GameFrame extends Frame implements Runnable {
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
);
// 鸟
}
bird
.
draw
(
bufG
);
// 鸟
g
.
drawImage
(
bufImg
,
0
,
0
,
null
);
// 一次性将图片绘制到屏幕上
}
// TODO: 不建议在while循环中使用sleep
@SuppressWarnings
(
"InfiniteLoopStatement"
)
@Override
public
void
run
()
{
while
(
true
)
{
...
...
@@ -163,11 +162,6 @@ public class GameFrame extends Frame implements Runnable {
}
}
// 获取、设置游戏状态的方法
public
static
int
getGameState
()
{
return
gameState
;
}
public
static
void
setGameState
(
int
gameState
)
{
GameFrame
.
gameState
=
gameState
;
}
...
...
src/com/bird/main/GameReady.java
View file @
2968bb8e
...
...
@@ -2,9 +2,9 @@ package com.bird.main;
import
java.awt.Graphics
;
import
java.awt.image.BufferedImage
;
import
com.bird.util.Constant
;
import
com.bird.util.GameUtil
;
import
static
com
.
bird
.
util
.
GameUtil
.
drawTitle
;
/**
* 游戏启动界面类
...
...
@@ -14,15 +14,15 @@ import com.bird.util.GameUtil;
*/
public
class
GameReady
{
private
BufferedImage
titleImg
;
private
BufferedImage
noticeImg
;
private
final
BufferedImage
titleImg
;
private
final
BufferedImage
noticeImg
;
private
int
flash
;
// 图像闪烁参数
private
int
flash
=
0
;
// 图像闪烁参数
// 构造器中进行初始化,装载图像资源
public
GameReady
()
{
titleImg
=
GameUtil
.
loadB
U
fferedImage
(
Constant
.
TITLE_IMG_PATH
);
noticeImg
=
GameUtil
.
loadB
U
fferedImage
(
Constant
.
NOTICE_IMG_PATH
);
titleImg
=
GameUtil
.
loadB
u
fferedImage
(
Constant
.
TITLE_IMG_PATH
);
noticeImg
=
GameUtil
.
loadB
u
fferedImage
(
Constant
.
NOTICE_IMG_PATH
);
}
public
void
draw
(
Graphics
g
)
{
...
...
@@ -33,14 +33,10 @@ public class GameReady {
// 使notice的图像闪烁
final
int
COUNT
=
30
;
// 闪烁周期
if
(
flash
++
>
COUNT
)
{
// 计算notice图像的x、y坐标
x
=
Constant
.
FRAME_WIDTH
-
noticeImg
.
getWidth
()
>>
1
;
y
=
Constant
.
FRAME_HEIGHT
/
5
*
3
;
g
.
drawImage
(
noticeImg
,
x
,
y
,
null
);
// 绘制
if
(
flash
++
>
COUNT
)
drawTitle
(
noticeImg
,
g
);
if
(
flash
==
COUNT
*
2
)
// 重置闪烁参数
flash
=
0
;
}
}
}
src/com/bird/main/Game
Tim
e.java
→
src/com/bird/main/Game
Scor
e.java
View file @
2968bb8e
...
...
@@ -15,35 +15,27 @@ import com.bird.util.MusicUtil;
* @author Kingyu
*
*/
public
class
Game
Tim
e
{
private
static
final
Game
Tim
e
GAME_
TIM
E
=
new
Game
Tim
e
();
public
class
Game
Scor
e
{
private
static
final
Game
Scor
e
GAME_
SCOR
E
=
new
Game
Scor
e
();
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
;
// 最高分数
private
GameTime
()
{
timeState
=
STATE_READY
;
private
GameScore
()
{
bestScore
=
-
1
;
try
{
loadBest
Tim
e
();
loadBest
Scor
e
();
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
public
static
Game
Tim
e
getInstance
()
{
return
GAME_
TIM
E
;
public
static
Game
Scor
e
getInstance
()
{
return
GAME_
SCOR
E
;
}
// 装载最高纪录
private
void
loadBest
Tim
e
()
throws
Exception
{
private
void
loadBest
Scor
e
()
throws
Exception
{
File
file
=
new
File
(
Constant
.
SCORE_FILE_PATH
);
if
(
file
.
exists
())
{
DataInputStream
dis
=
new
DataInputStream
(
new
FileInputStream
(
file
));
...
...
@@ -53,10 +45,10 @@ public class GameTime {
}
// 保存最高纪录
public
void
saveBestScore
(
long
tim
e
)
throws
Exception
{
public
void
saveBestScore
(
long
scor
e
)
throws
Exception
{
File
file
=
new
File
(
Constant
.
SCORE_FILE_PATH
);
DataOutputStream
dos
=
new
DataOutputStream
(
new
FileOutputStream
(
file
));
dos
.
writeLong
(
tim
e
);
dos
.
writeLong
(
scor
e
);
dos
.
close
();
}
...
...
@@ -64,51 +56,20 @@ public class GameTime {
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
long
getScore
()
{
return
score
;
}
// 计时器是否就绪
public
boolean
isReadyTiming
()
{
return
timeState
==
STATE_READY
;
public
void
setScore
(
Bird
bird
)
{
if
(!
bird
.
isDead
()){
MusicUtil
.
playScore
();
//每次得分播放音效
score
+=
1
;
//小鸟没死时记分
}
// 开始计时
public
void
startTiming
()
{
startTime
=
System
.
currentTimeMillis
();
timeState
=
STATE_START
;
}
// 结束计时并判断是否保存记录
public
void
endTiming
()
{
endTime
=
System
.
currentTimeMillis
();
timeState
=
STATE_OVER
;
// 判断本次得分是否为最高分
long
score
=
TimeToScore
();
// 判断是否为最高纪录
public
void
isSaveScore
()
{
long
score
=
getScore
();
if
(
bestScore
<
score
)
bestScore
=
score
;
try
{
...
...
@@ -118,39 +79,78 @@ public class GameTime {
}
}
private
static
final
int
FIRST_SCORE_TIME
=
6600
;
// 从游戏开始到通过第一根水管的所需时间
private
static
final
int
PER_SCORE_TIME
=
2900
;
// 通过后续每一根水管的间隔的所需时间
//将游戏时间转换为通过水管的数量
public
long
TimeToScore
()
{
long
time
=
getTime
();
long
temp
=
score
;
if
(
time
>=
FIRST_SCORE_TIME
&&
time
<
FIRST_SCORE_TIME
+
PER_SCORE_TIME
)
{
score
=
1
;
//time大于FIRST_SCORE_TIME且未到第二对水管
}
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
;
score
=
0
;
}
// private int timeState; // 计时器的状态
// public static final int STATE_READY = 0; // 计时就绪
// public static final int STATE_START = 1; // 计时开始
// public static final int STATE_OVER = 2; // 计时结束
// 以下为游戏计时的相关方法,因改进了记分方式目前无用
// /**
// * 游戏用时,毫秒
// *
// * @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;
// }
//
// private static final int FIRST_SCORE_TIME = 6600; // 从游戏开始到通过第一根水管的所需时间
// private static final int PER_SCORE_TIME = 2900; // 通过后续每一根水管的间隔的所需时间
//
// //将游戏时间转换为通过水管的数量
// public long TimeToScore() {
// long time = getTime();
// long temp = score;
// if (time >= FIRST_SCORE_TIME && time < FIRST_SCORE_TIME + PER_SCORE_TIME) {
// score = 1; //time大于FIRST_SCORE_TIME且未到第二对水管
// } 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;
// }
}
src/com/bird/main/MovingPipe.java
View file @
2968bb8e
...
...
@@ -27,11 +27,11 @@ public class MovingPipe extends Pipe {
/**
* 设置水管参数
*
* @param x
* @param y
* @param height
* @param type
* @param visible
* @param x
:x坐标
* @param y
:y坐标
* @param height
:水管高度
* @param type
:水管类型
* @param visible
:水管可见性
*/
public
void
setAttribute
(
int
x
,
int
y
,
int
height
,
int
type
,
boolean
visible
)
{
this
.
x
=
x
;
...
...
src/com/bird/main/Pipe.java
View file @
2968bb8e
...
...
@@ -11,15 +11,15 @@ import com.bird.util.GameUtil;
* 水管类
*
* @author Kingyu
*
*/
public
class
Pipe
{
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
.
loadB
U
fferedImage
(
Constant
.
PIPE_IMG_PATH
[
i
]);
imgs
[
i
]
=
GameUtil
.
loadB
u
fferedImage
(
Constant
.
PIPE_IMG_PATH
[
i
]);
}
}
...
...
@@ -43,15 +43,14 @@ public class Pipe {
public
static
final
int
TYPE_HOVER_HARD
=
5
;
// 水管的速度
public
static
final
int
MIN_SPEED
=
1
;
public
static
final
int
MAX_SPEED
=
2
;
public
static
final
int
SPEED
=
1
;
int
speed
;
Rectangle
pipeRect
;
// 水管的碰撞矩形
// 构造器
public
Pipe
()
{
this
.
speed
=
MIN_
SPEED
;
this
.
speed
=
SPEED
;
this
.
width
=
PIPE_WIDTH
;
pipeRect
=
new
Rectangle
();
...
...
@@ -61,11 +60,11 @@ public class Pipe {
/**
* 设置水管参数
*
* @param x
* @param y
* @param height
* @param type
* @param visible
* @param x
:x坐标
* @param y
:y坐标
* @param height
:水管高度
* @param type
:水管类型
* @param visible
:水管可见性
*/
public
void
setAttribute
(
int
x
,
int
y
,
int
height
,
int
type
,
boolean
visible
)
{
this
.
x
=
x
;
...
...
@@ -78,10 +77,6 @@ public class Pipe {
/**
* 设置碰撞矩形参数
*
* @param x
* @param y
* @param height
*/
public
void
setRectangle
(
int
x
,
int
y
,
int
height
)
{
pipeRect
.
x
=
x
;
...
...
@@ -179,27 +174,7 @@ public class Pipe {
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
;
}
// 获取水管的x坐标
public
int
getX
()
{
return
x
;
}
...
...
@@ -208,4 +183,26 @@ public class Pipe {
public
Rectangle
getPipeRect
()
{
return
pipeRect
;
}
// 各参数的写入与获取
// 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;
// }
}
src/com/bird/main/PipePool.java
View file @
2968bb8e
...
...
@@ -6,25 +6,23 @@ import java.util.List;
import
com.bird.util.Constant
;
/**
* 为了避免反复地创建和销毁对象,使用对象池来提前创建好一些对象,使用时从对象池中获得,使用完
毕
归还
* 为了避免反复地创建和销毁对象,使用对象池来提前创建好一些对象,使用时从对象池中获得,使用完
后
归还
*
* @author Kingyu
*
*/
public
class
PipePool
{
private
static
List
<
Pipe
>
pool
=
new
ArrayList
<
Pipe
>();
// 池中对象的容器
private
static
List
<
MovingPipe
>
movingPool
=
new
ArrayList
<
MovingPipe
>();
// 池中对象的容器
private
static
final
List
<
Pipe
>
pool
=
new
ArrayList
<>();
// 池中对象的容器
private
static
final
List
<
MovingPipe
>
movingPool
=
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
=
30
;
// 对象池中对象的最大个数,自行定义
// 初始化水管容器
// 初始化水管容器
,初始化水管的数量的计算方式见常量类中的注释
static
{
for
(
int
i
=
0
;
i
<
INIT_PIPE_COUNT
;
i
++)
{
for
(
int
i
=
0
;
i
<
Constant
.
FULL_PIPE
;
i
++)
{
pool
.
add
(
new
Pipe
());
}
for
(
int
i
=
0
;
i
<
INIT_PIPE_COUNT
;
i
++)
{
for
(
int
i
=
0
;
i
<
Constant
.
FULL_PIPE
;
i
++)
{
movingPool
.
add
(
new
MovingPipe
());
}
}
...
...
@@ -54,8 +52,6 @@ public class PipePool {
/**
* 归还对象给容器
*
* @param pipe
*/
public
static
void
giveBack
(
Pipe
pipe
)
{
//判断类的类型
...
...
src/com/bird/util/Constant.java
View file @
2968bb8e
...
...
@@ -3,6 +3,9 @@ package com.bird.util;
import
java.awt.Color
;
import
java.awt.Font
;
import
com.bird.main.GameElementLayer
;
import
com.bird.main.Pipe
;
/**
* 常量类
*
...
...
@@ -18,14 +21,12 @@ public class Constant {
public
static
final
String
GAME_TITLE
=
"Flappy Bird written by Kingyu"
;
// 窗口位置
public
static
final
int
FRAME_X
=
12
00
;
public
static
final
int
FRAME_X
=
6
00
;
public
static
final
int
FRAME_Y
=
100
;
// 图像资源路径
public
static
final
String
BG_IMG_PATH
=
"resources/img/background.png"
;
// 背景图片
public
static
final
int
HOVER_MOVING_SCORE
=
4
;
//出现移动管道的分数
// 小鸟图片
public
static
final
String
[][]
BIRDS_IMG_PATH
=
{
{
"resources/img/0.png"
,
"resources/img/1.png"
,
"resources/img/2.png"
,
"resources/img/3.png"
,
...
...
@@ -72,9 +73,11 @@ public class Constant {
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
TIM
E_FONT
=
new
Font
(
"华文琥珀"
,
Font
.
BOLD
,
32
);
// 字体
public
static
final
Font
CURRENT_SCOR
E_FONT
=
new
Font
(
"华文琥珀"
,
Font
.
BOLD
,
32
);
// 字体
public
static
final
Font
SCORE_FONT
=
new
Font
(
"华文琥珀"
,
Font
.
BOLD
,
24
);
// 字体
// 窗口可容纳的水管数量+2, 由窗口宽度、水管宽度、水管间距算得
public
static
final
int
FULL_PIPE
=
(
Constant
.
FRAME_WIDTH
/
(
Pipe
.
PIPE_HEAD_WIDTH
+
GameElementLayer
.
HORIZONTAL_INTERVAL
)
+
2
)
*
2
;
}
src/com/bird/util/GameUtil.java
View file @
2968bb8e
package
com.bird.util
;
import
java.awt.
Font
;
import
java.awt.
*
;
import
java.awt.image.BufferedImage
;
import
java.io.FileInputStream
;
import
java.io.IOException
;
...
...
@@ -13,7 +13,6 @@ import javax.imageio.ImageIO;
* 工具类,游戏中用到的工具都在此类
*
* @author Kingyu
*
*/
public
class
GameUtil
{
...
...
@@ -24,9 +23,9 @@ public class GameUtil {
* 装载图片的方法
*
* @param imgPath 图片路径
* @return
* @return
图片资源
*/
public
static
BufferedImage
loadB
U
fferedImage
(
String
imgPath
)
{
public
static
BufferedImage
loadB
u
fferedImage
(
String
imgPath
)
{
try
{
return
ImageIO
.
read
(
new
FileInputStream
(
imgPath
));
}
catch
(
IOException
e
)
{
...
...
@@ -42,13 +41,13 @@ public class GameUtil {
* @param denominator 分母,不小于0的值
* @return 概率性事件发生返回true,否则返回false
*/
public
static
boolean
isInProbability
(
int
numerator
,
int
denominator
)
throws
Exception
{
public
static
boolean
isInProbability
(
int
numerator
,
int
denominator
)
throws
Exception
{
// 分子分母不小于0
if
(
numerator
<=
0
||
denominator
<=
0
)
{
throw
new
Exception
(
"传入了非法的参数"
);
}
//分子大于分母,一定发生
if
(
numerator
>=
denominator
)
{
if
(
numerator
>=
denominator
)
{
return
true
;
}
...
...
@@ -71,16 +70,22 @@ public class GameUtil {
*/
public
static
int
getStringWidth
(
Font
font
,
String
str
)
{
AffineTransform
affinetransform
=
new
AffineTransform
();
FontRenderContext
frc
=
new
FontRenderContext
(
affinetransform
,
true
,
true
);
int
textHeight
=
(
int
)(
font
.
getStringBounds
(
str
,
frc
).
getWidth
());
return
textHeight
;
FontRenderContext
frc
=
new
FontRenderContext
(
affinetransform
,
true
,
true
);
return
(
int
)
(
font
.
getStringBounds
(
str
,
frc
).
getWidth
());
}
public
static
int
getStringHeight
(
Font
font
,
String
str
)
{
AffineTransform
affinetransform
=
new
AffineTransform
();
FontRenderContext
frc
=
new
FontRenderContext
(
affinetransform
,
true
,
true
);
int
textHeight
=
(
int
)(
font
.
getStringBounds
(
str
,
frc
).
getHeight
());
return
textHeight
;
FontRenderContext
frc
=
new
FontRenderContext
(
affinetransform
,
true
,
true
);
return
(
int
)
(
font
.
getStringBounds
(
str
,
frc
).
getHeight
());
}
// 于屏幕x轴中央、y轴3/5处绘制图像
public
static
void
drawTitle
(
BufferedImage
image
,
Graphics
g
)
{
int
x
=
Constant
.
FRAME_WIDTH
-
image
.
getWidth
()
>>
1
;
int
y
=
Constant
.
FRAME_HEIGHT
/
5
*
3
;
g
.
drawImage
(
image
,
x
,
y
,
null
);
}
}
src/com/bird/util/MusicUtil.java
View file @
2968bb8e
package
com.bird.util
;
import
java.io.FileInputStream
;
import
java.io.FileNotFoundException
;
import
java.io.IOException
;
import
java.io.InputStream
;
...
...
@@ -11,8 +10,8 @@ import sun.audio.AudioStream;
/**
* 音乐工具类
*
* @author Kingyu
wav音频:JDK提供的类可直接解码 mp3音频:JDK没有提供支持,需要使用第三方的工具包
*
* @author Kingyu
*
wav音频:JDK提供的类可直接解码 mp3音频:JDK没有提供支持,需要使用第三方的工具包
*/
public
class
MusicUtil
{
...
...
@@ -20,40 +19,33 @@ public class MusicUtil {
private
static
AudioStream
crash
;
private
static
AudioStream
score
;
private
static
InputStream
flyIn
;
private
static
InputStream
crashIn
;
private
static
InputStream
scoreIn
;
//wav播放
// wav播放
public
static
void
playFly
()
{
try
{
// create an
a
udio
s
tream from the
i
nput
s
tream
flyIn
=
new
FileInputStream
(
"resources/wav/fly.wav"
);
// create an
A
udio
S
tream from the
I
nput
S
tream
InputStream
flyIn
=
new
FileInputStream
(
"resources/wav/fly.wav"
);
fly
=
new
AudioStream
(
flyIn
);
}
catch
(
FileNotFoundException
fnfe
)
{
}
catch
(
IOException
ioe
)
{
}
catch
(
IOException
ignored
)
{
}
AudioPlayer
.
player
.
start
(
fly
);
}
public
static
void
playCrash
()
{
try
{
// create an
a
udio
s
tream from the
i
nput
s
tream
crashIn
=
new
FileInputStream
(
"resources/wav/crash.wav"
);
// create an
A
udio
S
tream from the
I
nput
S
tream
InputStream
crashIn
=
new
FileInputStream
(
"resources/wav/crash.wav"
);
crash
=
new
AudioStream
(
crashIn
);
}
catch
(
FileNotFoundException
fnfe
)
{
}
catch
(
IOException
ioe
)
{
}
catch
(
IOException
ignored
)
{
}
AudioPlayer
.
player
.
start
(
crash
);
}
public
static
void
playScore
()
{
try
{
// create an
a
udio
s
tream from the
i
nput
s
tream
scoreIn
=
new
FileInputStream
(
"resources/wav/score.wav"
);
// create an
A
udio
S
tream from the
I
nput
S
tream
InputStream
scoreIn
=
new
FileInputStream
(
"resources/wav/score.wav"
);
score
=
new
AudioStream
(
scoreIn
);
}
catch
(
FileNotFoundException
fnfe
)
{
}
catch
(
IOException
ioe
)
{
}
catch
(
IOException
ignored
)
{
}
AudioPlayer
.
player
.
start
(
score
);
}
...
...
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