主要是有六个类来实现,游戏界面类,管子类,管子管理线程类,鸟管理线程类,鸟飞行鼠标监听器类,还有一个动画实现管理类。
首先是游戏界面类,用来创建游戏窗口和启动动画实现管理:
public class mainUI { //程序入口 public static void main(String[] args) { mainUI ui = new mainUI(); } //构造函数,自动调用初始化方法和开始游戏方法 public mainUI() { // TODO Auto-generated constructor stub initUI(); } //初始化游戏窗口 public void initUI(){ //判断是否开始游戏的标记 boolean startflag = false; JFrame jf = new JFrame("Flappy Bird"); jf.setSize(600, 500); jf.setLocationRelativeTo(null); jf.setResizable(false); jf.setDefaultCloseOperation(3); jf.setVisible(true); //创建游戏动画线程管理线程 Administration ad = new Administration(jf); ad.start(); } }
然后是动画实现管理类,在写这么一个类之前是纠结了好一段时间了,因为刚开始写的时候发现管子的线程,和鸟的线程,和背景的动画实现,没办法协调起来,分别独自画在窗体上会出现相互覆盖的情况,所以后来想出了这么个类,用来获取当前鸟的坐标,还有管子队列中各个管子坐标,然后和背景一起在这个类里面的BufferedImage()里面画出来,再一起显示,没有看过别人写的动画实现方法,就只能先想出这个办法了。
public class Administration extends Thread { private JFrame jf; private boolean die; public Administration(JFrame jf){ this.jf = jf; } public void run(){ //创建柱子队列管理线程,管理柱子的生成 PipeManage pm = new PipeManage(jf); pm.start(); //创建鸟管理线程,管理鸟的动态 BirdManage bm = new BirdManage(jf); bm.start(); //创建图片图标 ImageIcon baitian = new ImageIcon("image/白天.png"); ImageIcon dimian = new ImageIcon("image/地.png"); ImageIcon zheng = new ImageIcon("image/正.png"); ImageIcon fan = new ImageIcon("image/反.png"); ImageIcon shang = new ImageIcon("image/上.png"); ImageIcon zhong = new ImageIcon("image/中.png"); ImageIcon xia = new ImageIcon("image/下.png"); //飞行状态 int fly = 3; ImageIcon gameover = new ImageIcon("image/gameover.png"); //图片坐标 int x = 0; //管子队列起画坐标 int xx = 0; //创建图片缓冲区 BufferedImage buffer = new BufferedImage(jf.getWidth(), jf.getHeight(), BufferedImage.TYPE_INT_RGB); //获取缓冲区画布 Graphics g = buffer.getGraphics(); //获取窗体画布 Graphics gg = jf.getGraphics(); while(true){ //延迟 delay(10); //起画坐标移动 x--; xx--; if(x <= -baitian.getIconWidth()) x = 0; //判断是否碰撞 iscrash(bm,pm,xx,zheng.getIconWidth(),zhong.getIconWidth(),zhong.getIconHeight(),dimian.getIconHeight()); if(die){ gg.drawImage(gameover.getImage(),(jf.getWidth()-gameover.getIconWidth())/2,(jf.getHeight()-gameover.getIconHeight())/2,null); this.stop(); } //清屏 g.setColor(jf.getBackground()); g.fillRect(0, 0, jf.getWidth(), jf.getHeight()); //----------画背景---------------------- g.drawImage(baitian.getImage(),x,0,null); g.drawImage(baitian.getImage(),x+baitian.getIconWidth(),0,null); g.drawImage(baitian.getImage(),x+2*baitian.getIconWidth(),0,null); //---------画管子------------- for(int i=1;i<=pm.getPipeNum();i++){ g.drawImage(fan.getImage(),xx+pm.getPipe(i).position,pm.getPipe(i).gap-fan.getIconHeight(),null); g.drawImage(zheng.getImage(),xx+pm.getPipe(i).position,pm.getPipe(i).gap+130,null); } //---------画地面------- g.drawImage(dimian.getImage(),x,jf.getHeight()-dimian.getIconHeight(),null); g.drawImage(dimian.getImage(),x+dimian.getIconWidth(),jf.getHeight()-dimian.getIconHeight(),null); g.drawImage(dimian.getImage(),x+2*dimian.getIconWidth(),jf.getHeight()-dimian.getIconHeight(),null); //---------画鸟-------- if(fly==3){ g.drawImage(shang.getImage(),bm.getX(),bm.getY(),null); fly--; }else if(fly==2){ g.drawImage(zhong.getImage(),bm.getX(),bm.getY(),null); fly--; }else{ g.drawImage(xia.getImage(),bm.getX(),bm.getY(),null); fly = 3; } //累计得分 g.setColor(Color.black); g.drawString(CountScore(bm,pm, xx,zheng.getIconWidth()), 50, jf.getHeight()-dimian.getIconHeight()+50); //显示出图片缓冲区的内容 gg.drawImage(buffer, 0, 0, null); } } //累计得分 public String CountScore(BirdManage bm,PipeManage pm,int xx,int PipeWidth){ int score=0; for(int i=1;i<=pm.getPipeNum();i++){ if(bm.getX()>=pm.getPipe(i).position+xx+PipeWidth) score++; } System.out.println(score); return Integer.toString(score); } //判断碰撞方法 public void iscrash(BirdManage bm,PipeManage pm,int xx,int PipeWidth,int birdWidth,int birdHeight,int dimian){ for(int i=1;i<=pm.getPipeNum();i++){ //判断上柱子(右碰) if(bm.getX()+birdWidth >= pm.getPipe(i).position+xx && bm.getX()+birdWidth <= pm.getPipe(i).position+xx+PipeWidth && bm.getY() <= pm.getPipe(i).gap-5){ die = true; return; } //判断上柱子(左碰) if(bm.getX() >= pm.getPipe(i).position+xx && bm.getX() <= pm.getPipe(i).position+xx+PipeWidth && bm.getY() <= pm.getPipe(i).gap-5){ die = true; return; } //判断下柱子(右碰) if(bm.getX()+birdWidth >= pm.getPipe(i).position+xx && bm.getX()+birdWidth <= pm.getPipe(i).position+xx+PipeWidth && bm.getY()+birdHeight >= pm.getPipe(i).gap+145){ die = true; return; } //判断下柱子(左碰) if(bm.getX() >= pm.getPipe(i).position+xx && bm.getX() <= pm.getPipe(i).position+xx+PipeWidth && bm.getY()+birdHeight >= pm.getPipe(i).gap+145){ die = true; return; } if(bm.getY()+birdHeight >= jf.getHeight()-dimian+12){ die = true; return; } } } //延迟方法 public void delay(int t){ try { sleep(t); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
然后是管子线程管理类,用来生成管子对象,并移动管子坐标,用来给动画实现管理类提供管子坐标,实现动画还有碰撞判断:
public class PipeManage extends Thread { private static nodeQuene nq = new nodeQuene();; private int position, Heigh; private int gap; private JFrame jf; public PipeManage(JFrame jf) { this.position = jf.getWidth(); this.Heigh = jf.getHeight(); } public void run() { Random rd = new Random(); while (true) { // 延迟 try { sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // 移动管子坐标 position++; // 管子队列 if (nq.size() == 0) { // 产生管子随机缝隙纵坐标 gap = 70 + rd.nextInt(Heigh - 300); pipe pp = new pipe(position, gap); nq.add(pp); } else { // 产生管子随机缝隙纵坐标 gap = 70 + rd.nextInt(Heigh - 300); // 如果前一个管子移动得足够远,生成管子,加入队列 if (nq.get(nq.size()).o.position <= position -180) { pipe pp = new pipe(position, gap); nq.add(pp); } } } } public pipe getPipe(int i) { return nq.get(i).o; } public int getPipeNum() { return nq.size(); } }
这个是管子类:
public class pipe { public int position; public int gap; public pipe(int position,int gap){ this.gap = gap; this.position = position; } }
然后就是鸟线程管理类,由于只有一直鸟,就没有再特地写一个鸟的类了,就直接放在这个类里面了:
public class BirdManage extends Thread{ private int X=150,Y=250; static boolean flyingFlag; static int dist = 25; private JFrame jf; public BirdManage(JFrame jf){ this.jf = jf; } public void run(){ //鼠标监听器 BirdListener bl = new BirdListener(); jf.addMouseListener(bl); while(true){ //延迟 try { sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //如果按下了起飞 if(flyingFlag){ if(dist<=0){ dist=25; flyingFlag = false; } else{ Fly(); dist--; } continue; } //自动下落 DownBird(); } } public void Fly(){ Y-=3; } public void DownBird(){ Y+=2; } public int getX(){ return X; } public int getY(){ return Y; } }
还有就是点击事件,让鸟改变方向移动一段距离:
public class BirdListener extends MouseAdapter { public void mousePressed(MouseEvent e) { BirdManage.flyingFlag = true; BirdManage.dist = 25; } }
还有就是一个队列类nodeQueue(),就不拿出来啦。。
最后感觉做出来的游戏,动画上还是和原作差不多,但是鸟的移动就感觉有点生硬,在考虑重力加速度而不是单纯地匀速移动之后应该会有更好的效果。然后看到最近挺火的超难游戏I Wanna 之后,感觉添上一些未知陷阱会添加更多欢乐,接下来往这方面修改。