Commit 4e8f8dab authored by kingyu's avatar kingyu Committed by kingyuluk
Browse files

feat: 随机刷新可自行上下移动的水管

BREAKING CHANGE: 移动型水管的刷新概率会随着当前游戏分数递增
parent 5e5ba4bf
...@@ -3,6 +3,19 @@ ...@@ -3,6 +3,19 @@
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. 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.0 (2020-07-11)
### ⚠ BREAKING CHANGES
* 移动型水管的刷新概率会随着当前游戏分数递增
### Features
* 随机刷新可自行上下移动的水管 ([6c72b96](https://github.com/kingyuluk/FlappyBird/commit/6c72b968c8f7d953f2a34bc795674dcfe570c688))
* 添加悬浮型水管 ([3f84810](https://github.com/kingyuluk/FlappyBird/commit/3f84810429e0e2dd0932ff325845fc10daebde88))
* 具备原版的游戏功能 ([264a7c3](https://github.com/kingyuluk/FlappyBird/commit/264a7c320c894851aa3e6c8c30ffcddf3ce1f78a))
## 1.1.0 (2020-07-11) ## 1.1.0 (2020-07-11)
......
# Flappy Bird # Flappy Bird
Flappy Bird for desktop platforms. Flappy Bird for desktop platforms.
基于java语言编写的Flappy Bird,只用了基本库 基于Java和JDK基本库编写
开发难度不高,适合JAVA刚入门的同学,注释写得很详细,欢迎交流 开发平台为macOS 10.15.5,开发工具为Eclipse IDE (4.16.0),Java SE 8[1.8.0_251]
开发平台为macOS 10.15.5,开发工具为Eclipse IDE (4.16.0), Java SE 8[1.8.0_251]
## Overview
# Package Contents 本项目为Flappy bird的桌面平台版,具备原版的所有功能,且相较于原版优化了游戏难度的梯度并加入移动型水管,使游戏的可玩性更高。
com.bird.app 游戏的入口 ### 游戏玩法
每局游戏随机刷新所有元素,游戏只需空格键即可操作,敲击按键小鸟就会振翅向上飞,且受到重力作用会不断下坠,需要玩家控制小鸟不断飞行,并注意躲避随机生成的水管,每飞过一对水管就会得分,飞行过程中如果撞到水管或掉落在地则游戏结束。
com.bird.main 游戏的内容
com.bird.util 自定义的工具 ## 游戏界面
### 游戏启动
![image](https://github.com/kingyuluk/flappy-bird/blob/master/examples/start.png)
# Version History ### 运行示例
1.0.0 - July 10, 2020 ![image](https://github.com/kingyuluk/flappy-bird/blob/master/examples/play.gif)
* 具备完整的游戏功能
### 游戏结束
![image](https://github.com/kingyuluk/flappy-bird/blob/master/examples/over.png)
## Package Contents
* com.bird.app 游戏的入口
* com.bird.main 游戏的内容
* com.bird.util 自定义的工具
1.1.0 - July 11, 2020 ## Change Log
v1.2.0 - July 11, 2020
* 现在水管可以移动了,随着游戏分数的上升会提升游戏难度
v1.1.0 - July 11, 2020
* 添加了悬浮型的水管 * 添加了悬浮型的水管
# Notes v1.0.0 - July 10, 2020
* 具备完整的游戏功能
## Notes
* 文本编码格式为UTF-8,若注释出现乱码请修改编译器的文本编码格式
* 由于使用了sun.*包,不同版本的JDK中sun包中的类可能发生变化,因此无法确保工作在所有JAVA平台上
文本编码格式为UTF-8,若注释出现乱码请修改编译器的文本编码格式 ## Contact
# Contact * email: <kingyuluk@mail.dlut.edu.cn>
* email: <kingyuluk@hotmail.com>
sources/img/title.png

1.72 KB | W: | H:

sources/img/title.png

5.22 KB | W: | H:

sources/img/title.png
sources/img/title.png
sources/img/title.png
sources/img/title.png
  • 2-up
  • Swipe
  • Onion skin
...@@ -72,7 +72,7 @@ public class Bird { ...@@ -72,7 +72,7 @@ public class Bird {
// 绘制方法 // 绘制方法
public void draw(Graphics g) { public void draw(Graphics g) {
Fly(); fly();
int state_index = state > STATE_FALL ? STATE_FALL : state; // 图片资源索引 int state_index = state > STATE_FALL ? STATE_FALL : state; // 图片资源索引
// 小鸟中心点计算 // 小鸟中心点计算
int halfImgWidth = birdImgs[state_index][0].getWidth() >> 1; int halfImgWidth = birdImgs[state_index][0].getWidth() >> 1;
...@@ -85,10 +85,8 @@ public class Bird { ...@@ -85,10 +85,8 @@ public class Bird {
drawGameover(g); drawGameover(g);
} else if (state == STATE_FALL) { } else if (state == STATE_FALL) {
} else { } else {
drawTime(g); drawScore(g);
} }
timing.TimeToScore();
// 绘制矩形 // 绘制矩形
// g.setColor(Color.black); // g.setColor(Color.black);
// g.drawRect((int) birdRect.getX(), (int) birdRect.getY(), (int) birdRect.getWidth(), (int) birdRect.getHeight()); // g.drawRect((int) birdRect.getX(), (int) birdRect.getY(), (int) birdRect.getWidth(), (int) birdRect.getHeight());
...@@ -116,7 +114,7 @@ public class Bird { ...@@ -116,7 +114,7 @@ public class Bird {
} }
// 小鸟的飞行逻辑 // 小鸟的飞行逻辑
private void Fly() { private void fly() {
// 翅膀状态,实现小鸟振翅飞行 // 翅膀状态,实现小鸟振翅飞行
wingState++; wingState++;
image = birdImgs[state > STATE_FALL ? STATE_FALL : state][wingState / 10 % IMG_COUNT]; image = birdImgs[state > STATE_FALL ? STATE_FALL : state][wingState / 10 % IMG_COUNT];
...@@ -126,8 +124,6 @@ public class Bird { ...@@ -126,8 +124,6 @@ public class Bird {
break; break;
case STATE_UP: case STATE_UP:
// 控制上边界
break; break;
case STATE_DOWN: case STATE_DOWN:
...@@ -136,7 +132,13 @@ public class Bird { ...@@ -136,7 +132,13 @@ public class Bird {
h = speed * T - g * T * T / 2; h = speed * T - g * T * T / 2;
y = (int) (y - h); y = (int) (y - h);
birdRect.y = (int) (birdRect.y - h); birdRect.y = (int) (birdRect.y - h);
// 控制边界,死亡条件 // 控制坠落的边界,若y坐标 > 窗口的高度 - 地面的高度 - 小鸟图片的高度则死亡
if (birdRect.y >= Constant.FRAME_HEIGHT - Constant.GROUND_HEIGHT - (birdImgs[state][0].getHeight() >> 1)) {
y = Constant.FRAME_HEIGHT - Constant.GROUND_HEIGHT - (birdImgs[state][0].getHeight() >> 1);
birdRect.y = Constant.FRAME_HEIGHT - Constant.GROUND_HEIGHT - (birdImgs[state][0].getHeight() >> 1);
birdFall();
}
break; break;
case STATE_FALL: case STATE_FALL:
...@@ -146,13 +148,14 @@ public class Bird { ...@@ -146,13 +148,14 @@ public class Bird {
y = (int) (y - h); y = (int) (y - h);
birdRect.y = (int) (birdRect.y - h); birdRect.y = (int) (birdRect.y - h);
// 控制坠落的边界 // 控制坠落的边界,若y坐标 > 窗口的高度 - 地面的高度 - 小鸟图片的高度则死亡
if (birdRect.y > Constant.FRAME_HEIGHT - Constant.GROUND_HEIGHT - (birdImgs[state][0].getHeight() >> 1)) { if (birdRect.y >= Constant.FRAME_HEIGHT - Constant.GROUND_HEIGHT - (birdImgs[state][0].getHeight() >> 1)) {
y = Constant.FRAME_HEIGHT - Constant.GROUND_HEIGHT - (birdImgs[state][0].getHeight() >> 1); y = Constant.FRAME_HEIGHT - Constant.GROUND_HEIGHT - (birdImgs[state][0].getHeight() >> 1);
birdRect.y = Constant.FRAME_HEIGHT - Constant.GROUND_HEIGHT - (birdImgs[state][0].getHeight() >> 1); birdRect.y = Constant.FRAME_HEIGHT - Constant.GROUND_HEIGHT - (birdImgs[state][0].getHeight() >> 1);
GameFrame.setGameState(GameFrame.STATE_OVER); GameFrame.setGameState(GameFrame.STATE_OVER); // 改变游戏状态
BirdDead(); birdDead();
} }
break; break;
...@@ -166,38 +169,42 @@ public class Bird { ...@@ -166,38 +169,42 @@ public class Bird {
y = -1 * Constant.TOP_PIPE_LENGTHENING / 2; y = -1 * Constant.TOP_PIPE_LENGTHENING / 2;
} }
// 控制下方边界
if (birdRect.y > Constant.FRAME_HEIGHT - Constant.GROUND_HEIGHT - (image.getHeight() >> 1)) { if (birdRect.y > Constant.FRAME_HEIGHT - Constant.GROUND_HEIGHT - (image.getHeight() >> 1)) {
BirdFall(); birdFall();
} }
} }
// 鸟的状态 // 小鸟振翅
public void BirdUp() { public void birdUp() {
if (keyIsReleased()) { // 如果按键已释放 if (keyIsReleased()) { // 如果按键已释放
if (state == STATE_DEAD || state == STATE_FALL || state == STATE_UP) if (state == STATE_DEAD || state == STATE_UP || state == STATE_FALL)
return; return; // 小鸟死亡或坠落时返回
state = STATE_UP;
speed = SPEED_UP;
MusicUtil.playFly(); // 播放音效 MusicUtil.playFly(); // 播放音效
state = STATE_UP;
speed = SPEED_UP; // 每次振翅将速度改为上升速度
wingState = 0; // 重置翅膀状态 wingState = 0; // 重置翅膀状态
keyPressed(); keyPressed();
} }
} }
public void BirdDown() { // 小鸟下降
public void birdDown() {
if (state == STATE_DEAD || state == STATE_FALL) if (state == STATE_DEAD || state == STATE_FALL)
return; return; // 小鸟死亡或坠落时返回
state = STATE_DOWN; state = STATE_DOWN;
} }
public void BirdFall() { // 小鸟坠落(已死)
public void birdFall() {
state = STATE_FALL; state = STATE_FALL;
MusicUtil.playCrash(); // 播放音效 MusicUtil.playCrash(); // 播放音效
// 结束计时 // 结束计时
timing.endTiming(); timing.endTiming();
} }
public void BirdDead() { // 小鸟死亡
public void birdDead() {
state = STATE_DEAD; state = STATE_DEAD;
// 加载游戏结束的资源 // 加载游戏结束的资源
if (overImg == null) { if (overImg == null) {
...@@ -218,10 +225,9 @@ public class Bird { ...@@ -218,10 +225,9 @@ public class Bird {
} }
// 绘制实时分数 // 绘制实时分数
private void drawTime(Graphics g) { private void drawScore(Graphics g) {
g.setColor(Color.white); g.setColor(Color.white);
g.setFont(Constant.TIME_FONT); g.setFont(Constant.TIME_FONT);
// String str = Long.toString(timing.getTimeInSeconds()); 时间分数
String str = Long.toString(timing.TimeToScore()); String str = Long.toString(timing.TimeToScore());
int x = Constant.FRAME_WIDTH - GameUtil.getStringWidth(Constant.TIME_FONT, str) >> 1; int x = Constant.FRAME_WIDTH - GameUtil.getStringWidth(Constant.TIME_FONT, str) >> 1;
g.drawString(str, x, Constant.FRAME_HEIGHT / 10); g.drawString(str, x, Constant.FRAME_HEIGHT / 10);
...@@ -230,8 +236,8 @@ public class Bird { ...@@ -230,8 +236,8 @@ public class Bird {
private static final int SCORE_LOCATE = 5; // 位置补偿参数 private static final int SCORE_LOCATE = 5; // 位置补偿参数
private int flash = 0; // 图片闪烁参数 private int flash = 0; // 图片闪烁参数
// 绘制游戏结束的显示
// 绘制游戏结束的显示
private void drawGameover(Graphics g) { private void drawGameover(Graphics g) {
// 绘制结束标志 // 绘制结束标志
int x = Constant.FRAME_WIDTH - overImg.getWidth() >> 1; int x = Constant.FRAME_WIDTH - overImg.getWidth() >> 1;
......
...@@ -6,7 +6,7 @@ import java.awt.image.BufferedImage; ...@@ -6,7 +6,7 @@ import java.awt.image.BufferedImage;
import com.bird.util.Constant; import com.bird.util.Constant;
/** /**
* 云朵类,在屏幕的上半部飘动 * 云朵类
* *
* @author Kingyu * @author Kingyu
* *
......
...@@ -99,23 +99,23 @@ public class GameElementLayer { ...@@ -99,23 +99,23 @@ public class GameElementLayer {
e.printStackTrace(); e.printStackTrace();
} }
} }
} }
} }
} }
/** /**
* 添加普通水管 * 添加普通水管
* *
* @param lastPipe * @param lastPipe 传入最后一根水管以获取x坐标
*/ */
private void addNormalPipe(Pipe lastPipe) { private void addNormalPipe(Pipe lastPipe) {
int topHeight = GameUtil.getRandomNumber(MIN_HEIGHT, MAX_HEIGHT + 1); // 随机生成水管高度 int topHeight = GameUtil.getRandomNumber(MIN_HEIGHT, MAX_HEIGHT + 1); // 随机生成水管高度
int x = lastPipe.getX() + HORIZONTAL_INTERVAL; // 新水管的x坐标 = 最后一对水管的x坐标 + 水管的间隔 int x = lastPipe.getX() + HORIZONTAL_INTERVAL; // 新水管的x坐标 = 最后一对水管的x坐标 + 水管的间隔
// 概率生成移动的水管 Pipe top = PipePool.get("Pipe"); //从水管对象池中获取对象
Pipe top = PipePool.get("Pipe"); //设置x, y, height, type属性
top.setAttribute(x, -Constant.TOP_PIPE_LENGTHENING, topHeight + Constant.TOP_PIPE_LENGTHENING, top.setAttribute(x, -Constant.TOP_PIPE_LENGTHENING, topHeight + Constant.TOP_PIPE_LENGTHENING,
Pipe.TYPE_TOP_NORMAL, true); Pipe.TYPE_TOP_NORMAL, true);
...@@ -215,13 +215,12 @@ public class GameElementLayer { ...@@ -215,13 +215,12 @@ public class GameElementLayer {
if (bird.isDead()) { if (bird.isDead()) {
return false; return false;
} }
// 遍历水管容器 // 遍历水管容器
for (int i = 0; i < pipes.size(); i++) { for (int i = 0; i < pipes.size(); i++) {
Pipe pipe = pipes.get(i); Pipe pipe = pipes.get(i);
// 判断碰撞矩形是否有交集 // 判断碰撞矩形是否有交集
if (pipe.getPipeRect().intersects(bird.getBirdRect())) { if (pipe.getPipeRect().intersects(bird.getBirdRect())) {
bird.BirdFall(); bird.birdFall(); //有交集则小鸟坠落
return true; return true;
} }
} }
......
...@@ -94,6 +94,5 @@ public class GameForeground { ...@@ -94,6 +94,5 @@ public class GameForeground {
* (Exception e) { e.printStackTrace(); } * (Exception e) { e.printStackTrace(); }
*/ */
} }
} }
} }
\ No newline at end of file
package com.bird.main; package com.bird.main;
import com.bird.util.MusicUtil;
import static com.bird.util.Constant.FRAME_HEIGHT; import static com.bird.util.Constant.FRAME_HEIGHT;
import static com.bird.util.Constant.FRAME_WIDTH; import static com.bird.util.Constant.FRAME_WIDTH;
import static com.bird.util.Constant.FRAME_X; import static com.bird.util.Constant.FRAME_X;
...@@ -15,7 +17,6 @@ import java.awt.event.WindowAdapter; ...@@ -15,7 +17,6 @@ import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent; import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import com.bird.util.MusicUtil;
/** /**
* 主窗口类,游戏窗口和绘制的相关内容 * 主窗口类,游戏窗口和绘制的相关内容
...@@ -69,31 +70,34 @@ public class GameFrame extends Frame implements Runnable { ...@@ -69,31 +70,34 @@ public class GameFrame extends Frame implements Runnable {
switch (gameState) { switch (gameState) {
case STATE_READY: case STATE_READY:
if (keycode == KeyEvent.VK_SPACE) { if (keycode == KeyEvent.VK_SPACE) {
bird.BirdUp(); // 游戏启动界面时按下空格,小鸟振翅一次并开始受重力影响
bird.BirdDown(); bird.birdUp();
setGameState(STATE_START); bird.birdDown();
bird.startTiming(); setGameState(STATE_START); // 游戏状态改变
bird.startTiming(); // 计时器开始计时
} }
break; break;
case STATE_START: case STATE_START:
if (keycode == KeyEvent.VK_SPACE) { if (keycode == KeyEvent.VK_SPACE) {
bird.BirdUp(); //游戏过程中按下空格则振翅一次,并持续受重力影响
bird.BirdDown(); bird.birdUp();
bird.birdDown();
} }
break; break;
case STATE_OVER: case STATE_OVER:
if (keycode == KeyEvent.VK_SPACE) { if (keycode == KeyEvent.VK_SPACE) {
//游戏结束时按下空格,重新开始游戏
resetGame(); resetGame();
} }
break; break;
} }
} }
// 定义重新开始游戏的方法
// 重新开始游戏
private void resetGame() { private void resetGame() {
setGameState(STATE_READY); setGameState(STATE_READY);
gameElement.reset(); gameElement.reset();
bird.reset(); bird.reset();
} }
// 按键松开,更改按键状态标志 // 按键松开,更改按键状态标志
...@@ -114,30 +118,26 @@ public class GameFrame extends Frame implements Runnable { ...@@ -114,30 +118,26 @@ public class GameFrame extends Frame implements Runnable {
gameElement = new GameElementLayer(); gameElement = new GameElementLayer();
foreground = new GameForeground(); foreground = new GameForeground();
ready = new GameReady(); ready = new GameReady();
MusicUtil.load(); // 装载音乐资源
bird = new Bird(); bird = new Bird();
MusicUtil.load();
setGameState(STATE_READY); setGameState(STATE_READY);
// 启动用于刷新窗口的线程 // 启动用于刷新窗口的线程
new Thread(this).start(); new Thread(this).start();
} }
// 项目中存在两个线程:系统线程,自定义的线程:调用repaint()。
// 系统线程:屏幕内容的绘制,窗口事件的监听与处理 // 系统线程:屏幕内容的绘制,窗口事件的监听与处理
// 项目中存在两个线程:系统线程;自定义线程,调用repaint()。
// 两个线程会抢夺系统资源,可能会出现一次刷新周期所绘制的内容,并没有在一次刷新周期内完成 // 两个线程会抢夺系统资源,可能会出现一次刷新周期所绘制的内容,并没有在一次刷新周期内完成
// (双缓冲)单独定义一张图片,将需要绘制的内容绘制到这张图片,再一次性地将图片绘制到窗口 // (双缓冲)单独定义一张图片,将需要绘制的内容绘制到这张图片,再一次性地将图片绘制到窗口
private BufferedImage bufImg = new BufferedImage(FRAME_WIDTH, FRAME_HEIGHT, BufferedImage.TYPE_4BYTE_ABGR); private BufferedImage bufImg = new BufferedImage(FRAME_WIDTH, FRAME_HEIGHT, BufferedImage.TYPE_4BYTE_ABGR);
/** /**
* 绘制需要屏幕内容 当repaint()方法被调用时,JVM会调用update() 不要主动调用update 参数g是系统提供的画笔,由系统进行实例化 * 绘制游戏内容 当repaint()方法被调用时,JVM会调用update(),参数g是系统提供的画笔,由系统进行实例化
*
* 单独启动一个线程,不断地快速调用repaint(),让系统对整个窗口进行重绘 * 单独启动一个线程,不断地快速调用repaint(),让系统对整个窗口进行重绘
* *
*/ */
public void update(Graphics g) { public void update(Graphics g) {
Graphics bufG = bufImg.getGraphics(); // 获得图片画笔 Graphics bufG = bufImg.getGraphics(); // 获得图片画笔
// 使用图片画笔将需要绘制的内容绘制到图片 // 使用图片画笔将需要绘制的内容绘制到图片
...@@ -166,10 +166,11 @@ public class GameFrame extends Frame implements Runnable { ...@@ -166,10 +166,11 @@ public class GameFrame extends Frame implements Runnable {
} }
} }
//获取、设置游戏状态的方法 // 获取、设置游戏状态的方法
public static int getGameState() { public static int getGameState() {
return gameState; return gameState;
} }
public static void setGameState(int gameState) { public static void setGameState(int gameState) {
GameFrame.gameState = gameState; GameFrame.gameState = gameState;
} }
......
...@@ -7,7 +7,7 @@ import com.bird.util.Constant; ...@@ -7,7 +7,7 @@ import com.bird.util.Constant;
import com.bird.util.GameUtil; import com.bird.util.GameUtil;
/** /**
* 游戏未开始的内容 * 游戏启动界面类
* *
* @author Kingyu * @author Kingyu
* *
...@@ -17,26 +17,30 @@ public class GameReady { ...@@ -17,26 +17,30 @@ public class GameReady {
private BufferedImage titleImg; private BufferedImage titleImg;
private BufferedImage noticeImg; private BufferedImage noticeImg;
private int flash; private int flash; // 图像闪烁参数
// 构造器中进行初始化,装载图像资源
public GameReady() { public GameReady() {
titleImg = GameUtil.loadBUfferedImage(Constant.TITLE_IMG_PATH); titleImg = GameUtil.loadBUfferedImage(Constant.TITLE_IMG_PATH);
noticeImg = GameUtil.loadBUfferedImage(Constant.NOTICE_IMG_PATH); noticeImg = GameUtil.loadBUfferedImage(Constant.NOTICE_IMG_PATH);
} }
public void draw(Graphics g) { public void draw(Graphics g) {
int x = Constant.FRAME_WIDTH - titleImg.getWidth() >> 1; // 计算title图像的x、y坐标
int y = Constant.FRAME_HEIGHT / 5 << 1; int x = Constant.FRAME_WIDTH - titleImg.getWidth() >> 1; //x坐标为窗口中央
g.drawImage(titleImg, x, y, null); int y = Constant.FRAME_HEIGHT / 3; //y坐标为游戏窗口的1/3处
g.drawImage(titleImg, x, y, null); // 绘制
final int COUNT = 30; // 使notice的图像闪烁
final int COUNT = 30; // 闪烁周期
if (flash++ > COUNT) { if (flash++ > COUNT) {
// 计算notice图像的x、y坐标
x = Constant.FRAME_WIDTH - noticeImg.getWidth() >> 1; x = Constant.FRAME_WIDTH - noticeImg.getWidth() >> 1;
y = Constant.FRAME_HEIGHT / 5 * 3; y = Constant.FRAME_HEIGHT / 5 * 3;
g.drawImage(noticeImg, x, y, null); g.drawImage(noticeImg, x, y, null); // 绘制
if (flash == COUNT * 2) if (flash == COUNT * 2) // 重置闪烁参数
flash = 0; flash = 0;
} }
} }
} }
...@@ -10,7 +10,7 @@ import com.bird.util.Constant; ...@@ -10,7 +10,7 @@ import com.bird.util.Constant;
import com.bird.util.MusicUtil; import com.bird.util.MusicUtil;
/** /**
* 游戏计时类,单例类,方便调用 * 游戏计时类, 单例类,方便调用
* *
* @author Kingyu * @author Kingyu
* *
...@@ -31,7 +31,6 @@ public class GameTime { ...@@ -31,7 +31,6 @@ public class GameTime {
private GameTime() { private GameTime() {
timeState = STATE_READY; timeState = STATE_READY;
bestScore = -1; bestScore = -1;
try { try {
loadBestTime(); loadBestTime();
} catch (Exception e) { } catch (Exception e) {
...@@ -54,7 +53,7 @@ public class GameTime { ...@@ -54,7 +53,7 @@ public class GameTime {
} }
// 保存最高纪录 // 保存最高纪录
public void saveBestTime(long time) throws Exception { public void saveBestScore(long time) throws Exception {
File file = new File(Constant.SCORE_FILE_PATH); File file = new File(Constant.SCORE_FILE_PATH);
DataOutputStream dos = new DataOutputStream(new FileOutputStream(file)); DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));
dos.writeLong(time); dos.writeLong(time);
...@@ -104,7 +103,7 @@ public class GameTime { ...@@ -104,7 +103,7 @@ public class GameTime {
timeState = STATE_START; timeState = STATE_START;
} }
// 结束计时 // 结束计时并判断是否保存记录
public void endTiming() { public void endTiming() {
endTime = System.currentTimeMillis(); endTime = System.currentTimeMillis();
timeState = STATE_OVER; timeState = STATE_OVER;
...@@ -113,21 +112,21 @@ public class GameTime { ...@@ -113,21 +112,21 @@ public class GameTime {
if (bestScore < score) if (bestScore < score)
bestScore = score; bestScore = score;
try { try {
saveBestTime(bestScore); saveBestScore(bestScore);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
private static final int FIRST_SCORE_TIME = 6600; // 从游戏开始到通过第一根水管的所需时间 private static final int FIRST_SCORE_TIME = 6600; // 从游戏开始到通过第一根水管的所需时间
private static final int PER_SCORE_TIME = 2880; // 通过后续每一根水管的间隔的所需时间 private static final int PER_SCORE_TIME = 2900; // 通过后续每一根水管的间隔的所需时间
//将游戏时间转换为通过水管的数量 //将游戏时间转换为通过水管的数量
public long TimeToScore() { public long TimeToScore() {
long time = getTime(); long time = getTime();
long temp = score; long temp = score;
if (time >= FIRST_SCORE_TIME && time < FIRST_SCORE_TIME + PER_SCORE_TIME) { if (time >= FIRST_SCORE_TIME && time < FIRST_SCORE_TIME + PER_SCORE_TIME) {
score = 1; score = 1; //time大于FIRST_SCORE_TIME且未到第二对水管
} else if (time >= FIRST_SCORE_TIME + PER_SCORE_TIME) { } else if (time >= FIRST_SCORE_TIME + PER_SCORE_TIME) {
score = (int) (time - FIRST_SCORE_TIME) / PER_SCORE_TIME + 1; score = (int) (time - FIRST_SCORE_TIME) / PER_SCORE_TIME + 1;
} }
......
package com.bird.main; package com.bird.main;
import java.awt.Color;
import java.awt.Graphics; import java.awt.Graphics;
import com.bird.util.Constant; import com.bird.util.Constant;
...@@ -70,8 +69,8 @@ public class MovingPipe extends Pipe { ...@@ -70,8 +69,8 @@ public class MovingPipe extends Pipe {
pipeLogic(); pipeLogic();
// 绘制碰撞矩形 // 绘制碰撞矩形
g.setColor(Color.black); // g.setColor(Color.black);
g.drawRect((int) pipeRect.getX(), (int) pipeRect.getY(), (int) pipeRect.getWidth(), (int) pipeRect.getHeight()); // g.drawRect((int) pipeRect.getX(), (int) pipeRect.getY(), (int) pipeRect.getWidth(), (int) pipeRect.getHeight());
} }
// 绘制移动的悬浮水管 // 绘制移动的悬浮水管
...@@ -89,7 +88,7 @@ public class MovingPipe extends Pipe { ...@@ -89,7 +88,7 @@ public class MovingPipe extends Pipe {
g.drawImage(imgs[1], x - ((PIPE_HEAD_WIDTH - width) >> 1), y + dealtY, null); g.drawImage(imgs[1], x - ((PIPE_HEAD_WIDTH - width) >> 1), y + dealtY, null);
} }
// 绘制从上往下的普通水管 // 绘制从上往下的移动水管
private void drawTopHard(Graphics g) { private void drawTopHard(Graphics g) {
// 拼接的个数 // 拼接的个数
int count = (height - PIPE_HEAD_HEIGHT) / PIPE_HEIGHT + 1; // 取整+1 int count = (height - PIPE_HEAD_HEIGHT) / PIPE_HEIGHT + 1; // 取整+1
...@@ -102,7 +101,7 @@ public class MovingPipe extends Pipe { ...@@ -102,7 +101,7 @@ public class MovingPipe extends Pipe {
height - Constant.TOP_PIPE_LENGTHENING - PIPE_HEAD_HEIGHT + dealtY, null); height - Constant.TOP_PIPE_LENGTHENING - PIPE_HEAD_HEIGHT + dealtY, null);
} }
// 绘制从下往上的普通水管 // 绘制从下往上的移动水管
private void drawBottomHard(Graphics g) { private void drawBottomHard(Graphics g) {
// 拼接的个数 // 拼接的个数
int count = (height - PIPE_HEAD_HEIGHT) / PIPE_HEIGHT + 1; int count = (height - PIPE_HEAD_HEIGHT) / PIPE_HEIGHT + 1;
...@@ -118,13 +117,14 @@ public class MovingPipe extends Pipe { ...@@ -118,13 +117,14 @@ public class MovingPipe extends Pipe {
* 可动水管的运动逻辑 * 可动水管的运动逻辑
*/ */
private void pipeLogic() { private void pipeLogic() {
//x坐标的运动逻辑与普通水管相同
x -= speed; x -= speed;
pipeRect.x -= speed; pipeRect.x -= speed;
if (x < -1 * PIPE_HEAD_WIDTH) {// 水管完全离开了窗口 if (x < -1 * PIPE_HEAD_WIDTH) {// 水管完全离开了窗口
visible = false; visible = false;
} }
//水管上下移动 //水管上下移动的逻辑
if (dir == DIR_DOWN) { if (dir == DIR_DOWN) {
dealtY++; dealtY++;
if (dealtY > MAX_DEALY) { if (dealtY > MAX_DEALY) {
...@@ -137,6 +137,6 @@ public class MovingPipe extends Pipe { ...@@ -137,6 +137,6 @@ public class MovingPipe extends Pipe {
} }
} }
pipeRect.y = this.y + dealtY; pipeRect.y = this.y + dealtY;
} }
} }
...@@ -105,8 +105,7 @@ public class Pipe { ...@@ -105,8 +105,7 @@ public class Pipe {
break; break;
case TYPE_HOVER_NORMAL: case TYPE_HOVER_NORMAL:
drawHoverNormal(g); drawHoverNormal(g);
break; break;
} }
// //绘制碰撞矩形 // //绘制碰撞矩形
// g.setColor(Color.black); // g.setColor(Color.black);
......
...@@ -32,7 +32,7 @@ public class PipePool { ...@@ -32,7 +32,7 @@ public class PipePool {
/** /**
* 从对象池中获取一个对象 * 从对象池中获取一个对象
* *
* @return * @return 传入对象的类型,以判断从哪个对象池中获取
*/ */
public static Pipe get(String className) { public static Pipe get(String className) {
if ("Pipe".equals(className)) { if ("Pipe".equals(className)) {
......
...@@ -15,7 +15,7 @@ public class Constant { ...@@ -15,7 +15,7 @@ public class Constant {
public static final int FRAME_HEIGHT = 640; public static final int FRAME_HEIGHT = 640;
// 游戏标题 // 游戏标题
public static final String GAME_TITLE = "Flappy Bird"; public static final String GAME_TITLE = "Flappy Bird written by Kingyu";
// 窗口位置 // 窗口位置
public static final int FRAME_X = 1200; public static final int FRAME_X = 1200;
......
...@@ -18,7 +18,7 @@ import javax.imageio.ImageIO; ...@@ -18,7 +18,7 @@ import javax.imageio.ImageIO;
public class GameUtil { public class GameUtil {
private GameUtil() { private GameUtil() {
} // 私有化,不让其他类实例化 } // 私有化,防止其他类实例化
/** /**
* 装载图片的方法 * 装载图片的方法
......
...@@ -7,9 +7,9 @@ import java.net.MalformedURLException; ...@@ -7,9 +7,9 @@ import java.net.MalformedURLException;
/** /**
* 音乐工具类 * 音乐工具类
* *
* @author Kingyu wav音频:JDK提供的类可直接解码 mp3音频:JDK没有提供支持,需要使用第三方的工具包 * @author Kingyu wav音频:JDK提供的类可直接解码 mp3音频:JDK没有提供支持,需要使用第三方的工具包
* *
*/ */
public class MusicUtil { public class MusicUtil {
...@@ -28,16 +28,16 @@ public class MusicUtil { ...@@ -28,16 +28,16 @@ public class MusicUtil {
e.printStackTrace(); e.printStackTrace();
} }
} }
//wav播放 //wav播放
public static void playFly() { public static void playFly() {
fly.play(); fly.play();
} }
public static void playCrash() { public static void playCrash() {
crash.play(); crash.play();
} }
public static void playScore() { public static void playScore() {
score.play(); score.play();
} }
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment