游戏界面
运行界面
开发准备
1、eclipse开发工具
二、创建游戏窗口
Mains类作为主类,在mian方法下定义一个m1()方法,设置窗口。
//定义一个初始化的游戏窗口方法
public static void m1() {
//获取底层窗口界面的工具类
JFrame jf =new JFrame();//创建了窗口对象
jf.setSize(432, 644);//设置窗口大小
jf.setTitle("出击吧小鸟");//设置窗口标题
jf.setLocationRelativeTo(null);//默认坐标居中
jf.setVisible(true);//设置窗口可见
jf.setResizable(false);//设置窗口大小不可以调整
//设置窗口监听,关闭窗口时,程序结束运行
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
三、背景对象
将图片资源都放在src下面,在BackGround类中,使用IO流读取背景图片,获取图片宽度高度等。
public class BackGround {//背景类
//在这个类中描述背景图片的属性
public int width;//图片的宽度属性
public int height;//图片的高度属性
//使用处理图片的工具类
public BufferedImage img=null;
public BackGround() {
//在构造器中初始化背景类的宽度、高度属性
// 读取 ,写入
/*
* 异常处理机制、异常捕获机制
* try抛出异常 ,catch捕获异常
* 处理程序运行时出现的异常
* try{} 可能出现问题的代码
* catch{} 出现问题之后,跳过try中剩余的代码
* 执行catch{}中的代码
* catch( 准备捕获的异常类型 )
*/
try {
img = ImageIO.read(getClass().getResource("/bg.png"));
//图片资源 储存了背景图片的所有信息
width = img.getWidth();//获取图片资源的宽度给类中的属性赋值
height=img.getHeight();
} catch (IOException e) {
e.printStackTrace();
}
}
}
四、操作面板
面板类这里直接在Mians类下定义一个单独的类,上面用于实现背景,小鸟等对象。
面板类继承Jpanel类,重新paint方法,绘制背景对象。
-
//游戏的操作面板类 //继承面板工具类 JPanel class Panel extends JPanel { BackGround bg =null; // 创建背景类的对象 public Panel() {//构造器 bg =new BackGround();//加载背景类的实例 } //绘制图片的方法 Jpanel工具类中的 paint() @Override public void paint(Graphics g) { //在绘制图片的方法中 ,绘制背景图片,小鸟。柱子等等 //Graphics类 制图工具类 使用制图功能绘制图片,或者文字 g.drawImage(bg.img,0,0,null); //参数1: 要绘制的图片 //参数2:图片的x坐标 //参数3: 图片的y坐标 //参数4: 默认出现的位置. } }
然后在设置游戏窗口的m1方法中,将制作的游戏面板添加到窗口中。在设置窗口可见这行代码的下边添加即可。
Panel p =new Panel();//创建面板对象 jf.add(p);//在窗口中添加面板
然后点击运行,窗口中就有背景图了。
五、地面对象
在窗口的左上角默认坐标点是x=0.y=0
地面图片的y坐标 = 背景图片的高度 - 地面图片的高度。
地面是根据背景的基础上实现移动的,在地面类中需要添加一个单独的移动方法。
public class Ground {//地面类 BufferedImage img =null; public int width;//图片的宽度属性 public int height;//图片的高度属性 public int x,y;//地面的x坐标 y坐标 //获取背景类对象的高度属性 BackGround bg=null; public Ground() { try { bg=new BackGround(); img = ImageIO.read(getClass().getResource("/ground.png")); //图片资源 储存了背景图片的所有信息 width = img.getWidth();//获取图片资源的宽度给类中的属性赋值 height=img.getHeight(); x=0; y=bg.height-height; //背景图片高度,减去地面图片高度,就是地面的初始y坐标 } catch (IOException e) { e.printStackTrace(); } } //让地面移动的方法 public void move(BackGround bg) { // 横向前移 x x--; //设计一个循环 ,能够一直移动 if(x==bg.width+10-width) {//修正值 x=0;//x坐标归零 } } }
然后在面板类Panel中创建地面类的对象,将地面对象在paint方法中绘制出来
class Panel extends JPanel{ BackGround bg =null; // 创建背景类的对象 Ground ground=null;//创建地面类对象 public Panel() { bg =new BackGround();//加载背景类的实例 ground =new Ground();//加载地面类实例 } //绘制图片的方法 Jpanel工具类中的 paint() @Override public void paint(Graphics g) { //在绘制图片的方法中 ,绘制背景图片,小鸟。柱子等等 //Graphics类 制图工具类 使用制图功能绘制图片,或者文字 g.drawImage(bg.img,0,0,null); //参数1: 要绘制的图片 //参数2:图片的x坐标 //参数3: 图片的y坐标 //参数4: 默认出现的位置. g.drawImage(ground.img,ground.x,ground.y,null); } }
现在点击运行,看起来和没绘制地面之前没有什么不同,因为地面图片与背景中的地面位置完美重合了,接下来要调用地面移动的方法。在面板类中定义一个方法action作为所有运行状态的启动开关,地面一直在移动,所以用一个死循环来实现,加上线程休眠时间,来控制游戏运行的速度。
//调用游戏中动态效果的方法 public void action() {//执行这个方法,游戏开始运行 //地面移动 一直在移动 是一个死循环 while(true) { ground.move(bg); //设置线程休眠 每隔一段时间,线程休眠一次,相当于清空内存重新执行 try { Thread.sleep(1000/40);//控制速度的 this.repaint();//重新绘制 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }//每隔这么长时间重新执行 } }
写完之后在Mains类中的m1方法最后一行调用,点击运行,地面开始移动起来。
p.action();//开始运行
六、加入游戏状态图片
游戏开始前,加入start图片,游戏结束,加入gameover图片。由于状态不需要单独创建对象,图片资源直接在面板类中设计。通过鼠标点击来切换状态,所以这里需要加载鼠标点击松开事件。JPanle面板工具类中有定义好的鼠标工具对象,使用this来调用即可。
//继承面板工具类 JPanel class Panel extends JPanel { //在 这里添加游戏状态属性 0 1 2 public int state=0;//初始状态是0 准备开始 BufferedImage imgStart=null;//0 准备开始的图片 BufferedImage gameover=null;//2 游戏结束的图片 BackGround bg =null; // 创建背景类的对象 Ground ground=null;//创建地面类对象 public Panel() {//构造器 bg =new BackGround();//加载背景类的实例 ground =new Ground();//加载地面类实例 try {//加载图片资源 imgStart = ImageIO.read(getClass().getResource("/start.png")); gameover = ImageIO.read(getClass().getResource("/gameover.png")); } catch (IOException e) { // TODO: handle exception } } //绘制图片的方法 Jpanel工具类中的 paint() @Override public void paint(Graphics g) { //在绘制图片的方法中 ,绘制背景图片,小鸟。柱子等等 //Graphics类 制图工具类 使用制图功能绘制图片,或者文字 g.drawImage(bg.img,0,0,null); //参数1: 要绘制的图片 //参数2:图片的x坐标 //参数3: 图片的y坐标 //参数4: 默认出现的位置. if(state==0) { g.drawImage(imgStart,0,0,null); }else if(state==1) { }else if(state==2) { g.drawImage(gameover,0,0,null); } g.drawImage(ground.img,ground.x,ground.y,null); } //调用游戏中动态效果的方法 public void action() {//执行这个方法,游戏开始运行 //通过鼠标点击,切换游戏状态 //获取鼠标权限 this.addMouseListener(new MouseAdapter() { //点击鼠标释放后的事件 @Override public void mouseReleased(MouseEvent e) { //super.mouseReleased(e); // 0准备 1开始 2 结束 switch (state) { case 0: state=1; break; case 1: state=2; break; case 2: state=0; break; default: break; } } }); //地面移动 一直在移动 是一个死循环 while(true) { ground.move(bg); //设置线程休眠 每隔一段时间,线程休眠一次,相当于清空内存重新执行 try { Thread.sleep(1000/40);//控制速度的 this.repaint();//重新绘制 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }//每隔这么长时间重新执行 } } }
现在点击运行,在界面中点击鼠标可以切换状态了。
七、小鸟对象
小鸟一共有8张图片,所以这里使用图片工具类数组来储存,然后添加一个方法,由切换图片实现小鸟煽动翅膀的飞行动作。首先是游戏状态0时,在start图片中间有一处留白的位置准备放一个会动的小鸟,这里先创建小鸟类,然后在状态0时绘制,切换到状态1时,小鸟位置切换。
public class Bird {//小鸟类 // x:190 y:220 public int width;//图片的宽度属性 public int height;//图片的高度属性 public int x,y;//地面的x坐标 y坐标 public BufferedImage[] imgs=new BufferedImage[8]; BufferedImage img=null;//准备一个图片,用来属性赋值 //这是一个图片数组,数组中储存8张图片 int index=0; //下标属性 public Bird() { try { for (int i = 0; i < imgs.length; i++) {//i 0-7 imgs[i]=ImageIO.read(getClass(). getResource("/"+i+".png")); } img=imgs[0]; height=img.getHeight(); width=img.getWidth(); x=190; y=220; } catch (Exception e) { } } // 添加小鸟煽动翅膀飞行的动作 public void fly() { //定义一个数组的下标 根据下标切换数组中的图片,实现轮播效果 index++; img = imgs[index%8];//index自增得出下标 振动频率/6 if(index==200) {//修正值,到100之后归0 index=0; } } }
在面板类中绘制小鸟,在action方法中执行小鸟飞行动作
while(true) { ground.move(bg); bird.fly();//小鸟一直在飞行 }
下一步,小鸟在状态1时,通过鼠标点击向上飞行,不点就按照重力加速度规则自动掉落,这里在小鸟类中添加一些速度变量,定义两个方法控制上升和掉落
//关于重力加速度的变量 double g=9.8; double v=0;//初始下降速度 double t=0.18;//小鸟自动下降的时长 double h;//小鸟下降的距离 double up=25;//小鸟上升的速度 //小鸟向上飞行的动作 public void up() { v = up; } //如果不点鼠标小鸟会根据重力自动向下掉 public void down() { v=v-g*t; h=v*t-g*t*t/2; y=y-(int)h; //根据物理公式,得出小鸟下降的距离,给y坐标重新赋值 }
掉落的方法是一直都在执行的,所以写在死循环中,当状态是1时,小鸟自行掉落。
while(true) { ground.move(bg); bird.fly();//小鸟一直在飞行 if(state==0) { }else if(state==1) { bird.down();//如果是开始状态,小鸟会自动下降 }
上升的方法是点击鼠标的时候执行的,所以要写在点击鼠标事件的状态1中
this.addMouseListener(new MouseAdapter() { //点击鼠标释放后的事件 @Override public void mouseReleased(MouseEvent e) { //super.mouseReleased(e); // 0准备 1开始 2 结束 switch (state) { case 0: state=1; break; case 1: bird.up();//状态是运行时,点击鼠标小鸟向上飞 break; case 2: state=0; break; default: break; } } });
八、柱子对象
public class Column {//柱子类 public int width;//图片的宽度属性 public int height;//图片的高度属性 public int x,y;//x坐标 y坐标 BufferedImage img =null; /* * 小鸟闯关的柱子,每隔244间距,就要再产生一根新柱子 * 柱子的高度通过随机数产生,所以要先计算出柱子的高度 * 柱子的最大高度:柱子的图片高度-柱子通道距离144,然后除2 * 柱子的最小高度:柱子的最大高度 - 背景高度 -地面高度 -通道距离 * 柱子的y坐标: 最大柱子高度和最小高度之间的随机数 */ Random ran =new Random();//获取随机数权限 int max=0 , min=0; public Column(BackGround bg,Ground ground) { // 柱子的x坐标需要参考背景和地面的x坐标 try { img = ImageIO.read(getClass().getResource("/column.png")); width=img.getWidth(); height=img.getHeight(); x=bg.width; // 柱子高度的最大值和最小值 max= (height -144)/2; min =(height -144)/2 - (bg.height-144 -ground.height); y= -(ran.nextInt(max-min)+ min); } catch (Exception e) { // TODO: handle exception } } //柱子不断向前移动的动作 public void move (BackGround bg) { x--; if(x== - width) { x=bg.width; y=-(ran.nextInt(max-min)+ min); //如果柱子移动出界,将x 坐标和y坐标 初始化 } } }
在面板类中创建两根柱子对象,然后在paint方法中绘制。在action方法中调用柱子移动的方法。
Column c1=null; Column c2=null;//创建两根柱子对象 public Panel() { bg =new BackGround();//加载背景类的实例 ground =new Ground();//加载地面类实例 bird=new Bird();//加载小鸟类实例 c1=new Column(bg, ground); c2=new Column(bg, ground);//加载柱子类实例 c2.x = bg.width+244;//两根柱子的间距 }
if(state==0) { g.drawImage(imgStart,0,0,null); g.drawImage(bird.img,bird.x,bird.y,null); }else if(state==1) { //准备绘制小鸟和柱子 以及分数等 g.drawImage(bird.img,bird.x-80,bird.y,null); g.drawImage(c1.img,c1.x,c1.y,null); g.drawImage(c2.img,c2.x,c2.y,null); }else if(state==2) { g.drawImage(gameover,0,0,null); }
现在点击运行,柱子也出现了。现在需要定义小鸟与柱子、天空、地面等对象碰撞死亡,和穿过柱子计分的方法。
//给小鸟添加死亡方法 //碰撞地面 死亡 public boolean hitGround(BackGround bg ,Ground ground) { if(y+height >= (bg.height-ground.height)) { //小鸟当前y坐标+小鸟自身的高度 》>= 背景高度-地面高度 return true; //说明碰撞到了地面 }else { return false;//说明没有碰到 } } //碰撞天空 死亡 public boolean hitSky() { if(y<=0) {//小鸟当前的y坐标<=0 说明碰到了天空边缘 return true;//是,死亡 } return false; } //碰到柱子 死亡 public boolean hitColumn(Column c) { // 检测x 坐标 和 y坐标 if( x-width>= c.x && x<= (c.x+c.width)) { //如果碰撞了当前柱子的x坐标 if(y<= c.y+(c.height-144)/2 || y>= c.y+(c.height+144)/2-height) { //如果碰撞到上半部柱子或者下半部柱子 return true;//死亡 } } return false; } //如果没有碰到柱子得分的方法 public boolean addScore(Column c) { if(x==c.x+c.width) { return true; } return false; }
//绘制图片的方法 Jpanel工具类中的 paint() @Override public void paint(Graphics g) { //在绘制图片的方法中 ,绘制背景图片,小鸟。柱子等等 //Graphics类 制图工具类 使用制图功能绘制图片,或者文字 g.drawImage(bg.img,0,0,null); //参数1: 要绘制的图片 //参数2:图片的x坐标 //参数3: 图片的y坐标 //参数4: 默认出现的位置. Font f =new Font(Font.SANS_SERIF,Font.ITALIC,20); g.setFont(f); g.setColor(Color.ORANGE);//设置橙色字体 g.drawString("得分:"+score, 20, 40); if(state==0) { g.drawImage(imgStart,0,0,null); g.drawImage(bird.img,bird.x,bird.y,null); }else if(state==1) { //准备绘制小鸟和柱子 以及分数等 g.drawImage(bird.img,bird.x-80,bird.y,null); g.drawImage(c1.img,c1.x,c1.y,null); g.drawImage(c2.img,c2.x,c2.y,null); }else if(state==2) { g.drawImage(gameover,0,0,null); } g.drawImage(ground.img,ground.x,ground.y,null); } //调用游戏中动态效果的方法 public void action() {//执行这个方法,游戏开始运行 //通过鼠标点击,切换游戏状态 //获取鼠标权限 this.addMouseListener(new MouseAdapter() { //点击鼠标释放后的事件 @Override public void mouseReleased(MouseEvent e) { //super.mouseReleased(e); // 0准备 1开始 2 结束 switch (state) { case 0: state=1; bird.x=110; break; case 1: bird.up();//状态是运行时,点击鼠标小鸟向上飞 break; case 2: state=0; score=0; bird.x=190; bird.y=220; bird.v=0; c1.x=bg.width; c2.x=bg.width+244; break; default: break; } } }); //地面移动 一直在移动 是一个死循环 while(true) { ground.move(bg); bird.fly();//小鸟一直在飞行 if(state==0) { }else if(state==1) { bird.down();//如果是开始状态,小鸟会自动下降 c1.move(bg); c2.move(bg); if(bird.hitColumn(c1)||bird.hitColumn(c2)|| bird.hitSky()||bird.hitGround(bg, ground)) { //如果小鸟飞行的过程中碰到了柱子1,2 或者天空,地面,切换到游戏结束状态 state=2; }else { // 没碰到时调用是否穿过通道 if(bird.addScore(c1)||bird.addScore(c2)) { score++;//分数自增 System.out.println("恭喜你的小鸟穿过了通道加一分"); } } } //设置线程休眠 每隔一段时间,线程休眠一次,相当于清空内存重新执行 try { Thread.sleep(1000/40);//控制速度的 this.repaint();//重新绘制 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }//每隔这么长时间重新执行 } } }