游戏介绍

在《FlappyBird》这款游戏中,玩家鼠标点击屏幕,小鸟就会往上飞,不断的点击就会不断的往高处飞。不点击的话则会快速下降。所以玩家要控制小鸟一直向前飞行,然后注意躲避途中高低不平的管子。

1、在游戏开始后,鼠标点击屏幕,要记住是有间歇的点击屏幕,不要让小鸟掉下来。

2、尽量保持平和的心情,点的时候不要下手太重,尽量注视着小鸟。

3、游戏的得分是,小鸟安全穿过一个柱子且不撞上就是1分。撞上柱子就直接挂掉,只有一条命。

本篇博文开发了一个《flappy bird》游戏,运行效果如下:

使用素材文件夹:

素材及完整工程链接:https://pan.baidu.com/s/1D1eCMNzVrVl8XeOz6vDSkg 提取码: xbc1

游戏设计思路

使用场景相对小鸟移动的过程间接实现小鸟在水平方向的位移,小鸟实际上只在垂直方向上进行了位置的改变,调用线程,每次循环使小鸟的y值自动增加以达到重力效果,玩家点击鼠标按键时,减少小鸟y轴坐标以达到跳跃效果,当小鸟位于某根水管中间时,判断小鸟是否与该水管的上侧或者下侧发生了碰撞,如果没有,当小鸟的x坐标>水管左上角x坐标+水管宽度时,分数+1;如果发生了碰撞,游戏结束。

游戏具体实现

Ⅰ信息的存储

游戏使用两张背景图片平铺的形式达到背景循环效果,需要使用backgroundX0和backgroundX1两个变量记录背景1和背景2两张图片左上角的x坐标,使用birdX和birdY记录小鸟左上角的x坐标和y坐标,使用barXArrays数组记录各个水管左上角的x坐标,使用barUpArrays数组记录各个水管上半部分底部的y坐标,使用barDownArrays数组记录各个水管下半部分顶部的y坐标,使用score变量记录分数,使用width和height变量记录屏幕长宽,使用nowStep表示当前跳跃状态y值改变的大小,flag表示小鸟是否在跳跃中:

 Image[] pics = new Image[5];//存储图片int birdX;//小鸟左上角x,y坐标int birdY;int width;//屏幕长宽int height;int backgroundX0 = 0;//背景1的x轴起点int backgroundX1 = 750;//背景2的x轴起点int nowStep = 40;//当前跳跃状态改变的y值大小int flag = 0;//是否跳跃中int score = 0;//记录分数int[] barXArrays = new int[5];//记录各个水管左上角的x坐标int[] barUpArrays = new int[5];//记录各个水管上半部分底部的y坐标int[] barDownArrays = new int[5];//记录各个水管下半部分顶部的y坐标

Ⅱ信息初始化

