Commit d7b64a56 authored by kingyu's avatar kingyu Committed by kingyuluk
Browse files

feat: 具备原版的游戏功能

parent b9468532
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
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
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;
}
}
}
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;
}
}
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
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);
}
}
}
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
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();
}
}
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();
}
}
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