前言

飞翔的小鸟 小游戏 可以作为 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> 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() 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 > 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 >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 >1),y,null);  }  /*       障碍物 的 逻辑 部分   */  private void logic(){      x -= speed;      rect.x -=speed;      // 障碍物 完全移除了 屏幕      if (x 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 pool =new ArrayList();  // 对象 池中 初始 的对象的个数  public static final int INIT_BARRIER_COUNT = 16;  // 最大个数  public static final int MAX_BARRIER_COUNT = 20;  static {  // 初始化 池中的 对象  for (int i=0;i0) {  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 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 = 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” />

五、感悟

代码可能过长,但是每行代码都有详细的解释哟!!!
学完了java基础,可以自己尝试做个小游戏作为检测哟!!!

希望能给您带来帮助!