初始时backgroundX0的值为0,backgroundX1的值为width,小鸟垂直位于屏幕中间,水平方向靠左1/3处。初始化水管x值位于屏幕右侧,每隔400像素出现一根水管,水管宽度为100像素,每根水管上半部分底部的y值位于150~350之间,每根水管下半部分顶部的y值是上半部分底部的y值+250,小鸟初始状态未跳跃,分数为0:

 public GamePanel(){width = 750;height = 750;birdX = width/3-50;//小鸟始终位于屏幕左边1/3处birdY = height/2;//只通过重力去改变小鸟纵坐标this.setPreferredSize(new Dimension(width,height));this.setVisible(true); getPics();initData();this.addMouseListener(new MouseAdapter() {@Overridepublic void mousePressed(MouseEvent e) {flag = 1;repaint();}});new Thread(this).start();}private void initData(){for(int i=0;i<5;i++){int barUp = (int) (Math.random()*200+150);//barUp取值范围是150~350int barDown = barUp+250;//barDown取值范围是450~600barXArrays[i] = width+400*i;barUpArrays[i] = barUp;barDownArrays[i] = barDown;
//          System.out.println(barXArrays[i]+","+barUpArrays[i]+","+barDownArrays[i]);}}

Ⅲ场景相对位移

线程每次循环,背景1和背景2两图片左上角x坐标减去10,当背景1或者背景2的左上角x值为-width时(该背景完全位于屏幕左侧),将x值赋值为width,达到背景循环轮播的效果;线程每次循环还要对每根水管左上角的x值进行减去10的操作,如果某根水管完全位于屏幕左侧时,获取当前场景最后一根水管的数组下标值,根据最后一根水管的x值,在新的位置生成新的一根水管:

@Overridepublic void run() {while(true){try {backgroundX0-=10;backgroundX1-=10;省略...for(int i=0;i<5;i++){barXArrays[i]-=10;省略...if(barXArrays[i]<-100){int index = i-1<0?4:i-1;//获取当前位于最后的一根水管的下标int barUp = (int) (Math.random()*200+150);//barUp取值范围是150~350int barDown = barUp+250;//barDown取值范围是450~600barXArrays[i] = barXArrays[index]+400;barUpArrays[i] = barUp;barDownArrays[i] = barDown;}}repaint();省略...if(backgroundX0==-width){backgroundX0=width;}if(backgroundX1==-width){backgroundX1=width;}                Thread.sleep(50);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}

Ⅳ小鸟的跳跃

为了增加小鸟跳跃的流畅性,可以将小鸟的跳跃过程分为多帧数处理,即每帧向上跳一定距离nowStep,nowStep逐渐减小,然后跟重力效果抵消,当nowStep为0时,跳跃状态结束;在这里可以为小鸟添加一个标记flag,表示小鸟是否在跳跃过程中;当玩家点击鼠标按键时,flag=1;当跳跃状态结束时,flag=0并将nowStep的值初始化;线程每次循环将增加小鸟的y值;

//鼠标监听时间,有鼠标按键时跳跃
this.addMouseListener(new MouseAdapter() {@Overridepublic void mousePressed(MouseEvent e) {flag = 1;repaint();}});@Overridepublic void run() {while(true){try {省略...if(flag==1){//如果是跳跃过程中if(birdY>=0){//没有触碰到游戏屏幕顶部birdY-=nowStep;}nowStep-=4;if(nowStep==0){nowStep=40;flag = 0;}}省略...Thread.sleep(50);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}

Ⅴ碰撞检测

循环遍历水管左上角x坐标数组,如果小鸟左上角x值+小鸟宽度>某根水管左上角x值 并且 小鸟左上角x值+小鸟宽度<该根水管左上角x值+水管宽度,说明小鸟位于该根水管的中间,可能发生碰撞;如果前面两个条件满足,再判断小鸟左上角y值是否小于该水管上半部分底部y值 或者 小鸟左上角y值+小鸟高度是否大于该水管下半部分顶部的y值,如果是,说明发生了碰撞;简单说,碰撞检测需要满足下列条件:

①小鸟左上角x值+小鸟宽度>某根水管左上角x值

②小鸟左上角x值+小鸟宽度<该根水管左上角x值+水管宽度

③小鸟左上角y值<该水管上半部分底部y值 或者 小鸟左上角y值+小鸟高度>该水管下半部分顶部的y值

                    for(int i=0;i<5;i++){if(birdX>barXArrays[i]-50&&birdX<barXArrays[i]+100&&(birdY<=barUpArrays[i]||birdY+50>=barDownArrays[i])){//碰撞检测int best = GameClient.helpPanel.getRecord();int choice;if(score>best){GameClient.helpPanel.writeRecord(score);choice = JOptionPane.showConfirmDialog(null, "你的分数是"+score+",更新了历史记录"+best+"\n是否重新开始?","游戏结束",JOptionPane.YES_NO_OPTION);//获取用户选择}else{choice = JOptionPane.showConfirmDialog(null, "你的分数是"+score+"是否重新开始?","游戏结束",JOptionPane.YES_NO_OPTION);//获取用户选择   }if(choice==1){//否System.exit(0);//退出}else if(choice == 0){//是,重置游戏数据initData();birdY = height/2;flag = 0;score = 0;nowStep = 40;backgroundX0 = 0;backgroundX1 = width;GameClient.helpPanel.getRecord();GameClient.helpPanel.setScore(score);}break;}}

Ⅵ图片的获取及显示

图形化编程基础不多解释。。。

private void getPics() {for(int i=0;i<4;i++){pics[i] = Toolkit.getDefaultToolkit().getImage("D://Game//FlappyBirdGame//pic"+i+".png");}}public void paint(Graphics g){g.clearRect(0, 0, width, height);//清屏g.drawImage(pics[0],backgroundX0,0,width,height,this);//背景1g.drawImage(pics[0],backgroundX1,0,width,height,this);//背景2g.drawImage(pics[3],birdX,birdY,50,50,this);for(int i=0;i<5;i++){//画水管g.drawImage(pics[1], barXArrays[i], 0, 100, barUpArrays[i], this);g.drawImage(pics[2], barXArrays[i],barDownArrays[i], 100,height-barDownArrays[i],this);}}

Ⅶ历史记录读取及更新

常用IO操作,如果不存在则新建历史记录文本;

 //获取历史记录public int getRecord(){File file = new File("D://GameRecordAboutSwing");if(!file.exists()){file.mkdirs();}File record = new File("D://GameRecordAboutSwing//flappyBirdGame.txt");try{if(!record.exists()){//如果不存在,新建文本record.createNewFile();fos = new FileOutputStream(record);dos = new DataOutputStream(fos);String s = "0";dos.writeBytes(s);System.out.println(record.isFile());;}//读取记录fis = new FileInputStream(record);dis = new DataInputStream(fis);String str = dis.readLine();best = Integer.parseInt(str);bestLabel.setText(""+best);}catch(Exception e){e.printStackTrace();}finally{try {if(fis!=null)fis.close();if(dis!=null)dis.close();          if(fos!=null)fos.close();if(dos!=null)dos.close();                } catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return best;}//更新关卡历史记录public void writeRecord(int score){File record = new File("D://GameRecordAboutSwing//flappyBirdGame.txt");try {//清空原有记录FileWriter fileWriter =new FileWriter(record);fileWriter.write("");fileWriter.flush();fileWriter.close();//重新写入文本fos = new FileOutputStream(record);dos = new DataOutputStream(fos);String s = score+"";dos.writeBytes(s);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{try {if(fos!=null)fos.close();if(dos!=null)dos.close();              } catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}

到这游戏的主要实现步骤已经介绍完了,完整源码篇幅不多,这次贴下,自己实现的话素材需自备:

GamePanel类:

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;import javax.swing.JOptionPane;
import javax.swing.JPanel;public class GamePanel extends JPanel implements Runnable{Image[] pics = new Image[5];//存储图片int birdX;//小鸟左上角x,y坐标int birdY;int width;//屏幕长宽int height;int backgroundX0 = 0;//背景1的x轴起点int backgroundX1 = 750;//背景2的x轴起点int nowStep = 40;//当前跳跃状态改变的y值大小int flag = 0;//是否跳跃中int score = 0;//记录分数int[] barXArrays = new int[5];//记录各个水管左上角的x坐标int[] barUpArrays = new int[5];//记录各个水管上半部分底部的y坐标int[] barDownArrays = new int[5];//记录各个水管下半部分顶部的y坐标public GamePanel(){width = 750;height = 750;birdX = width/3-50;//小鸟始终位于屏幕左边1/3处birdY = height/2;//只通过重力去改变小鸟纵坐标this.setPreferredSize(new Dimension(width,height));this.setVisible(true); getPics();initData();this.addMouseListener(new MouseAdapter() {@Overridepublic void mousePressed(MouseEvent e) {flag = 1;repaint();}});new Thread(this).start();}private void initData(){for(int i=0;i<5;i++){int barUp = (int) (Math.random()*200+150);//barUp取值范围是150~350int barDown = barUp+250;//barDown取值范围是450~600barXArrays[i] = width+400*i;barUpArrays[i] = barUp;barDownArrays[i] = barDown;
//          System.out.println(barXArrays[i]+","+barUpArrays[i]+","+barDownArrays[i]);}}private void getPics() {for(int i=0;i<4;i++){pics[i] = Toolkit.getDefaultToolkit().getImage("D://Game//FlappyBirdGame//pic"+i+".png");}}public void paint(Graphics g){g.clearRect(0, 0, width, height);//清屏g.drawImage(pics[0],backgroundX0,0,width,height,this);//背景1g.drawImage(pics[0],backgroundX1,0,width,height,this);//背景2g.drawImage(pics[3],birdX,birdY,50,50,this);for(int i=0;i<5;i++){//画水管g.drawImage(pics[1], barXArrays[i], 0, 100, barUpArrays[i], this);g.drawImage(pics[2], barXArrays[i],barDownArrays[i], 100,height-barDownArrays[i],this);}}@Overridepublic void run() {while(true){try {backgroundX0-=10;backgroundX1-=10;birdY+=10;for(int i=0;i<5;i++){barXArrays[i]-=10;if(barXArrays[i]+100>birdX-5&&barXArrays[i]+100<=birdX+5){score++;GameClient.helpPanel.setScore(score);}if(barXArrays[i]<-100){int index = i-1<0?4:i-1;//获取当前位于最后的一根水管的下标int barUp = (int) (Math.random()*200+150);//barUp取值范围是150~350int barDown = barUp+250;//barDown取值范围是450~600barXArrays[i] = barXArrays[index]+400;barUpArrays[i] = barUp;barDownArrays[i] = barDown;}}repaint();for(int i=0;i<5;i++){if(birdX>barXArrays[i]-50&&birdX<barXArrays[i]+100&&(birdY<=barUpArrays[i]||birdY+50>=barDownArrays[i])){//碰撞检测int best = GameClient.helpPanel.getRecord();int choice;if(score>best){GameClient.helpPanel.writeRecord(score);choice = JOptionPane.showConfirmDialog(null, "你的分数是"+score+",更新了历史记录"+best+"\n是否重新开始?","游戏结束",JOptionPane.YES_NO_OPTION);//获取用户选择}else{choice = JOptionPane.showConfirmDialog(null, "你的分数是"+score+"是否重新开始?","游戏结束",JOptionPane.YES_NO_OPTION);//获取用户选择   }if(choice==1){//否System.exit(0);//退出}else if(choice == 0){//是,重置游戏数据initData();birdY = height/2;flag = 0;score = 0;nowStep = 40;backgroundX0 = 0;backgroundX1 = width;GameClient.helpPanel.getRecord();GameClient.helpPanel.setScore(score);}break;}}if(flag==1){if(birdY>=0){birdY-=nowStep;}nowStep-=4;if(nowStep==0){nowStep=40;flag = 0;}}if(backgroundX0==-width){backgroundX0=width;}if(backgroundX1==-width){backgroundX1=width;}               Thread.sleep(50);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}

HelpPanel类:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;import javax.swing.*;//辅助面板
public class HelpPanel extends JPanel{int score = 0;int best = 0;JLabel scoreLabel = new JLabel("0");JLabel bestLabel = new JLabel("0");FileInputStream fis = null;FileOutputStream fos = null;DataInputStream dis = null;DataOutputStream dos = null;public HelpPanel(){this.setPreferredSize(new Dimension(100,750));this.setVisible(true); this.setLayout(new GridLayout(2,2,10,10));this.add(new JLabel("score:"));this.add(scoreLabel);this.add(new JLabel("best:"));this.add(bestLabel);getRecord();}public void setScore(int score){this.score = score;scoreLabel.setText(score+"");}//获取历史记录public int getRecord(){File file = new File("D://GameRecordAboutSwing");if(!file.exists()){file.mkdirs();}File record = new File("D://GameRecordAboutSwing//flappyBirdGame.txt");try{if(!record.exists()){//如果不存在,新建文本record.createNewFile();fos = new FileOutputStream(record);dos = new DataOutputStream(fos);String s = "0";dos.writeBytes(s);System.out.println(record.isFile());;}//读取记录fis = new FileInputStream(record);dis = new DataInputStream(fis);String str = dis.readLine();best = Integer.parseInt(str);bestLabel.setText(""+best);}catch(Exception e){e.printStackTrace();}finally{try {if(fis!=null)fis.close();if(dis!=null)dis.close();          if(fos!=null)fos.close();if(dos!=null)dos.close();                } catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}return best;}//更新关卡历史记录public void writeRecord(int score){File record = new File("D://GameRecordAboutSwing//flappyBirdGame.txt");try {//清空原有记录FileWriter fileWriter =new FileWriter(record);fileWriter.write("");fileWriter.flush();fileWriter.close();//重新写入文本fos = new FileOutputStream(record);dos = new DataOutputStream(fos);String s = score+"";dos.writeBytes(s);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{try {if(fos!=null)fos.close();if(dos!=null)dos.close();              } catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
}

GameClient类:

import java.awt.BorderLayout;
import java.awt.Container;import javax.swing.JFrame;
import javax.swing.JPanel;public class GameClient extends JFrame{static HelpPanel helpPanel;public GameClient(){GamePanel gamePanel = new GamePanel();//实例化主面板对象helpPanel = new HelpPanel();//实例化辅助面板对象Container container = this.getContentPane();//获取窗体内置容器container.setLayout(new BorderLayout());//设置布局container.add(gamePanel,BorderLayout.CENTER);//添加游戏主面板到内置容器container.add(helpPanel,BorderLayout.EAST);//添加游戏辅助面板到内置容器this.setSize(850,750);//设置窗体大小pack();this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//当用户点击窗体右上角的x时,自动退出程序this.setTitle("flappy bird");//设置窗体标题this.setLocationRelativeTo(null);//让窗体显示在屏幕正中间this.setVisible(true);//展示窗体gamePanel.requestFocus();}public static void main(String[] args) {new GameClient();}}

Java游戏开发——flappy bird相关推荐

  1. 【源码+图片素材+详细教程】Java游戏开发_Java开发经典游戏飞翔的小鸟_飞扬的小鸟_Java游戏项目Flappy Bird像素鸟游戏_Java课程设计项目

    课程目标: 1.通过本课程的学习巩固Java的相关基础知识,例如循环判断,数组和集合的使用,对象的继承,接口的实现,窗口的创建,事件监听,图形绘制. 2.完成小鸟的移动,管道自动生成.碰撞死亡,计分系 ...

  2. 使用Pygame开发flappy bird

    使用Pygame开发flappy bird 1.Pygame简介 Pygame是跨平台Python模块,专为电子游戏设计,包含图像.声音等,创建在SDL(simple DirectMedia Laye ...

  3. java游戏开发杂谈 - 实现游戏主菜单

    经常玩游戏的同学,大家都知道,游戏都会有个主菜单,里面有多个菜单选项:开始游戏.游戏设置.关于游戏.退出游戏等等,这个菜单是怎么实现的呢. 有一定桌面软件开发基础的同学可能会想到,用JButton组件 ...

  4. java游戏开发杂谈 - 游戏编程浅析

    每个游戏,你所看到的它的一切,都是计算机画出来的! 地图是画出来,人物是画出来的,树木建筑是画出来的,菜单按钮是画出来的,滚动的文字.闪烁的图标.云雾烟火,都是画出来的. 游戏编程,所要做的,就是控制 ...

  5. java游戏开发入门(十) -粒子特效

    java游戏开发入门十 - 粒子特效 java游戏开发入门十 - 粒子特效 前言 编码 创建一个粒子发射器,并将粒子发射器添加到实体对象 效果图 完整代码 完整项目 java游戏开发入门十 - 粒子特 ...

  6. Java游戏开发组件LGame简易测试版发布(版本号 0 1 5)

    LGame-Simple-0.1.5组件下载地址:http://code.google.com/p/loon-simple/downloads/list 2009-09-13 更新内容: Java游戏 ...

  7. 三七互娱 Java游戏开发工程师 面试(两轮技术+HR面)

    (2020春招补招,已拿到offer) 抱着投着试试看的心态去的 自己还是不太了解游戏开发 框架啊这些都没怎么问 比较重视基础知识,面的也基本都是基础知识 所以面完才发现自己太基础的知识反而一问三不知 ...

  8. java游戏开发入门(二) - 菜单

    java游戏开发入门(二) - 菜单 前言 编码 主菜单 游戏内菜单(按下ESC后出现,并会暂停游戏) 效果展示 主菜单 游戏内菜单 完整代码 完整项目 前言   上一章我们创建了一个600x600的 ...

  9. 【11款最全最新】Java游戏开发项目合集_Java项目实战_Java练手项目

    黄金矿工.俄罗斯方块.飞机大战.超级玛丽.坦克大战.飞翔的小鸟.扫雷.王者荣耀.推箱子.贪吃蛇.大鱼吃小鱼这些经典小游戏相信很多人都玩过.那大家有没有想过亲自制作出这些小游戏呢? 下面就给大家分享这1 ...

  10. Java游戏开发《超级玛丽》附课件+源码+资料

    <超级玛丽>这款游戏想必大家都玩过,在市面上五花八门的游戏我们眼花缭乱,但是都无法与童年的记忆相比较,经典版的超级玛丽小编觉得是我们00后的童年回忆. 在你的童年记忆里,是否有一个会蹦跳, ...

最新文章

  1. SAP SD基础知识之交货单不完全日志
  2. 小波变换和Gabor变换
  3. 关于Windows Unicode 编码的问题
  4. [蓝桥杯2016初赛]卡片换位-bfs
  5. 马哥学习笔记二十七——IO复用
  6. 几种开源软件授权协议
  7. Qt工作笔记-使用SQL中的LIMIT进行数据分页
  8. 塞尔达传说雷电大剑位置_《塞尔达传说:荒野之息》全收集——双手武器(1)...
  9. 修复 IE 的文本3像素偏移Bug
  10. Docker Swarm学习教程
  11. mysql 的时间函数
  12. EBT 道客巴巴的加密与破解 -免费下载器的基础
  13. #华为云在HC2018#华为云学院
  14. 【hdu 6045】Is Derek lying?
  15. 警告: [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.ecli
  16. linux截图验证码,webdriver保存验证码截图
  17. C. Get an Even String
  18. Android Things:外设I/O接口-UART
  19. 编程的技术|艺术|术术——面向开发者编程
  20. ROS学习记录(二)阿克曼转向车运动学模型及在gazebo中搭建仿真环境

热门文章

  1. 135端口入侵个人电脑
  2. 人工智能数学基础-线性代数1:向量及向量加减法与数乘
  3. python实践报告的心得体会_动物医学实践报告心得体会
  4. Redhat之NIS
  5. 开发linux无线网卡驱动程序,linux-2.6.26下开发marvell 88w8686 wifi驱动 (转)
  6. STM32F103通过M26实现远程在线IAP
  7. Oracle EBS Workflow简介和客制之后的感想
  8. C/C++外部链接、内部链接
  9. matlab的置换,来自CLUSTERGRAM对象的置换向量(MATLAB)
  10. 支付宝快捷支付服务 android,图文详解Android下支付宝快捷支付教程