Skip to content

Commit 8f2fb62

Browse files
authored
Add files via upload
1 parent da4b27f commit 8f2fb62

File tree

16 files changed

+1617
-0
lines changed

16 files changed

+1617
-0
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package Games.FlappyBird.com.kingyu.flappybird.app;
2+
3+
/**
4+
* 游�入�
5+
*
6+
* @author Kingyu
7+
*
8+
*/
9+
10+
public class App {
11+
public static void main(String[] args) {
12+
new Game();
13+
}
14+
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
package Games.FlappyBird.com.kingyu.flappybird.app;
2+
3+
import Games.FlappyBird.com.kingyu.flappybird.component.GameElementLayer;
4+
import Games.FlappyBird.com.kingyu.flappybird.component.Bird;
5+
import Games.FlappyBird.com.kingyu.flappybird.component.GameBackground;
6+
import Games.FlappyBird.com.kingyu.flappybird.component.GameForeground;
7+
import Games.FlappyBird.com.kingyu.flappybird.component.WelcomeAnimation;
8+
9+
import static Games.FlappyBird.com.kingyu.flappybird.util.Constant.FRAME_HEIGHT;
10+
import static Games.FlappyBird.com.kingyu.flappybird.util.Constant.FRAME_WIDTH;
11+
import static Games.FlappyBird.com.kingyu.flappybird.util.Constant.FRAME_X;
12+
import static Games.FlappyBird.com.kingyu.flappybird.util.Constant.FRAME_Y;
13+
import static Games.FlappyBird.com.kingyu.flappybird.util.Constant.FPS;
14+
import static Games.FlappyBird.com.kingyu.flappybird.util.Constant.GAME_TITLE;
15+
16+
import java.awt.Frame;
17+
import java.awt.Graphics;
18+
import java.awt.event.*;
19+
import java.awt.image.BufferedImage;
20+
21+
22+
/**
23+
* 游�主体,管�游�的组件和窗�绘制
24+
*
25+
* @author Kingyu
26+
*/
27+
28+
public class Game extends Frame {
29+
private static final long serialVersionUID = 1L; // ��版本的兼容性
30+
31+
private static int gameState; // 游�状�
32+
public static final int GAME_READY = 0; // 游�未开始
33+
public static final int GAME_START = 1; // 游�开始
34+
public static final int STATE_OVER = 2; // 游�结�
35+
36+
private GameBackground background; // 游�背景对象
37+
private GameForeground foreground; // 游��景对象
38+
private Bird bird; // �鸟对象
39+
private GameElementLayer gameElement; // 游�元素对象
40+
private WelcomeAnimation welcomeAnimation; // 游�未开始时对象
41+
42+
// 在构造器中�始化
43+
public Game() {
44+
initFrame(); // �始化游�窗�
45+
setVisible(true); // 窗�默认为���,设置为��
46+
initGame(); // �始化游�对象
47+
}
48+
49+
// �始化游�窗�
50+
private void initFrame() {
51+
setSize(FRAME_WIDTH, FRAME_HEIGHT); // 设置窗�大�
52+
setTitle(GAME_TITLE); // 设置窗�标题
53+
setLocation(FRAME_X, FRAME_Y); // 窗��始�置
54+
setResizable(false); // 设置窗�大����
55+
// 添加关闭窗�事件(监�窗��生的事件,派�给�数对象,�数对象调用对应的方法)
56+
addWindowListener(new WindowAdapter() {
57+
@Override
58+
public void windowClosing(WindowEvent e) {
59+
System.exit(0); // 结�程�
60+
}
61+
});
62+
addKeyListener(new BirdKeyListener()); // 添加按键监�
63+
}
64+
65+
// 用于接收按键事件的对象的内部类
66+
class BirdKeyListener implements KeyListener {
67+
// 按键按下,根�游�当�的状�调用��的方法
68+
public void keyPressed(KeyEvent e) {
69+
int keycode = e.getKeyCode();
70+
switch (gameState) {
71+
case GAME_READY:
72+
if (keycode == KeyEvent.VK_SPACE) {
73+
// 游��动界�时按下空格,�鸟振翅一次并开始��力影�
74+
bird.birdFlap();
75+
bird.birdFall();
76+
setGameState(GAME_START); // 游�状�改�
77+
}
78+
break;
79+
case GAME_START:
80+
if (keycode == KeyEvent.VK_SPACE) {
81+
//游�过程中按下空格则振翅一次,并�续��力影�
82+
bird.birdFlap();
83+
bird.birdFall();
84+
}
85+
break;
86+
case STATE_OVER:
87+
if (keycode == KeyEvent.VK_SPACE) {
88+
//游�结�时按下空格,�新开始游�
89+
resetGame();
90+
}
91+
break;
92+
}
93+
}
94+
95+
// �新开始游�
96+
private void resetGame() {
97+
setGameState(GAME_READY);
98+
gameElement.reset();
99+
bird.reset();
100+
}
101+
102+
// 按键�开,更改按键状�标志
103+
public void keyReleased(KeyEvent e) {
104+
int keycode = e.getKeyChar();
105+
if (keycode == KeyEvent.VK_SPACE) {
106+
bird.keyReleased();
107+
}
108+
}
109+
110+
public void keyTyped(KeyEvent e) {
111+
}
112+
}
113+
114+
// �始化游�中的�个对象
115+
private void initGame() {
116+
background = new GameBackground();
117+
gameElement = new GameElementLayer();
118+
foreground = new GameForeground();
119+
welcomeAnimation = new WelcomeAnimation();
120+
bird = new Bird();
121+
setGameState(GAME_READY);
122+
123+
// �动用于刷新窗�的线程
124+
new Thread(() ->{
125+
while (true) {
126+
repaint(); // 通过调用repaint(),让JVM调用update()
127+
try {
128+
Thread.sleep(FPS);
129+
} catch (InterruptedException e) {
130+
e.printStackTrace();
131+
}
132+
}
133+
}).start();
134+
}
135+
136+
// 项目中存在两个线程:系统线程,自定义的线程:调用repaint()。
137+
// 系统线程:�幕内容的绘制,窗�事件的监�与处�
138+
// 两个线程会抢夺系统资�,�能会出现一次刷新周期所绘制的内容,并没有在一次刷新周期内完�
139+
// (�缓冲)�独定义一张图片,将需�绘制的内容绘制到这张图片,�一次性地将图片绘制到窗�
140+
private final BufferedImage bufImg = new BufferedImage(FRAME_WIDTH, FRAME_HEIGHT, BufferedImage.TYPE_4BYTE_ABGR);
141+
142+
/**
143+
* 绘制游�内容 当repaint()方法被调用时,JVM会调用update(),�数g是系统�供的画笔,由系统进行实例化
144+
* �独�动一个线程,�断地快速调用repaint(),让系统对整个窗�进行�绘
145+
*/
146+
public void update(Graphics g) {
147+
Graphics bufG = bufImg.getGraphics(); // 获得图片画笔
148+
// 使用图片画笔将需�绘制的内容绘制到图片
149+
background.draw(bufG, bird); // 背景层
150+
foreground.draw(bufG, bird); // �景层
151+
if (gameState == GAME_READY) { // 游�未开始
152+
welcomeAnimation.draw(bufG);
153+
} else { // 游�结�
154+
gameElement.draw(bufG, bird); // 游�元素层
155+
}
156+
bird.draw(bufG);
157+
g.drawImage(bufImg, 0, 0, null); // 一次性将图片绘制到�幕上
158+
}
159+
160+
public static void setGameState(int gameState) {
161+
Game.gameState = gameState;
162+
}
163+
164+
}
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
package Games.FlappyBird.com.kingyu.flappybird.component;
2+
3+
import java.awt.Color;
4+
import java.awt.Graphics;
5+
import java.awt.Rectangle;
6+
import java.awt.image.BufferedImage;
7+
8+
import Games.FlappyBird.com.kingyu.flappybird.app.Game;
9+
import Games.FlappyBird.com.kingyu.flappybird.util.Constant;
10+
import Games.FlappyBird.com.kingyu.flappybird.util.GameUtil;
11+
import Games.FlappyBird.com.kingyu.flappybird.util.MusicUtil;
12+
13+
/**
14+
* �鸟类,实现�鸟的绘制与飞行逻辑
15+
*
16+
* @author Kingyu
17+
*/
18+
public class Bird {
19+
public static final int IMG_COUNT = 8; // 图片数�
20+
public static final int STATE_COUNT = 4; // 状�数
21+
private final BufferedImage[][] birdImages; // �鸟的图片数组对象
22+
private final int x;
23+
private int y; // �鸟的�标
24+
private int wingState; // 翅膀状�
25+
26+
// 图片资�
27+
private BufferedImage image; // 实时的�鸟图片
28+
29+
// �鸟的状�
30+
private int state;
31+
public static final int BIRD_NORMAL = 0;
32+
public static final int BIRD_UP = 1;
33+
public static final int BIRD_FALL = 2;
34+
public static final int BIRD_DEAD_FALL = 3;
35+
public static final int BIRD_DEAD = 4;
36+
37+
private final Rectangle birdCollisionRect; // 碰撞矩形
38+
public static final int RECT_DESCALE = 2; // 补�碰撞矩形宽高的�数
39+
40+
private final ScoreCounter counter; // 计分器
41+
private final GameOverAnimation gameOverAnimation;
42+
43+
public static int BIRD_WIDTH;
44+
public static int BIRD_HEIGHT;
45+
46+
// 在构造器中对资��始化
47+
public Bird() {
48+
counter = ScoreCounter.getInstance(); // 计分器
49+
gameOverAnimation = new GameOverAnimation();
50+
51+
// 读��鸟图片资�
52+
birdImages = new BufferedImage[STATE_COUNT][IMG_COUNT];
53+
for (int j = 0; j < STATE_COUNT; j++) {
54+
for (int i = 0; i < IMG_COUNT; i++) {
55+
birdImages[j][i] = GameUtil.loadBufferedImage(Constant.BIRDS_IMG_PATH[j][i]);
56+
}
57+
}
58+
59+
assert birdImages[0][0] != null;
60+
BIRD_WIDTH = birdImages[0][0].getWidth();
61+
BIRD_HEIGHT = birdImages[0][0].getHeight();
62+
63+
// �始化�鸟的�标
64+
x = Constant.FRAME_WIDTH >> 2;
65+
y = Constant.FRAME_HEIGHT >> 1;
66+
67+
// �始化碰撞矩形
68+
int rectX = x - BIRD_WIDTH / 2;
69+
int rectY = y - BIRD_HEIGHT / 2;
70+
birdCollisionRect = new Rectangle(rectX + RECT_DESCALE, rectY + RECT_DESCALE * 2, BIRD_WIDTH - RECT_DESCALE * 3,
71+
BIRD_WIDTH - RECT_DESCALE * 4); // 碰撞矩形的�标与�鸟相�
72+
}
73+
74+
// 绘制方法
75+
public void draw(Graphics g) {
76+
movement();
77+
int state_index = Math.min(state, BIRD_DEAD_FALL); // 图片资�索引
78+
// �鸟中心点计算
79+
int halfImgWidth = birdImages[state_index][0].getWidth() >> 1;
80+
int halfImgHeight = birdImages[state_index][0].getHeight() >> 1;
81+
if (velocity > 0)
82+
image = birdImages[BIRD_UP][0];
83+
g.drawImage(image, x - halfImgWidth, y - halfImgHeight, null); // x�标于窗�1/4处,y�标�窗�中心
84+
85+
if (state == BIRD_DEAD)
86+
gameOverAnimation.draw(g, this);
87+
else if (state != BIRD_DEAD_FALL)
88+
drawScore(g);
89+
// 绘制碰撞矩形
90+
// g.setColor(Color.black);
91+
// g.drawRect((int) birdRect.getX(), (int) birdRect.getY(), (int) birdRect.getWidth(), (int) birdRect.getHeight());
92+
}
93+
94+
public static final int ACC_FLAP = 14; // players speed on flapping
95+
public static final double ACC_Y = 2; // players downward acceleration
96+
public static final int MAX_VEL_Y = 15; // max vel along Y, max descend speed
97+
private int velocity = 0; // bird's velocity along Y, default same as playerFlapped
98+
private final int BOTTOM_BOUNDARY = Constant.FRAME_HEIGHT - GameBackground.GROUND_HEIGHT - (BIRD_HEIGHT / 2);
99+
100+
// �鸟的飞行逻辑
101+
private void movement() {
102+
// 翅膀状�,实现�鸟振翅飞行
103+
wingState++;
104+
image = birdImages[Math.min(state, BIRD_DEAD_FALL)][wingState / 10 % IMG_COUNT];
105+
if (state == BIRD_FALL || state == BIRD_DEAD_FALL) {
106+
freeFall();
107+
if (birdCollisionRect.y > BOTTOM_BOUNDARY) {
108+
if (state == BIRD_FALL) {
109+
MusicUtil.playCrash();
110+
}
111+
die();
112+
}
113+
}
114+
}
115+
116+
private void freeFall() {
117+
if (velocity < MAX_VEL_Y)
118+
velocity -= ACC_Y;
119+
y = Math.min((y - velocity), BOTTOM_BOUNDARY);
120+
birdCollisionRect.y = birdCollisionRect.y - velocity;
121+
}
122+
123+
private void die() {
124+
counter.saveScore();
125+
state = BIRD_DEAD;
126+
Game.setGameState(Game.STATE_OVER);
127+
}
128+
129+
// �鸟振翅
130+
public void birdFlap() {
131+
if (keyIsReleased()) {
132+
if (isDead())
133+
return;
134+
MusicUtil.playFly(); // 播放音效
135+
state = BIRD_UP;
136+
if (birdCollisionRect.y > Constant.TOP_BAR_HEIGHT) {
137+
velocity = ACC_FLAP; // �次振翅将速度改为上�速度
138+
wingState = 0; // �置翅膀状�
139+
}
140+
keyPressed();
141+
}
142+
}
143+
144+
// �鸟下�
145+
public void birdFall() {
146+
if (isDead())
147+
return;
148+
state = BIRD_FALL;
149+
}
150+
151+
// �鸟��(已死)
152+
public void deadBirdFall() {
153+
state = BIRD_DEAD_FALL;
154+
MusicUtil.playCrash(); // 播放音效
155+
velocity = 0; // 速度置0,防止�鸟继续上�与水管��
156+
}
157+
158+
// 判断�鸟是�死亡
159+
public boolean isDead() {
160+
return state == BIRD_DEAD_FALL || state == BIRD_DEAD;
161+
}
162+
163+
// 绘制实时分数
164+
private void drawScore(Graphics g) {
165+
g.setColor(Color.white);
166+
g.setFont(Constant.CURRENT_SCORE_FONT);
167+
String str = Long.toString(counter.getCurrentScore());
168+
int x = Constant.FRAME_WIDTH - GameUtil.getStringWidth(Constant.CURRENT_SCORE_FONT, str) >> 1;
169+
g.drawString(str, x, Constant.FRAME_HEIGHT / 10);
170+
}
171+
172+
// �置�鸟
173+
public void reset() {
174+
state = BIRD_NORMAL; // �鸟状�
175+
y = Constant.FRAME_HEIGHT >> 1; // �鸟�标
176+
velocity = 0; // �鸟速度
177+
178+
int ImgHeight = birdImages[state][0].getHeight();
179+
birdCollisionRect.y = y - ImgHeight / 2 + RECT_DESCALE * 2; // �鸟碰撞矩形�标
180+
181+
counter.reset(); // �置计分器
182+
}
183+
184+
private boolean keyFlag = true; // 按键状�,true为已释放,使当按�按键时�会��调用方法
185+
186+
public void keyPressed() {
187+
keyFlag = false;
188+
}
189+
190+
public void keyReleased() {
191+
keyFlag = true;
192+
}
193+
194+
public boolean keyIsReleased() {
195+
return keyFlag;
196+
}
197+
198+
public long getCurrentScore() {
199+
return counter.getCurrentScore();
200+
}
201+
202+
public long getBestScore() {
203+
return counter.getBestScore();
204+
}
205+
206+
public int getBirdX() {
207+
return x;
208+
}
209+
210+
// 获��鸟的碰撞矩形
211+
public Rectangle getBirdCollisionRect() {
212+
return birdCollisionRect;
213+
}
214+
}

0 commit comments

Comments
 (0)