java小游戏 : 飞翔的小鸟 (完整版)
Posted 壹个人的火锅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java小游戏 : 飞翔的小鸟 (完整版)相关的知识,希望对你有一定的参考价值。
前言
飞翔的小鸟 小游戏 可以作为 java入门阶段的收尾作品 ;
需要掌握 面向对象的使用以及了解 多线程,IO流,异常处理,一些java基础等相关知识。
一 、游戏分析
1. 分析游戏逻辑
(1)先让窗口显示出来,然后绘制 游戏的背景
(2)让小鸟显示在屏幕中,并且可以上下飞
(3)屏幕出现闪烁,解决闪烁问题 : 用双缓冲,就是将内容绘制到 一张图片上,然后再显示出来
(4)让障碍物显示出来,并且让障碍物可以移动起来
(5)碰撞检测
(6)绘制刚开始的页面和死亡后的页面
(7)让 障碍物 动起来
2.代码分析
(1)app 包 :(游戏启动类)
GameApp 类 作为 游戏启动类 。
(2)main 包 : (游戏主要类)
① Bird 类
② Barrier 类
③ BarrierPool 类
④ GameBackGround 类
⑤ GameBarrierLayer 类
⑥ GameFrame 类
⑦ GameReady 类
⑧ GameTime 类
(3)util 包 :(游戏工具类)
① Constant 类
② Gameutil 类
③ MusicUtil 类
二、代码展示 (每个类代码最上面都有该类的解释)
(1) app 包
GameApp 类
import main.GameFrame;
public class GameApp
public static void main(String[] args)
new GameFrame();
(2) main 包
① Bird 类
package main;
import util.Constant;
import util.GameUtil;
import util.MusicUtil;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
public class Bird
// 图片的张数
public static final int IMG_COUNT=8;
private Image[] imgs;
// 将 计时 的 方法 引入进来
private GameTime gameTime;
// 定义 记分牌 和 结束 时候的 图片
private BufferedImage over;
private BufferedImage score;
// 定义 一下 小鸟 位置 属性,即起始位置
private static int x;
private static int y;
// 定义 小鸟 状态的变量
private int state = STATE_NORMAL;
public static final int STATE_NORMAL = 0;
public static final int STATE_UP = 1;
public static final int STATE_DOWN = 2;
public static final int STATE_DIE = 3;
public static final int STATE_DIED = 4;
// 定义小鸟在 Y 轴 上的速度
private int dealtY;
// 小鸟 向上 飞的 最大 速度
public static final int MAX_UP_DEALTY=20;
// 小鸟 向下 飞的 最大 速度
public static final int MAX_DOWN_DEALTY=15;
public Rectangle getRect()
return rect;
// 给 小鸟 添加 小框 做碰撞测试
private Rectangle rect; // 设置 矩形 特定的 方法
public static final int RECT_DESC = 2; // 让小鸟矩形框 在原来的基础上 再减少 2, 让矩形框 再 小一点
// 对 小鸟 照片进行 初始化
public Bird()
imgs=new Image [IMG_COUNT];
gameTime=GameTime.getInstance();
for (int i=0;i<IMG_COUNT;i++) // 将小鸟 照片 存进这个数组
imgs[i]= GameUtil.loadBufferedImage(Constant.BIRDS_IMG_PATH[i]);
// 小鸟 初始位置 坐标,窗口的 中心点
this.x=Constant.FRAME_WIDTH >> 1 ;
this.y=Constant.FRAME_HEIGHT >> 1 ;
// 给 鸟 加上 小框框
int w = imgs[0].getWidth(null);
int h = imgs[0].getHeight(null);
int x=this.x-w/2;
int y=this.y-h/2;
rect = new Rectangle(x+RECT_DESC,y+RECT_DESC,w-RECT_DESC*2,h-RECT_DESC*2);
// 初始时候的 x坐标 y坐标 宽度 高度
// 画 小鸟 此时的 状态的
public void draw(Graphics g)
flyLogic();
if (gameTime.getSecondTime() < 10)
int index=state >STATE_DIE ?STATE_DIE :state; // 判断 小鸟 现在是 怎样的 状态
int halfImgW = imgs[index].getWidth(null) >> 1 ;
int halfImgH = imgs[index].getHeight(null) >> 1 ;
g.drawImage(imgs[index],x-halfImgW,y-halfImgH,null);
else
int index=state >STATE_DIE ?STATE_DIE :state; // 判断 小鸟 现在是 怎样的 状态
int halfImgW = imgs[index].getWidth(null) >> 1 ;
int halfImgH = imgs[index].getHeight(null) >> 1 ;
g.drawImage(imgs[index+4],x-halfImgW,y-halfImgH,null);
// 做碰撞测试用的,先 把鸟 加上 小框框
// g.setColor(Color.BLACK);
// g.drawRect( (int)rect.getX(), (int)rect.getY(), (int)rect.getWidth(), (int)rect.getHeight());
// 开始 绘制 游戏结束 和 记分牌
if (state == STATE_DIED)
drawGameOver(g);
else // 此时 小鸟 还没有死亡,需要 绘制 计时器
drawTime(g);
// 绘制 游戏 结束 时候 的画面 的 方法
private void drawGameOver(Graphics g)
// 绘制 over 的 牌子
int x =Constant.FRAME_WIDTH-over.getWidth() >>1 ;
int y =Constant.FRAME_HEIGHT/3;
g.drawImage(over,x,y,null);
// 绘制 score 的牌子
x=Constant.FRAME_WIDTH-score.getWidth() >>1;
y=Constant.FRAME_HEIGHT/2;
g.drawImage(score,x,y,null);
// 将 最终 的 分数 绘制 在 记分牌上
g.setFont(Constant.TIME_FONT); // 设置字体属性
g.setColor(Color.WHITE); // 设置 颜色
g.drawString(Long.toString(gameTime.getSecondTime()),480,500); // 这里的 x 和 y 是 分数 最终 的显示 位置
// 绘制 游戏 上方 的 计时器
private void drawTime(Graphics g)
// 显示 小鸟的飞行时间
g.setFont(Constant.TIME_FONT);
g.setColor(Color.WHITE);
g.drawString(Long.toString(gameTime.getSecondTime()),500,88);
// 按空格 调用 这个方法 fly()和down() 主要作用是 改变小鸟上下飞的速度
public void fly()
// 状态 只 改变一次
if (state == STATE_UP || state == STATE_DIE || state == STATE_DIED)
return;
// 开始 计时
if(gameTime.isReady())
// MusicUtil.playFly(); // 调用 音乐
gameTime.startTiming();
// MusicUtil.tiao();
state=STATE_UP;
dealtY=0;
public void down()
if (state == STATE_DOWN || state == STATE_DIE || state==STATE_DIED)
return;
state=STATE_DOWN;
dealtY=0;
// 改变 小鸟 在 y 轴 上的 位移
private void flyLogic ()
switch (state)
case STATE_NORMAL : break;
case STATE_UP :
dealtY+=3;
if (dealtY > MAX_UP_DEALTY)
dealtY = MAX_UP_DEALTY;
y -= dealtY;
rect.y -= dealtY;
// 撞上 游戏上侧 则死亡
if (y < (imgs[state].getHeight(null) >> 1)+ Constant.TOP_BAR_HEIGHT)
die();
break;
case STATE_DOWN :
dealtY+=2;
if (dealtY > MAX_DOWN_DEALTY)
dealtY = MAX_DOWN_DEALTY;
y+=dealtY;
rect.y += dealtY;
// 撞上 游戏下侧 则死亡
if (y > Constant.FRAME_HEIGHT-(imgs[state].getHeight(null) >> 1))
die();
break;
case STATE_DIE : // 死亡 下路 过程
dealtY++;
if (dealtY > MAX_DOWN_DEALTY)
dealtY = MAX_DOWN_DEALTY;
y+=dealtY;
// 最后 静止 在 游戏框 下侧
if (y > Constant.FRAME_HEIGHT-(imgs[state].getHeight(null) >> 1))
y=Constant.FRAME_HEIGHT-(imgs[state].getHeight(null) >> 1);
died();
break;
case STATE_DIED :
break;
// 重置 小鸟
public void reset()
state=STATE_NORMAL;
dealtY=0;
x=Constant.FRAME_WIDTH >> 1 ;
y=Constant.FRAME_HEIGHT >> 1 ;
int h=imgs[0].getHeight(null);
rect.y=this.y-h/2+RECT_DESC;
gameTime.reset();
// 小鸟 死亡 的过程
public void die()
state=STATE_DIE;
// 结束 计时
gameTime.endTiming();
// 加载 游戏结束 时候的 资源
over=GameUtil.loadBufferedImage(Constant.OVER_IMG_PATH);
score=GameUtil.loadBufferedImage(Constant.SCORE_IMG_PATH);
// 小鸟 死亡了
public void died()
state = STATE_DIED;
GameFrame.setGameState(GameFrame.STATE_OVER);
// 判断 小鸟 是否 死亡
public boolean isDied()
return state==STATE_DIED;
②Barrier 类
/*
这个类里面 绘制了 四种 类型的 障碍物
设置了 三大 类型的 障碍物 绘制的 方法
给 悬浮在 中间的 障碍物 添加 逻辑,使其 能 移动
设置了 所有 障碍物的 属性
给 障碍物 添加了 矩形框
*/
import util.Constant;
import util.GameUtil;
import java.awt.*;
import java.awt.image.BufferedImage;
public class Barrier
private static BufferedImage [] imgs;
static // 静态代码块,最先被执行
final int COUNT = 3;
imgs = new BufferedImage[COUNT];
for (int i=0;i<COUNT;i++) // 照片赋值
imgs[i]= GameUtil.loadBufferedImage(Constant.BARRIERS_IMG_PATH[i]);
// 障碍物 图片的 长宽
public static final int BARRIER_WIDTH = imgs[0].getWidth();
public static final int BARRIER_HEIGHT = imgs[0].getHeight();
public static final int BARRIER_HEAD_WIDTH = imgs[1].getWidth();
public static final int BARRIER_HEAD_HEIGHT = imgs[1].getHeight();
// 添加 障碍物 是否可见 状态 true 代表 可见
private boolean visible;
// 给 柱子 添加 小框框
private Rectangle rect;
// 这个 x,y 是 障碍物的坐标
private int x,y;
// 移动 障碍物 的坐标 在 y 轴一直运动 的坐标
private int dealtY;
public static final int MAX_DEALY =66; // 移动障碍物 下降的位移
private int width,height; // 一个正常 障碍物的 总长度和总宽度
// 移动的 障碍物 是否 在向下 移动
private boolean isDown = true;
// 障碍物 的移动 速度
private int speed=7;
// 障碍物 一共有 四种 情况
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 Barrier ()
this.width=BARRIER_WIDTH;
rect = new Rectangle();
rect.width=this.width;
// 选择 绘画 类型
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 :
case TYPE_HOVER_HARD :
drawHoverNormal(g);
break;
// 绘制 障碍物的 矩形块
// g.setColor(Color.RED);
// g.drawRect((int)rect.getX(),(int)rect.getY(),(int)rect.getWidth(),(int)rect.getHeight());
// 鸟 死亡之后,后面不会在出 障碍物
if (bird.isDied()) // 判断鸟是否死亡
return ; // 结束 该 方法 ,返回调用出 ,即 此时 不会在 绘制 障碍物了
logic();
/*
绘制 从上到下的 障碍物
*/
private void drawTopNormal(Graphics g)
// 绘制 上半部分 拼接 个 个数
int COUNT = (height-BARRIER_HEAD_HEIGHT)/BARRIER_HEIGHT;
for (int i = 0; i < COUNT; i++)
g.drawImage(imgs[0], x, y + i * BARRIER_HEIGHT + BARRIER_HEAD_HEIGHT, null);
// 绘制 障碍物 的头
int y=this.y+height-BARRIER_HEAD_HEIGHT;
g.drawImage(imgs[2],x-(BARRIER_HEAD_WIDTH-BARRIER_WIDTH),y,null);
/*
绘制 从下往上的 障碍物
*/
private void drawBottomNormal(Graphics g)
// 绘制 下半部分 拼接 个 个数
int count = (height-BARRIER_HEAD_HEIGHT)/BARRIER_HEIGHT +1 ;
for (int i = 0; i < count; i++)
g.drawImage(imgs[0], x, y + i * BARRIER_HEIGHT + BARRIER_HEAD_HEIGHT, null);
// 绘制 障碍物 的头
g.drawImage(imgs[1],x-(BARRIER_HEAD_WIDTH-BARRIER_WIDTH>>1),y,null);
/*
绘制 在 中间的 障碍物
*/
private void drawHoverNormal(Graphics g)
// 先绘制 上面的 头
g.drawImage(imgs[1],x-(BARRIER_HEAD_WIDTH-BARRIER_WIDTH>>1),y+dealtY,null);
// 绘制 中间的 部分
int count = (height - BARRIER_HEAD_HEIGHT*2)/BARRIER_HEIGHT+1;
for (int i = 0; i < count; i++)
g.drawImage(imgs[0], x, y + i * BARRIER_HEIGHT + BARRIER_HEAD_HEIGHT+dealtY, null);
// 绘制 下面的 头
int y=this.y+dealtY+height-BARRIER_HEAD_HEIGHT;
g.drawImage(imgs[2],x-(BARRIER_HEAD_WIDTH-BARRIER_WIDTH>>1),y,null);
/*
障碍物 的 逻辑 部分
*/
private void logic()
x -= speed;
rect.x -=speed;
// 障碍物 完全移除了 屏幕
if (x<-BARRIER_WIDTH)
visible =false;
// 给 移动的 障碍物 添加 逻辑
switch (type)
case TYPE_HOVER_HARD :
if (isDown) // 障碍物 下移
dealtY++;
if(dealtY > MAX_DEALY)
isDown=false;
else // 障碍物 上移
dealtY--;
if (dealtY == 0)
isDown=true;
rect.y=this.y+dealtY;
/*
判断 障碍物 是否 完全 进入到 游戏 窗口 中
即 此时柱子的x坐标加上柱子的宽度 如果小于 整个屏幕的宽度,那么 说明 障碍物完全出现在了 游戏窗口中
*/
public boolean isInFrame()
return x+BARRIER_WIDTH < Constant.FRAME_WIDTH;
public int getX() // 此时 该 障碍物的 X 轴的位置
return x;
// 返回 当前 障碍物的 x 坐标, 以方便控制 两个障碍物 之间的距离
public boolean isVisible()
return visible;
// 返回障碍物 是否可见,即是否还在 画面内
public Rectangle getRect()
return rect;
// 设置 障碍物的 属性 (x坐标, y坐标, 高度 , 类型, 是否可见)
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(x,y,height);
dealtY=0;
isDown=true;
// 设置 障碍物 矩形框 的 属性
public void setRectangle(int x,int y,int height)
rect.x=x;
rect.y=y;
rect.height=height;
③ BarrierPool 类
import java.util.ArrayList;
import java.util.List;
/*
* 障碍物的 对象池
* 为了 避免 反复 创建和 销毁 对象
*/
public class BarrierPool
static List<Barrier> pool =new ArrayList<Barrier>();
// 对象 池中 初始 的对象的个数
public static final int INIT_BARRIER_COUNT = 16;
// 最大个数
public static final int MAX_BARRIER_COUNT = 20;
static
// 初始化 池中的 对象
for (int i=0;i<INIT_BARRIER_COUNT;i++)
pool.add(new Barrier());
public static Barrier get()
int size = pool.size();
if (size>0)
return pool.remove(size-1);
else
// 池塘被 掏空,只能返回 一个新对象
return new Barrier();
public static void giveBack(Barrier barrier)
if (pool.size()<MAX_BARRIER_COUNT)
pool.add(barrier);
④ GameBackGround 类
/*
这个类里面 填充了 游戏页面你的 背景颜色
将 最下面 草地的 照片 添加进去
*/
import util.Constant;
import util.GameUtil;
import java.awt.*;
public class GameBackGround
private Image bkImg;
public GameBackGround()
bkImg=GameUtil.loadBufferedImage(Constant.BK_IMG_PATH);
public void draw(Graphics g)
g.setColor(Constant.BK_COLOR); // 设置 背景填充 颜色
g.fillRect(0,0,Constant.FRAME_WIDTH,Constant.FRAME_HEIGHT); // 这个是 完全 填充 背景颜色;
// 得到 游戏 地面 图片 的 大小
int imgW=bkImg.getWidth(null);
int imgH=bkImg.getHeight(null);
int count=Constant.FRAME_WIDTH/imgW+1; // 需要重复 叠加的 图片 个数
for (int i=0;i<count;i++)
g.drawImage(bkImg,imgW*i,Constant.FRAME_HEIGHT - imgH , null );
⑤ GameBarrierLayer 类
/*
绘制 障碍物
将 上下型 和 中间型 的 柱子 添加到 barriers 集合中
对 障碍物 和 小鸟 进行 碰撞测试
重新开始游戏,对 障碍物 进行 清空
*/
import util.Constant;
import util.GameUtil;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
public class GameBarrierLayer
// 障碍物 出现的 规则
public static final int TYPE1_VER_INTERVAL =Constant.FRAME_HEIGHT>>2; // 两个 上下 障碍物中间 空 的距离
public static final int TYPE1_MIN_HEIGHT =Constant.FRAME_HEIGHT>>3;
public static final int TYPE1_MAX_HEIGHT =(Constant.FRAME_HEIGHT>>3)*5;
private List<Barrier> barriers; // 障碍物的集合
public GameBarrierLayer()
barriers = new ArrayList<>();
public void draw(Graphics g,Bird bird)
for (int i=0;i<barriers.size();i++)
Barrier barrier=barriers.get(i);
if (barrier.isVisible()) // 障碍物 可见 则 画出来
barrier.draw(g,bird);
else // 不可见 则 移除 容器
Barrier remove = barriers.remove(i);
BarrierPool.giveBack(remove);
i--;
// 碰撞检测
collideBird(bird);
logic(bird);
// 障碍物 添加 逻辑
private void logic(Bird bird)
if (bird.isDied())
return;
if (barriers.size()==0) //设置 第一个 障碍物
int height = GameUtil.getRandomNumber(TYPE1_MIN_HEIGHT,TYPE1_MAX_HEIGHT); // 取 这个区间内的长度为 障碍物的 高度
int type = Barrier.TYPE_TOP_NORMAL; // 类型为 上半部分的 障碍物
Barrier top=BarrierPool.get(); // 从对象池里面获取
top.setAttribute(Constant.FRAME_WIDTH,0,height,type,true); // 设置 属性
// ( 起始的x坐标,最右端 , y坐标为0 ,高度 , 类型, 是否可见 )
type=Barrier.TYPE_BOTTOM_NORMAL; // 类型为 下半部分的 障碍物
Barrier bottom=BarrierPool.get(); // 从对象池里面获取 并 设置属性
bottom.setAttribute(Constant.FRAME_WIDTH,height+TYPE1_VER_INTERVAL,Constant.FRAME_HEIGHT-height-TYPE1_VER_INTERVAL,type,true);
// ( x坐标也是最右端 , y = 上半部分 + 中间的间距 , 高度 = 屏幕的高度 - 上半部分 - 中间间距 , 类型, 可见)
barriers.add(top); // 在 集合 里面 添加 上半部分
barriers.add(bottom); // 在 集合 里面 添加 下半部分
else // 设置 后面的 障碍物
// 判断 最后一个 障碍物 是否完全 进入到 游戏窗口 中
Barrier last=barriers.get(barriers.size()-1);
if (last.isInFrame()) // 此时说明障碍物完全出现在了屏幕中, 然后开始添加下一对
if (GameTime.getInstance().getSecondTime() < GameTime.HOVER_BARRIER_TIME)
addNormalBarrier(last); // 如果 游戏开始时间 < 规定的悬浮障碍物出现的时间 则 添加上下普通障碍物
else
try
if (GameUtil.isInAnyProbability(1,2))
addHoverBarrier(last); // 1/2 的概率 添加 悬浮 障碍物
else
addNormalBarrier(last); // 1/2 的概率 添加 上下 障碍物
catch (Exception e)
e.printStackTrace();
// 普通的 柱子
private void addNormalBarrier (Barrier last)
int x=last.getX()+180; // 这个地方是 控制 两个 障碍物的间隙的
// 其中 last.getX() 是判断上一个障碍物的 X轴 的位置
int height = GameUtil.getRandomNumber(TYPE1_MIN_HEIGHT,TYPE1_MAX_HEIGHT);
int type = Barrier.TYPE_TOP_NORMAL;
Barrier top=BarrierPool.get(); // 对对象池中取出
top.setAttribute(x,0,height,type,true);// 设置属性
type=Barrier.TYPE_BOTTOM_NORMAL;
Barrier bottom=BarrierPool.get(); // 从对象池中 取出
bottom.setAttribute(x,height+TYPE1_VER_INTERVAL,Constant.FRAME_HEIGHT-height-TYPE1_VER_INTERVAL,type,true); // 设置属性
barriers.add(top);
barriers.add(bottom);
// 中间悬浮柱子
private void addHoverBarrier(Barrier last)
int height = GameUtil.getRandomNumber(Constant.FRAME_HEIGHT/4,Constant.FRAME_WIDTH/3); // 随机高度
int type = Barrier.TYPE_HOVER_NORMAL;
try // 1/2 的概率 悬浮柱子能 移动 , 1/2 的概率 悬浮柱子 不能移动
type = GameUtil.isInAnyProbability(1,2)? Barrier.TYPE_HOVER_NORMAL:Barrier.TYPE_HOVER_HARD;
catch (Exception e)
e.printStackTrace();
int x=last.getX()+180; // 这个地方是 控制 两个 障碍物的间隙的
int y=GameUtil.getRandomNumber(Constant.FRAME_HEIGHT/3,Constant.FRAME_HEIGHT*3/8); // y轴 位置 随机
Barrier hover=BarrierPool.get();
hover.setAttribute(x,y,height,type,true);
barriers.add(hover);
/*
判断 障碍物 和 小鸟 是否 发生了碰撞
*/
public boolean collideBird(Bird bird)
for (int i=0;i<barriers.size();i++)
Barrier barrier=barriers.get(i);
if (barrier.getRect().intersects(bird.getRect())) // 这里的 intersects 是 jdk 里面的方法,判断 两者 是否有交集
bird.die();
return true;
return false;
// 重新开始游戏后, 重置障碍物
public void reset()
barriers.clear(); // 这个 clear 方法是 容器自带的功能
⑥ GameFrame 类
/*
初始化 游戏窗口
增加 按键响应
重新开始 游戏
初始化 游戏中 用到的 类
双缓冲 解决 游戏的 闪屏
多线程 提高 程序运行 效率
*/
import org.w3c.dom.ls.LSOutput;
import util.Constant;
import util.MusicUtil;
import static util.Constant.*;
import java.awt.*;
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 java.net.BindException;
public class GameFrame extends Frame implements Runnable
// 游戏 刚开始 的界面
private GameReady ready;
// 创建 一个 游戏 背景
private GameBackGround backGround;
// 创建 小鸟
private Bird bird;
// 创建 碰撞物 数据
private GameBarrierLayer barrierLayer;
// 游戏进行 状态 有 不用的 状态
private static int gameState;
public static final int STATE_READY = 0;
public static final int STATE_PLAYING = 1;
public static final int STATE_OVER =2;
public GameFrame()
initFrame();
initGame();
// 设置 窗口 属性 ( 初始化 窗口 )
public void initFrame()
//设置是否可见
setVisible(true);
// 设置 标题
setTitle("飞翔的小鸟");
// 设置 大小
setSize(FRAME_WIDTH,FRAME_HEIGHT);
// 设置 不可 自己 放大 或 缩小
setResizable(false);
// 设置 初始 位置
setLocation(FRAME_X,FRAME_Y);
// 初始化 开始界面
ready=new GameReady();
// 添加 窗口 关闭事件
addWindowListener(new WindowAdapter()
@Override
public void windowClosing(WindowEvent e)
// 结束程序
System.exit(0);
);
// 添加 按键 事件
addKeyListener(new MyKeyListener());
// 按键 感应 即 按空格 和 松开 空格 的方法
class MyKeyListener implements KeyListener
@Override
public void keyTyped(KeyEvent e)
@Override
public void keyPressed(KeyEvent e)
// 捕获 系统 传入的 按键虚拟值
int keyCode = e.getKeyCode();
switch (gameState)
case STATE_READY :
if (keyCode == KeyEvent.VK_P)
setGameState(STATE_PLAYING);
break;
case STATE_PLAYING :
if (keyCode == KeyEvent.VK_SPACE)
bird.fly();
break;
case STATE_OVER :
if (keyCode == KeyEvent.VK_O)
resetGame();
break;
@Override
public void keyReleased(KeyEvent e)
// 捕获 系统 传入的 按键虚拟值 (这个是 按键后 松开的状态)
int keyCode = e.getKeyCode();
switch (gameState)
case STATE_READY :
break;
case STATE_PLAYING :
if (keyCode == KeyEvent.VK_SPACE)
bird.down();
break;
// 重新开始 游戏
private void resetGame()
setGameState(STATE_READY);
barrierLayer.reset();
bird.reset();
// 对游戏 对象 进行 初始化
private void initGame()
backGround = new GameBackGround();
barrierLayer=new GameBarrierLayer();
bird = new Bird();
// 音乐资源
MusicUtil.load();
setGameState(STATE_READY); // 设置 默认值为 准备状态
// 启动多线程 , 用于刷新 窗口
new Thread(this).start();
/*
所有 需要 绘制的内容, 都需要 在此 方法 中 绘制
update 方式 是 jvm 调用的
该方法 绘制的 所有内容,在调用的时候,都会别 绘制到 Frame 上来。
update 何时 被 jvm 调用. 当 repaint 方法 被调用时 它 被调用
g 是 画笔 , 系统 自带的
*/
/*
用 双缓冲 解决 屏幕闪烁的问题
单独定义一张 图片,然后将 需要绘制 的内容,都绘制到 这张图片上来
然后一次性的 将 图片 绘制 到窗口 中
*/
// 这是单独定义的一张图片, 然后将需要 绘制的东西 先绘制到 这张图片上面,然后在 绘制到 窗口中
private BufferedImage bufImg = new BufferedImage(FRAME_WIDTH,FRAME_HEIGHT,BufferedImage.TYPE_4BYTE_ABGR_PRE);
@Override
public void update(Graphics g)
Graphics bufG = bufImg.getGraphics();
backGround.draw(bufG);
if (gameState==STATE_READY) // 游戏准备阶段
// 绘制 鸟
bird.draw(bufG);
ready.draw(bufG);
else // 对 游戏 中 阶段 进行一个 绘制
// 绘制 碰撞物
barrierLayer.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_INTERAVL); // 刷新率
catch (InterruptedException e)
e.printStackTrace();
// 设置 游戏进行的 状态的
public static void setGameState(int gameState)
GameFrame.gameState = gameState;
⑦ GameReady 类
/*
绘制 游戏的 开始界面
*/
import util.Constant;
import util.GameUtil;
import java.awt.*;
import java.awt.image.BufferedImage;
public class GameReady
private BufferedImage titleImg;
private BufferedImage noticeImg;
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 ; // 开始 牌的 x坐标
int y =Constant.FRAME_HEIGHT / 3; // 开始 牌的 y坐标
g.drawImage(titleImg,x,y,null); // fly_bird 的 牌子
x=Constant.FRAME_WIDTH-noticeImg.getWidth() >>1 ; // 牌子 下面 的东西
y=Constant.FRAME_HEIGHT /3 <<1;
g.drawImage(noticeImg,x,y,null); // press 的 牌子
⑧ GameTime 类
/*
设置 游戏上方 出现的 计时器
*/
public class GameTime
private static final GameTime GAME_TIME = new GameTime();
// 出现 悬浮障碍物的 时间
public static final int HOVER_BARRIER_TIME=5;
public static final int MOVING_BARRIER_TIME=12;
// 游戏时间 状态
private int timeState;
// 还没 开始 计时
public static final int STATE_READY =0;
// 开始 计时
public static final int STATE_RUN = 1;
// 结束 计时
public static final int STATE_OVER = 2;
// 游戏 的 计时 开始和结束
private long startTime,endTime;
private GameTime()
timeState = STATE_READY;
// 是否 准备好开始计时
public boolean isReady()
return timeState==STATE_READY;
// 开始 计时 的 方法
public void startTiming()
startTime=System.currentTimeMillis();// 获取 当前系统时间
timeState=STATE_RUN;
// 结束 计时 的 方法
public void endTiming()
endTime=System.currentTimeMillis(); // 获取 当前系统时间
timeState=STATE_OVER;
/*
游戏 用的 毫秒 来计时
*/
public long getTime()
if (timeState==STATE_RUN)
return System.currentTimeMillis()-startTime;// 游戏 运行时 的时间
return endTime-startTime; // 游戏 结束时 的时间
// 将游戏 用的 毫秒 转化 为秒 来计时
public long getSecondTime()
return getTime()/1000;
// 这个等于 返回 一个 对象,和 new 一个对象道理差不多
public static GameTime getInstance()
return GAME_TIME;
// 重新开始后 , 重置时间
public void reset()
timeState=STATE_READY;
startTime=0;
endTime=0;
(3) util 包
① Constant 类
import java.awt.*;
public class Constant
// 窗口 大小
public static final int FRAME_WIDTH=1000;
public static final int FRAME_HEIGHT=690;
// 窗口 初始位置
public static final int FRAME_X=500;
public static final int FRAME_Y=200;
// 游戏 地面 背景 图片
public static final String BK_IMG_PATH="img/ground.png";
// 游戏 屏幕 刷新率
public static final int GAME_INTERAVL=33;
// 游戏 背景颜色
public static final Color BK_COLOR=new Color(0x4bc4cf);
// 小鸟 状态 图片 资源
public static final String [] BIRDS_IMG_PATH=
"img/normal.png","img/up.png","img/down.png","img/die.png","img/bb.png","img/bb1.png","img/bb2.png","img/bb3.png";
// 标题栏的 高度
public static final int TOP_BAR_HEIGHT = 25;
// 障碍物 的 三种 情况 照片
public static final String [] BARRIERS_IMG_PATH =
"img/barrier.png","img/barrier_up.png","img/barrier_down.png";
// 添加 游戏 刚开始和 结束 的照片
public static final String TITLE_IMG_PATH = "img/title.png";
public static final String NOTICE_IMG_PATH = "img/start.png";
public static final String OVER_IMG_PATH = "img/over.png";
public static final String SCORE_IMG_PATH = "img/score1.jpg";
// 设置 计时牌 的字体
public static final Font TIME_FONT = new Font("华文琥珀",Font.BOLD,40);
② Gameutil 类
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.FileInputStream;
import java.io.IOException;
public class GameUtil
/*
插图 必备 方法
其中 BufferedImage 为 Image 的 子类 方法
插入的 照片 需要 引用 这个方法
*/
public static BufferedImage loadBufferedImage(String imgPath)
try
return ImageIO.read(new FileInputStream(imgPath)) ;
catch (IOException e)
e.printStackTrace();
return null;
// 随机出 两个数 之间的数字, 区间 包括左边 不包括右边
public static int getRandomNumber(int min,int max)
return (int)(Math.random()*(max-min)+min);
/*
判断是否大于这个 概率。
左边为 分子,右边为 分母
*/
public static boolean isInAnyProbability(int numrator , int denominator) throws Exception
// 处理的特殊情况
if (numrator <=0 || denominator <=0)
throw new Exception("传入了非法的参数!");
// 一定 发生的 事件
if (numrator >= denominator)
return true;
return getRandomNumber(1,denominator+1) <= numrator;
③MusicUtil 类
import java.applet.Applet;
import java.applet.AudioClip;
import java.io.File;
public class MusicUtil
private static AudioClip fly;
private static AudioClip fly1;
// 装载音乐资源
public static void load()
try
fly = Applet.newAudioClip(new File(("music/cf.wav")).toURL());
fly1 = Applet.newAudioClip(new File(("music/tiao2.wav")).toURL());
catch (Exception e)
e.printStackTrace();
// wav 音乐的 播放
public static void playFly()
fly.play();
public static void tiao()
fly1.play();
三、游戏展示
四 、素材展示
为方便大家找素材 把素材链接分享给大家 :下载链接:https://pan.baidu.com/s/1aMXZY9k-hMtWmv0hXlqeYw?pwd=0620
提取码:0620
五、感悟
代码可能过长,但是每行代码都有详细的解释哟!!!
学完了java基础,可以自己尝试做个小游戏作为检测哟!!!
希望能给您带来帮助!