游戏界面使用JFrame和JPanel构建。背景图通过BG类绘制。英雄机和敌机在界面上显示并移动。子弹从英雄机发射并在屏幕上移动。游戏有四种状态:READY、RUNNING、PAUSE、GAMEOVER。状态通过鼠标点击进行切换:点击开始游戏(从READY变为RUNNING)。点击重新开始游戏(从GAMEOVER变为READY)。鼠标移出窗口时游戏暂停(从RUNNING变为PAUSE)。鼠标移入窗口时游戏继续(从PAUSE变为RUNNING)。开局默认玩家生命值为3,分数为0。每隔一定时间随机生成敌机(小飞机、大飞机、小飞碟)。敌机在屏幕上移动,并与英雄机进行碰撞检测。英雄机可以发射子弹。子弹在屏幕上移动,并与敌机进行碰撞检测。英雄机与敌机发生碰撞时,英雄机会损失一条生命值。子弹与敌机发生碰撞时,敌机会被摧毁,玩家的分增加。当子弹与奖励机发生碰撞时,奖励机会被摧毁,玩家随机获得两张奖励(生命值加1、双倍子弹)。在屏幕左上角显示当前的生命值和得分。生命值减少到0时,游戏进入结束状态。再次点击,重新开始游戏。
1.图片素材(百度网盘分享)
链接:https://pan.baidu.com/s/10QiTcddI_Zxw5jbn8fmOpA?pwd=huan
提取码:huan
2.主页面world.java
package plane.gzeu;import javax.swing.*;import java.awt.*;import java.awt.event.MouseAdapter;import java.awt.event.MouseEvent;import java.util.Arrays;import java.util.Timer;import java.util.TimerTask;public class World extends JPanel { //ImageIcon类:保存图片的路径----->读取图片// ImageIcon icon = new ImageIcon("images\\background.png");// BG bg = new BG(0,0,420,700,2);// Airplane airplane = new Airplane(100,200,300,620,2);// Bee bee = new Bee(200,100,200,700,2); BG bg = new BG(); EnemyPlane [] planes; Hero hero; int index; //用来控制敌机的数量 Bullet[] bullets; //创建子弹的数组 int life; //英雄机的生命值 int score; //分数 //定义游戏的四种状态:准备,运行,暂停,结束(常量) public static final int READY = 0; public static final int RUNNING = 1; public static final int PAUSE = 2; public static final int GAMEOVER = 3; //游戏的初始状态 int state = READY; // Airplane [] airPlanes = new Airplane[10];// Bigairplane [] bigAirPlanes = new Bigairplane[10];// Bee [] bee = new Bee[10];// Bom [] bom = new Bom[4];// Bullet [] bullets = new Bullet[3];// Hero [] hero =new Hero[2]; //初始化方法 public void init(){ bg = new BG(); hero = new Hero(); planes = new EnemyPlane[0]; //默认一个敌机都没有 bullets = new Bullet[0]; //默认一个子弹都没有 index = 0; //默认从0开始 life = 3; //默认3条命 score = 0; //分数为0 } public World() { init();// for(int i=0;i<planes.length;i++){// if(i%2==0){// planes[i]=new Airplane();// }// else if(i%5==0 && i%2!=0){// planes[i] = new Bee();// }// else {// planes[i] = new Bigairplane();// }// }// for(int i =0;i<airPlanes.length;i++){// airPlanes[i] = new Airplane();// bigAirPlanes[i]=new BigAirPlane();// bee[i]=new Bee();// bom[i]=new Bom();// bullets[i]=new Bullet();// hero[i]=new Hero(); } //生成敌机的方法 public void createPlane(){ if(index%16==0){ int n = (int)(Math.random()*10); EnemyPlane plane; switch (n){ case 9 : plane = new Bee(); //10%生成小飞碟 break; case 8 : case 7 : plane = new Bigairplane(); //20%生成大飞机 break; default: plane = new Airplane(); //70%生成小飞机 } //将敌机存入数组中之前,要先对数组进行扩容处理 planes = Arrays.copyOf(planes,planes.length+1); //将生存的敌机放入数组的最后一个位置 planes[planes.length-1] = plane; } }// for(int i =0;i<bigAirPlanes.length;i++){// bigAirPlanes[i]=new Bigairplane();// }// } //绘制图片printing @Override public void paint(Graphics g) { //paint挂载到当前类,当前类实例化(创建对象)时自动调用// super.paint(g);// icon.paintIcon(this,g,0,y1++); //绘制背景,默认第一个绘制// bg.icon.paintIcon(this,g,(int)bg.x,(int)bg.y);// bg.move();// airplane.icon.paintIcon(this,g,(int)airplane.x,(int)airplane.y);// bee.icon.paintIcon(this,g,(int)bee.x,(int)bee.y); bg.painting(g); hero.painting(g); for(int i=0;i<planes.length;i++){ planes[i].painting(g); } for(int i=0;i<bullets.length;i++){ bullets[i].painting(g); } g.setColor(new Color(255,255,255)); //设置字体 g.setFont(new Font("微软雅黑",Font.BOLD,20)); g.drawString("生命值"+life,20,20); g.drawString("分数"+score,20,40); if(state==READY){ Images.start.paintIcon(this,g,0,0); } if(state==PAUSE){ Images.pause.paintIcon(this,g,0,0); } if(state==GAMEOVER){ Images.gameover.paintIcon(this,g,0,0); }// for(int i =0;i<airPlanes.length;i++){// airPlanes[i].painting(g);// bigAirPlanes[i].painting(g);// bee[i].painting(g);// bom[i].painting(g);// bullets[i].painting(g);// hero[i].painting(g);// }// for(int i =0;i<bigAirPlanes.length;i++){// bigAirPlanes[i].painting(g);// } repaint();//刷新窗口 } public static void main(String[] args) { //1.显示画框(外层):JFrame JFrame jf = new JFrame(); //2.显示面板:Jpanel World jp =new World(); //3.将面板放入画框中: jf.add(jp); //对窗口进行设置 jf.setTitle("我的窗口");//设置标题 jf.setSize(436,700); //设置窗口大小 jf.setLocationRelativeTo(null); //居中显示 jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //设置关闭窗口后自动结束程序 //4.显示窗口 jf.setVisible(true); jp.action(); } //唤醒定时器的方法 public void action(){ java.util.Timer timer = new Timer(); MyTesk mt = new MyTesk(); timer.schedule(mt,1000,1000/50); this.addMouseListener(new MouseAdapter() { //监听鼠标点击 @Override public void mouseClicked(MouseEvent e) { if(state==READY){ state=RUNNING; } if(state==GAMEOVER){ state=READY; init(); } } //鼠标移除 @Override public void mouseExited(MouseEvent e) {// System.out.println("鼠标移出"); if(state==RUNNING){ state=PAUSE; } } //鼠标移入 @Override public void mouseEntered(MouseEvent e) { if(state==PAUSE){ state=RUNNING; } } }); //匿名内部类 MouseAdapter mouseAdapter = new MouseAdapter() { @Override public void mouseMoved(MouseEvent e) { if(state==RUNNING){ int x = e.getX(); int y = e.getY(); hero.move(x,y); } } }; //安装监听器 this.addMouseMotionListener(mouseAdapter); } public void createBullet(){ if(!hero.isLiving()){ //英雄机死亡不发射子弹 return; } if(index%17==0){ Bullet[] bullets1 = hero.double_fire(); //俩个数组的合并 Bullet[] arr = Arrays.copyOf(bullets,bullets.length+bullets1.length); System.arraycopy(bullets1,0,arr,bullets.length,bullets1.length); bullets = arr;// Bullet bullet =hero.fire(); //英雄机调用fire方法生成子弹// //将子弹放入数组中// bullets = Arrays.copyOf(bullets,bullets.length+1); //子弹扩容// bullets[bullets.length-1] = bullet; } } //加分的方法 public void addScore(FlyingObjesct plane){ if(plane.isDead()){ if(plane instanceof Enemy){ Enemy enemy = (Enemy) plane; //需要做一个强制转化 score += enemy.getScore(); } if(plane instanceof Aword){ Aword aword = (Aword) plane; int type =aword.getAword(); if(type == Aword.DOUBLE_FIRE){ hero.getdouble_fire(); } if(type == Aword.LIFE){ life++; } } } } //检测敌机与英雄机的碰撞 public void hero_hit(){ if(hero.isLiving()){ for(int i=0;i<planes.length;i++){ if(!planes[i].isLiving()){ continue; } if(hero.bong(planes[i])){ hero.goDead(); planes[i].goDead(); break; } } } else if(hero.isWait()){ //僵尸状态 if(life>1){ //重新开始 hero = new Hero(); //清屏 for(int i=0;i<planes.length;i++){ planes[i].goDead(); } life--; } else{ life--; state=GAMEOVER; } } } //检测每个敌机与子弹的碰撞情况 public void hit(){ for(int i=0;i<bullets.length;i++){ Bullet bullet = bullets[i]; //拿出每一颗子弹 if(!bullet.isLiving()){ continue; } for(int j=0;j<planes.length;j++){ FlyingObjesct p = planes[j]; if(!p.isLiving()){ continue; } if(p.bong(bullet)){ //被击中 p.attack(); bullet.goDead(); addScore(p); } } } } //清理爆炸的飞机 public void clean(){ //清除飞机 EnemyPlane[] living = new EnemyPlane[planes.length]; int index = 0; for(int i=0;i<planes.length;i++){ //遍历敌机数组 if(planes[i].isWait() || planes[i].outOfBound()){ //如果是等待状态的效果就跳过 continue; } living[index++] = planes[i]; //将不是等待状态的敌机存在living里面 } planes = Arrays.copyOf(living,index); //清除子弹 Bullet[] livingBullet = new Bullet[bullets.length]; index = 0; for(int i=0;i<bullets.length;i++){ if(bullets[i].isDead() || bullets[i].outOfBound()){ //如果是已经爆炸的子弹就跳过 continue; } livingBullet[index++] = bullets[i]; } bullets = Arrays.copyOf(livingBullet,index); } class MyTesk extends TimerTask { @Override public void run() { index++; if(state == RUNNING){ createPlane(); //调用生成敌机的方法 createBullet(); //调用生成子弹的方法 hit();//调用子弹击打的效果 clean(); //调用清理敌机方法 hero_hit(); bg.move(); for(int i =0;i<planes.length;i++){ planes[i].move(); } for(int i =0;i<bullets.length;i++){ bullets[i].move(); } } } }}
3.小飞机类Airplane.java
package plane.gzeu;import javax.swing.*;//飞机类public class Airplane extends EnemyPlane implements Enemy{ // 实现接口时,要实现接口中的方法 public Airplane(){ this.images = Images.airplane; w = images[0].getIconWidth(); h = images[0].getIconHeight(); x=(int)(Math.random()*(420-images[0].getIconWidth())); y=-2*images[0].getIconHeight(); this.step=(Math.random()*2.5+0.7); this.icon=new ImageIcon("images\\airplane1.png");// this.icon = Images.airplane[0]; } public Airplane(double x, double y, double w, double h,double speed) {// super(x, y, w, h); this.step=speed; this.icon=Images.airplane[0]; } @Override public int getScore() { return 10; }}
4.大飞机类Bigairplane
package plane.gzeu;//大飞机类public class Bigairplane extends EnemyPlane implements Enemy{ public Bigairplane(double x, double y, double w, double h,double step) {// super(x, y, w, h); this.step=step; this.icon = Images.bigairplane[0]; } public Bigairplane(){ this.images = Images.bigairplane; //初始化数组 w = images[0].getIconWidth(); h = images[0].getIconHeight(); x=(int)(Math.random()*(420-images[0].getIconWidth())); y=-2*images[0].getIconHeight(); this.step=(Math.random()*3.5+0.7);// this.icon = Images.bigairplane[0]; life = 4; } @Override public int getScore() { return 50; }}
5.英雄机类Hero.java
package plane.gzeu;import javax.swing.*;import static plane.gzeu.Images.bullet;public class Hero extends EnemyPlane{ public Hero(){ this.images = Images.hero; x=431/2-images[0].getIconWidth()/2; y=510; w=images[0].getIconWidth(); h=images[0].getIconHeight();// this.speed=0.2; this.icon = Images.hero[0]; } public Hero(double x, double y, double w, double h,double step) {// super(x, y, w, h); this.step=step;// this.icon = new ImageIcon("images\\hero1.png"); //设置了Images类直接调用类名就可以引用图片 this.icon = Images.hero[0]; } @Override public void move() {} //接收鼠标的坐标 public void move(int x,int y) { this.x= x-images[0].getIconWidth()/2; this.y = y-images[0].getIconHeight()/2; } /* * 子弹的位置(在英雄机上) b.x = h.x-images[0].getIconWidth()/2; b.y=h.y * 子弹的移动:向上:b.y-=b.step; * 子弹是无限的:数序扩容 * 射击方法:当英雄机调用一次射击方法时,就发射一个子弹 * */ //射击的方法 public Bullet fire(){ double x = this.x+w/2-4; //获取英雄机的x坐标 Bullet bullet = new Bullet(x,y);//将处理过后的坐标传给子弹 return bullet; //将子弹返回 } //获取双倍子弹的方法 int doubleTime = 0; // 双倍子弹的时间,20次 public void getdouble_fire(){ doubleTime = 20; } //双倍子弹方法 public Bullet[] double_fire(){ if(doubleTime>0){ double x = this.x+w/2-7; double x1 = this.x+w/2-2; //获取英雄机的x坐标 Bullet bullet1 = new Bullet(x,y); Bullet bullet2 = new Bullet(x1,y);//将处理过后的坐标传给子弹 Bullet [] bullets = new Bullet[]{bullet1,bullet2};// return new Bullet[]{bullet1,bullet2}; return bullets; } else { double x = this.x+w/2-4; //获取英雄机的x坐标 Bullet bullet1 = new Bullet(x,y); return new Bullet[]{bullet1}; }// } //测试// public static void main(String[] args) {// Hero hero = new Hero();// hero.move(200,300);// Bullet bullet = hero.fire();// System.out.println(bullet.x+" "+bullet.y);// }}
6.敌机类Bee.java
package plane.gzeu;import javax.swing.*;public class Bee extends EnemyPlane implements Aword { double speed1; public Bee(){ this.images = Images.bee; w = images[0].getIconWidth(); h = images[0].getIconHeight(); x=(int)(Math.random()*(420-images[0].getIconWidth())); y=-2*images[0].getIconHeight(); this.step=(Math.random()*4.5+2.5); this.speed1=(Math.random()*3.5+0.5);// this.icon=new ImageIcon("images\\bee0.png"); this.icon=Images.bee[0]; life = 6; } public Bee(double x, double y, double w, double h,double step) {// super(x, y, w, h); this.step=step; this.icon = Images.bee[0]; } @Override public void move() { y+=speed1; x+=step; if(x>431-images[0].getIconWidth()){ step = -step; } if(x<=0){ step = -step; } } @Override public int getAword() { return Math.random()>0.5?DOUBLE_FIRE:LIFE; }}
7.背景类BG.java
package plane.gzeu;import javax.swing.*;import java.awt.*;//背景类(子类)//继承FlyingObjesct类,得到FlyingObjesct类的方法及属性public class BG extends FlyingObjesct { double y0; public BG(){ x = 0; y = 0; icon = Images.bg; w = icon.getIconWidth(); h = icon.getIconHeight(); y0 = -h; step = 2; } public BG(double x, double y, double w, double h,double step) { super(x, y, w, h); //重载:调用父类的构造方法,实现方法复用 this.step = step; this.icon = Images.bg; //背景是固定的 } public void painting(Graphics g){ icon.paintIcon(null,g,(int)x,(int)y); icon.paintIcon(null,g,(int)x,(int)y0); } @Override public void move() { y+=step; y0+=step; if(y>=h){ y=-h; } if(y0>=h){ y0=-h; } }}
8.爆炸类Bom.java
package plane.gzeu;import javax.swing.*;public class Bom extends EnemyPlane{ public Bom(){ x=(int)(Math.random()*370); y=(int)(Math.random()*370); this.step = 0.2; this.icon = Images.bom[0]; } public Bom(double x, double y, double w, double h, double speed) {//要重写父类的构造方法。新增一个速度// super(x, y, w, h);//重载:调用父类的构造方法,实现方法复用 this.step=speed; this.icon=Images.bom[0]; }}
9.子弹类Bullet.java
package plane.gzeu;import javax.swing.*;//继承FlyingObjesct类,得到FlyingObjesct类的方法及属性public class Bullet extends EnemyPlane { public Bullet(double x,double y){ this.icon = Images.bullet; w = icon.getIconWidth(); h = icon.getIconHeight(); this.x = x; this.y = y; step = 2; } public Bullet(){ x=(int)(Math.random()*370); y=(int)(Math.random()*370); this.step = 0.2; this.icon = Images.bullet; } public Bullet(double x, double y, double w, double h, double step) {// super(x, y, w, h); //重载:调用父类的构造方法,实现方法复用 this.step = step; this.icon = Images.bullet; //背景是固定的 } @Override public void move() { y-=step; }}
10.三种敌机总类EnemyPlane.java
package plane.gzeu;public abstract class EnemyPlane extends FlyingObjesct { @Override public void move() { y+=step; }}
11.飞行物的总类(父类)FlyingObjesct.java
package plane.gzeu;import javax.swing.*;import java.awt.*;//飞行物的总类(父类)public abstract class FlyingObjesct { public static final int LIVING = 1; //活着 public static final int DEAD = 0; //死亡 public static final int WAIT = -1; //等待死亡(播放爆炸动画) //飞机默认是活着的 public int state = LIVING; //飞机的生命值 public int life = 2; public double x,y,step,w,h;//step速度 public ImageIcon icon; //图片// Images images; public ImageIcon[] images; //数组 public int index = 0; public ImageIcon [] boms = Images.bom; //存放爆炸效果的图片 //赋初值:构造方法 //快速生成构建方法:快捷键Alt+Insert public FlyingObjesct(double x, double y, double w, double h) { this.x = x; this.y = y; this.w = w; this.h = h; } //当飞机击打一次,生命值减一,当生命值为0时,进入死亡状态 public boolean attack(){ if(life>0){ life--; if(life==0){ state = DEAD; } return true; } return false; }//让敌机快速去死 public boolean goDead(){ if(state==LIVING){ life = 0; state = DEAD; return true; }else{ return false; } } //判断飞机的三种状态 public boolean isLiving(){ return state==LIVING; //判断是否活着 } public boolean isDead(){ return state==DEAD; //判断是否死了 } public boolean isWait(){ return state==WAIT; //判断是否等待死亡 } /*实现动画效果 * 1.用数组存储动画效果图片 * 2.初始化数组 * 3.定义一个计数器,作为切换图片的控制条件 * 4.写一个方法,实现切换图片的操作 * */ int i; public void nextImage(){ switch (state){ case LIVING: if(images == null) { return; } icon = images[index++/300%images.length]; //0-[数组的长度-1] break; case DEAD: if(boms==null){ return; } if(i++/300==boms.length){ state = WAIT; return; } icon = boms[i++/300]; } } public FlyingObjesct(){}//无参构造 //移动 public abstract void move(); //绘制图形 方法 public void painting(Graphics g){ nextImage(); icon.paintIcon(null,g,(int)x,(int)y); } //检测敌机是否碰到子弹:判断条件中心距c=H/2+h/2 public boolean bong(FlyingObjesct bullet){ FlyingObjesct p =this; double a = Math.min(p.w,p.h)/2; double b = Math.min(bullet.w,bullet.h)/2; double x1=p.x+p.w/2; double y1=p.y+p.h/2; double x2=bullet.x+bullet.w/2; double y2=bullet.y+bullet.h/2; double c = Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); return c<=a+b; } //清除越界的敌机和子弹条件: y<-h-100 || y>700+100 public boolean outOfBound(){ if(y<-h-100){ return true; }else if(y>700+100){ return true; } return false; }}
12.图片类Images.java
package plane.gzeu;import javax.swing.*;public class Images { public static ImageIcon bg; public static ImageIcon bullet; public static ImageIcon start; public static ImageIcon pause; public static ImageIcon gameover; public static ImageIcon[] hero; public static ImageIcon[] bigairplane; public static ImageIcon[] bee; public static ImageIcon[] airplane; public static ImageIcon[] bom; static { airplane = new ImageIcon[2]; airplane[0] = new ImageIcon("images/airplane0.png"); airplane[1] = new ImageIcon("images/airplane1.png"); bigairplane = new ImageIcon[2]; bigairplane[0] = new ImageIcon("images/bigairplane0.png"); bigairplane[1] = new ImageIcon("images/bigairplane1.png"); bee = new ImageIcon[2]; bee[0] = new ImageIcon("images\\bee0.png"); bee[1] = new ImageIcon("images\\bee1.png"); bom = new ImageIcon[4]; bom[0] = new ImageIcon("images/bom1.png"); bom[1] = new ImageIcon("images/bom2.png"); bom[2] = new ImageIcon("images/bom3.png"); bom[3] = new ImageIcon("images/bom4.png"); bg = new ImageIcon("images\\background.png"); bullet = new ImageIcon("images\\bullet.png"); start = new ImageIcon("images\\start.png"); pause = new ImageIcon("images\\pause.png"); gameover = new ImageIcon("images\\gameover.png"); hero = new ImageIcon[2]; hero[0] = new ImageIcon("images\\hero0.png"); hero[1] = new ImageIcon("images\\hero1.png"); } //测试图片是否传过来了 public static void main(String[] args) { System.out.println(start.getImageLoadStatus()); //如果输出8那图片传过来了,输出其他数字有问题 System.out.println(bg.getImageLoadStatus()); System.out.println(bom[0].getImageLoadStatus()); System.out.println(bee[1].getImageLoadStatus()); System.out.println(airplane[1].getImageLoadStatus()); }}
13.获得奖励的接口Aword.java
package plane.gzeu;//获得奖励的接口public interface Aword { int DOUBLE_FIRE = 1; //第一种奖励,双倍火力 int LIFE = 2; //加生命值 int getAword(); //获得奖励的方法}
14.获得分数的接口Enemy.java
package plane.gzeu;//获得分数的接口public interface Enemy { //加分// int a = 5;//都是静态常量// public abstract void add(); //都是抽象方法 public int getScore(); //获得分数的方法}
运行界面:
开始界面
存活界面:
暂停界面
结束界面