最终效果
整个项目都是基于swing实现的。窗是口将图片加载到JPanel面板,然后将面板添加到到JFrame窗口实现显示。
这个类是选择几只像素鸟的类,也是main函数里执行的方法,代码有详细的注释,这里就不废话了
public class select extends JPanel { /** * */ private static final long serialVersionUID = 1L; Image background;//添加到窗口的背景图片 Image title1;//添加到窗口的游戏名称图片 JFrame jf; //选择小鸟的窗口 JButton []b ;//按钮数组 client t,t1,t2;//主游戏类 public select(){ jf = new JFrame();//实例化窗口 jf.setTitle("请小心选择小鸟数量");//窗口的标题 jf.setSize(300, 300);//设置窗口大小 jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗口关闭方式,关闭即退出程序 jf.setLocationRelativeTo(null);//设置窗口在屏幕中间显示 jf.setResizable(true);//窗口不可更改大小 jf.add(this);//将面板加入窗口 setLayout(null);//设置面板的布局为空,从而可以任意位置放置按钮 final ImageIcon icon = new ImageIcon("Image/2.png"); background = ((ImageIcon) icon).getImage(); title1 = new ImageIcon("Image/title.png").getImage(); b = new JButton[3]; //实例化按钮 for(int i = 0;i < 3;i++) b[i] = new JButton(); //为按钮添加照片、设置背景位置 b[0].setIcon(new ImageIcon("Image/bird0_0.png")); b[0].setBounds(120,80,40,30); this.add(b[0]); //为第二个按钮添加照片、设置背景位置 b[1].setIcon(new ImageIcon("bird3_2.png")); b[1].setBounds(95,130,100,33); this.add(b[1]); //外第三个按钮添加照片 b[2].setIcon(new ImageIcon("bird3_3.png")); b[2].setBounds(70,185,146,35); this.add(b[2]); //为第一个按钮添加点击事件 b[0].addActionListener(new ActionListener(){ public void actionPerformed(final ActionEvent e) { t = new client();//启动游戏类对象 jf.dispose();//撤销当前窗口 t.init1();//初始化即进入游戏 } }); //为第二个按钮添加点击事件 b[1].addActionListener(new ActionListener(){ public void actionPerformed(final ActionEvent e) { jf.dispose(); t = new client(); t.init2(); } }); //为第三个按钮添加点击事件 b[2].addActionListener(new ActionListener(){ public void actionPerformed(final ActionEvent e) { jf.dispose(); t = new client(); t.init3(); } }); jf.setVisible(true);//设置窗口可见 } //启动窗口后会自动调用这个画画方法,将背景图片画到界面上 public void paint(final Graphics g){ super.paint(g); g.drawImage(background,0,0,300,300,this); g.drawImage(title1,55,20,174,48,this); } }
client类,游戏主类。Main入口方法也放在了这里
public class client extends JPanel implements KeyListener{ /** * */ private static final long serialVersionUID = 1L; private Image background1,background2; //定义两张图片作为背景 private Plane pl; //定义一个线程 private pipe P[]=new pipe[300];//定义柱子数组对象 ArrayList<pipe> list=new ArrayList<pipe>();//存放柱子的集合 Random ran=new Random(); public int k=0; public int score = 0; private bird b1,b2,b3; //定义一个小鸟 int flag;//判断调用哪个初始化函数,即创建几个小鸟 private int x = 0; //这个变量是用来改变小鸟的飞行状态,起到切换图片的作用 public static Music m;//背景音乐 public static Music m2;//结束时的声音 JFrame jf; public Image gameover = new ImageIcon("Image/text_game_over.png").getImage();//游戏结束的图片 //分别获取三只小鸟的散装飞行状态的图片 ImageIcon icon1 = new ImageIcon("Image/bird0_0.png"); ImageIcon icon2 = new ImageIcon("Image/bird0_1.png"); ImageIcon icon3 = new ImageIcon("Image/bird0_2.png"); ImageIcon icon4 = new ImageIcon("Image/bird1_0.png"); ImageIcon icon5 = new ImageIcon("Image/bird1_1.png"); ImageIcon icon6 = new ImageIcon("Image/bird1_2.png"); ImageIcon icon7 = new ImageIcon("Image/bird2_0.png"); ImageIcon icon8 = new ImageIcon("Image/bird2_1.png"); ImageIcon icon9 = new ImageIcon("Image/bird2_2.png"); public boolean b = true;//判断是否进入画排行榜的变量 Font font = new Font("宋体",Font.BOLD,30); //存放从文件中获取的分数 int ScoreArray[] = new int[300]; JButton continueButton,outButton; RandomAccessFile Score;//定义随机流变量 int nandu = 1;//游戏难度 //实例化一个游戏类 public static void main(String[] args) { new select(); } public int produce() { return (int)(Math.random()*30 ); } //主类的构造方法 public client(){ jf = new JFrame("请注意!即将开始游戏,控制三只小鸟的按钮依次时是:Q,W,E");//新建一个窗口 jf.setSize(600, 393);//设置窗口大小 jf.setLocationRelativeTo(null);//设置窗口在屏幕中间显示 jf.setResizable(false);//窗口不可更改大小 jf.add(this);//将面板加入窗口 jf.setVisible(true);//设置窗口可见 jf.addKeyListener(this); //为窗口注册按键事件 m=new Music("风中舞.WAV");//因为这个文件太大,在资源中我已经删掉了。想要背景音乐的话下载一个wav格式的音乐放到文件目录下。然后在这里输入资源名称即可 m2=new Music("小黄人2.WAV"); //通过ImageIcon类加载背景图片 ImageIcon icon = new ImageIcon("Image/3.png"); // ImageIcon icon1=new ImageIcon("Image/pipe_down.png"); background1 = icon.getImage(); background2 = icon.getImage(); //给柱子初始化 for(int i=0;i<300;i++){ P[i]=new pipe(600+k,produce(),this); list.add(P[i]); k+=150; } } //数据初始化方法 //创建一只小鸟的时候调用 public void init1() { flag = 1;//设置小鸟数为1,为下面的画小鸟做准备 b1 = new bird(0,100,this);//创建一个小鸟对象 b1.setbird(icon1,icon2,icon3); pl = new Plane(background1,background2,0,0,this);//创建一个线程对象 m.play(); pl.start(); // } //两只小鸟一起飞时调用 public void init2() { flag = 2; b1 = new bird(0,100,this);//创建一个小鸟对象 b1.setbird(icon1,icon2,icon3); b2 = new bird(50,100,this);//创建一个小鸟对象 b2.setbird(icon4,icon5,icon6); pl = new Plane(background1,background2,0,0,this);//创建一个线程对象 m.play(); pl.start(); //开始线程 } //三只小鸟一起飞时调用 public void init3() { flag = 3; b1 = new bird(0,100,this);//创建一个小鸟对象 b1.setbird(icon1,icon2,icon3); b2 = new bird(50,100,this);//创建一个小鸟对象 b2.setbird(icon4,icon5,icon6); b3 = new bird(100,100,this);//创建一个小鸟对象 b3.setbird(icon7,icon8,icon9); pl = new Plane(background1,background2,0,0,this);//创建一个线程对象 m.play(); pl.start(); //开始线程 } //JPanel面板的画图方法-------这个方法需不断地被执行,我们放在自己创建的一个线程里执行 public void paint(Graphics g){ super.paint(g); g.drawImage(pl.background1, pl.x, pl.y, 600, 393, this);//画第一张背景图 g.drawImage(pl.background2,599+pl.x, pl.y, 600, 393, this);//画第二张背景图 //通过判断flag值来确定画几个小鸟 String s = Integer.toString(score); String n = Integer.toString(nandu); this.drawBirdAndJump(g); //画柱子和柱子的移动 for(int i=0;i<list.size();i++){ list.get(i).drawDown(g); list.get(i).drawUp(g); list.get(i).move(); } g.setColor(Color.RED); g.setFont(font); g.drawString("得分:"+s, 150, 25); //画分数 g.setColor(Color.PINK); g.drawString("难度:"+n, 380, 25); this.isDead(g); try { this.showScore(g); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void isDead(Graphics g){ //柱子和1只小鸟碰撞 if(flag==1){ for(int i = 0;i < list.size();i++){ if(b1.Getrect().intersects(list.get(i).GetDOWNrect()) ||//第一只鸟与上边界柱子相碰 b1.Getrect().intersects(list.get(i).GetUPrect())//第一只鸟与下边界柱子相碰 || b1.gety() < 0 || b1.gety() > 393)//小鸟往上飞出窗口或者往下掉出窗口 { pl.setFlag(false);//柱子停止移动 m.stop();//游戏结束时停止背景音乐 g.drawImage(gameover,200,180,204,54,this);//画出游戏结束图片 } } } //2只小鸟时,小鸟与柱子的碰撞 else if(flag==2){ for(int i = 0;i < list.size();i++){// if(b1.Getrect().intersects(list.get(i).GetDOWNrect()) || b1.Getrect().intersects(list.get(i).GetUPrect()) || b2.Getrect().intersects(list.get(i).GetDOWNrect()) || b2.Getrect().intersects(list.get(i).GetUPrect()) || b1.gety() < 0 || b1.gety() > 393 || b2.gety() < 0 || b2.gety() > 393) { pl.setFlag(false); m.stop();//游戏结束时停止背景音乐 g.drawImage(gameover,200,180,204,54,this); } } } //三只小鸟时候的小鸟与柱子碰撞 else if(flag==3){ for(int i = 0;i < list.size();i++){ if(b1.Getrect().intersects(list.get(i).GetDOWNrect()) || b1.Getrect().intersects(list.get(i).GetUPrect()) || b2.Getrect().intersects(list.get(i).GetDOWNrect()) || b2.Getrect().intersects(list.get(i).GetUPrect()) || b3.Getrect().intersects(list.get(i).GetDOWNrect()) || b3.Getrect().intersects(list.get(i).GetUPrect()) || b1.gety() < 0 || b1.gety() > 393 || b2.gety() < 0 || b2.gety() > 393 || b3.gety() < 0 || b3.gety() > 393) { pl.setFlag(false); m.stop(); //游戏结束时停止背景音乐 g.drawImage(gameover,200,180,204,54,this); } } } if(pl.getFlag()) { for(int i = 0;i < list.size();i++){ if(list.get(i).getUppipex() < 0){ //每十根柱子更改移动速度 if(score % 10 == 0 && score != 0){ pipe.setMoveSp(); nandu++; } list.remove(i); Music mus=new Music("小黄人1.WAV"); ;//定义多个过柱子时的笑声 mus.play(); score++ ; } } } } /* * 画出排行榜 * 画出当前分数的排行 */ public void showScore(Graphics g) throws IOException{ int max,sedmax,threemax;//三个变量保存分数的前三名 if(!pl.getFlag()&&b){ b = false;//避免线程刷新画面时重新进入浪费资源,设置为false int i; //创建随机流访问文件 //判断是几只小鸟,然后将分数存到不同的文件中 if(flag == 1) Score = new RandomAccessFile("scoreOneBird.dat","rw"); else if(flag == 2) Score = new RandomAccessFile("scoreTwoBird.dat","rw"); else Score = new RandomAccessFile("scoreThreeBird.dat","rw"); //将位置跳到文件末,存放分数 Score.seek(Score.length()); Score.writeInt(score); //关闭文件 Score.close(); i =0; while(true){ //判断是在玩几只小鸟,然后打开不同的存分数文件 if(flag == 1) Score = new RandomAccessFile("scoreOneBird.dat","rw"); else if(flag == 2) Score = new RandomAccessFile("scoreTwoBird.dat","rw"); else Score = new RandomAccessFile("scoreThreeBird.dat","rw"); //每隔四个字节读取一个整形数 Score.seek(i*4); //将分数保存到数组中 ScoreArray[i] = Score.readInt(); i++; //如果到文件末尾,退出循环 if(Score.length() == i*4){ break; } } //关闭文件 Score.close(); //初始化三个变量 max = sedmax = threemax = 0; //将数组按顺序排序 Arrays.sort(ScoreArray); //轻松获取前三大值 max = ScoreArray[ScoreArray.length-1]; sedmax = ScoreArray[ScoreArray.length-2]; threemax = ScoreArray[ScoreArray.length-3]; //记录排名,从末尾往前 int count = ScoreArray.length; for(i = 0;i < ScoreArray.length;i++) { //如果分数比数组里的数大,排名往前一位 if(ScoreArray[i] < score ) count--; //如果等0,说明是第一名 if(count == 0) count = 1; } //三个整形转换成字符型 String ScMax = Integer.toString(max); String ScSeMax = Integer.toString(sedmax); String ScThMax = Integer.toString(threemax); //设置字体大小、改变颜色、画出排名 g.setFont(font); g.setColor(Color.MAGENTA); g.drawString("当前排名:"+count,150, 90); g.setColor(Color.ORANGE); g.drawString("得分排名第一:"+ScMax, 150, 120); g.setColor(Color.CYAN); g.drawString("得分排名第二:"+ScSeMax, 150, 150); g.setColor(Color.GREEN); g.drawString("得分排名第三:"+ScThMax, 150, 180); //实例两个按钮 continueButton = new JButton("重新开始"); outButton = new JButton("退出游戏"); //将按钮添加到面板上、设置背景颜色、出现的位置以及大小 this.add(continueButton); this.add(outButton); continueButton.setBackground(Color.green); continueButton.setBounds(140,240,100,50); outButton.setBackground(Color.CYAN); outButton.setBounds(350,240,100,50); //为继续按钮添加点击事件 continueButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { jf.dispose(); new select(); } }); // 为退出游戏按钮添加点击事件 outButton.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { // TODO Auto-generated method stub jf.dispose(); } }); } } public void drawBirdAndJump(Graphics g){ //画一只小鸟 if(flag == 1) { jf.setTitle("单飞像素鸟 跳跃键:Q"); b1.draw(g,x);//调用小鸟对象的画图方法画一个小鸟 } //flag=2 画2只小鸟 else if(flag == 2) { jf.setTitle("双飞像素鸟 跳跃键:Q、W"); b1.draw(g,x); b2.draw(g,x); } //画三只小鸟 else { jf.setTitle("三飞像素鸟 跳跃键:Q、W、E"); b1.draw(g,x);//调用小鸟对象的画图方法画一个小鸟 b2.draw(g,x); b3.draw(g,x); } x++;//每次跑都改变小鸟飞行的图片 if(flag == 1) b1.move();//小鸟往下掉的方法 else if(flag == 2) { b1.move(); b2.move(); } else if(flag == 3){ b1.move();//小鸟往下掉的方法 b2.move(); b3.move(); } if(x == 3)//小鸟飞行有三张图片,循环变换 x = 0; } //按下键盘时自动调用的方法 public void keyPressed(KeyEvent e) { } //按下并且释放键盘时自动调用的方法 public void keyReleased(KeyEvent e) { //通过flag值判断调用多少个小鸟的按键函数 if(flag == 1) b1.keyPressed1(e);//调用小鸟类的按键方法 else if(flag == 2){ b1.keyPressed1(e); b2.keyPressed2(e); } else{ b1.keyPressed1(e);//调用小鸟类的按键方法 b2.keyPressed2(e); b3.keyPressed3(e); } }
线程类非常简单。只要一个死循环就行
public class Plane extends Thread { public Image background1, background2; // 定义两张背景 public int x = 0; // 背景图片x坐标 public int y = 0;// 背景图片y坐标 public JPanel jp;// 定义一个面板 public int movesp = 5;// 背景图的移动速度 public boolean flag = true; public Music m = new Music("风中舞.WAV"); public Music m2 = new Music("小黄人2.WAV"); // 构造方法 public Plane(Image background1, Image background2, int x, int y, JPanel jp) { m.play(); this.background1 = background1; this.background2 = background2; this.x = x; this.y = y; this.jp = jp;// 将面板传进来主要是为了在线程中调用窗口面板的repaint // 方法,重画游戏窗口 } public boolean setFlag(boolean flag) { this.flag = flag; return flag; } public boolean getFlag() { return flag; } public void run() { // 在线程中循环调用背景图的move方法,并且重画面板 while (true) { if (flag) { //移动就是通过不断刷新更改x,y值实现的 jp.repaint(); } else { m.stop(); m2.play(); } try { Thread.sleep(100);// 线程睡眠0.1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } }
还有一个柱子类、封装的调用播放音乐的类非常非常简单这里就不放占用资源了。下载地址