Java 实现飞机大战小游戏

游戏简介:通过键盘来控制飞机移动,躲避炮弹,碰到炮弹则游戏结束!
游戏项目共有七个类,包含了一些java面向对象基础知识。

首先是MygameFrame游戏窗口类:

import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Date;


public class MygameFrame extends Frame {//游戏窗口类继承JFrame类
    boolean flag=true;//在这里设置了一个标志,来控制窗口的刷新
    Image planeImg=GameUtil.getImage("images/plane.png");
    Image bg=GameUtil.getImage("images/bg.jpg");
    Image over=GameUtil.getImage("images/over.jpg");//调用GameUtil内重写的函数得到图片
    Plane plane=new Plane(planeImg,250,250);//飞机类对象
    Shell[] shells=new Shell[10];//炮弹数组,用来存放多个炮弹
    Explode ep;//声明爆炸类对象,不需要new,在发生接触再new 
    Date startTime=new Date();//程序开始时new
    Date endTime;//等游戏结束再new
    int period;//计算游戏持续时间
    @Override
    public void paint(Graphics g) {//piant会自动被系统调用一次
        super.paint(g);
        g.drawImage(bg,1,1, this.getWidth(),this.getHeight(),null);//设置背景图片
        plane.drawSelf(g);
     for (Shell shell : shells) {//循环遍历炮弹类数组
           shell.draw(g);
       boolean p = shell.getRect().intersects(plane.getRect());//飞机与炮弹接触检测,用矩形类的intersects方法,测试对象是否接触
            if (p) {
                System.out.println("碰了");
                plane.gameover = true;
           if(ep==null){ep=new Explode(plane.x,plane.y); }
                ep.newimgs();
                ep.draw(g);
                flag=false;//线程执行的标志
                g.clearRect(0,0,500,500);//清空画布
                endTime=new Date();
              g.drawImage(over,0,25,this.getWidth(),this.getHeight(),null);//结束的背景图
                period=(int)((endTime.getTime()-startTime.getTime())/1000);
                g.setColor(Color.blue);
                g.drawString("记录"+period+"秒",10,100);
            }
        }
    }
    class PaintThread extends Thread{
        //内部类,可以调用外部类方法,多线程用于反复重画窗口
        @Override
        public void run() {
            while(flag){
                //System.out.println("窗口刷新");
                repaint();
                try {
                    Thread.sleep(40);//40ms
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public void launchFrame(){//初始化窗口
        this.setTitle("肖杰航作品");//设置标题
        this.setVisible(true);//窗口可视化
        this.setSize(Constant.GAME_WIDTH,Constant.GAME_HEIGHT); //设置窗口大小与坐标
        this.setLocation(300,300);

        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);//重写该方法,使关闭窗口后程序结束运行!
            }
        });

    new PaintThread().start();//启动重画窗口线程,因为paint()方法只会自动调用一次
        addKeyListener(new KeyMonitor());//给窗口增加键盘监听

        for (int i = 0; i <shells.length ; i++) {//初始化炮弹数组内每个炮弹对象
            shells[i]=new Shell();
        }

    }

   class KeyMonitor extends KeyAdapter{//键盘监听内部类,继承 KeyAdapter适配器,重写其中两个方法,按下与释放键


       @Override
       public void keyPressed(KeyEvent e) {
           plane.addDirection(e);
       }

       @Override
       public void keyReleased(KeyEvent e) {
           plane.minusDirection(e);
       }
   }






    public static void main(String[] args) {//主函数调用游戏窗口类
        MygameFrame f=new MygameFrame();
        f.launchFrame();

    }


    private Image offScreenImage = null;
    public void update(Graphics g) {
        if(offScreenImage == null)
            offScreenImage = this.createImage(Constant.GAME_WIDTH,Constant.GAME_HEIGHT);//这是游戏窗口的宽度和高度
        Graphics gOff = offScreenImage.getGraphics();
        paint(gOff);
        g.drawImage(offScreenImage, 0, 0, null);
    }
   }/*如果窗口类继承自:java.swing.JFrame,则本身已经不完美的解决了闪烁问题,双缓冲是用于Frame,能更好的解决闪烁问题*/

先定义一个常量类,用于存放项目常量

public class Constant{
    public static final int GAME_WIDTH=500;
    public static final int GAME_HEIGHT=500;
}

ImageIO类来实现图片加载,并且为了代码的复用性,将图片加载的方法封装到GameUtil工具类中

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;

public class GameUtil {//游戏工具类
    private GameUtil(){}
    public static Image getImage(String path){//获得图片类,输入值是路径,返回值是图片
        BufferedImage bi =null;
        try{
            URL u=GameUtil.class.getClassLoader().getResource(path);
            bi= ImageIO.read(u);
        }catch(IOException e){
            e.printStackTrace(); //在命令行打印异常信息在程序中出错的位置及原因
        }
         return bi;
    }
}

我们知道,在游戏里的物体例如飞机,炮弹有相同的共性,初始位置,长度宽度,速度等。所以我们给这些类先创建一个父类叫做游戏物品类,以便于飞机类继承。

import java.awt.*;

/**游戏内所有物体父类,因为游戏里的物体属性类似
 *
 */
public class GameObject {
     Image img;//图片
     double x,y;//坐标
     int speed;//移动速度
     int width,height;

    public GameObject() {//无参构造
    }

    public GameObject(Image img, double x, double y) {//构造器
        this.img = img;
        this.x = x;
        this.y = y;
    }

    public void drawSelf(Graphics g){//定义一个画图函数
          g.drawImage(img,(int)x,(int)y,null);
    }

    public Rectangle getRect(){//返回图片所在矩形,用于检测碰撞
        return new Rectangle((int)x,(int)y,width,height);//java的矩形类

    }
}

在父类里特地写了getRect()方法,得到物体的位置矩形框,以便后面判断飞机与炮弹的接触
然后写完父类就可以创建子类飞机类

import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

ublic class Plane extends GameObject {

    boolean left,right,up,down;//设置标志,用于键盘监听
     boolean gameover=false;//设置标志,用于判断何时结束绘图
    public void drawSelf(Graphics g){//重写父类中的方法
      if(gameover==false){
        g.drawImage(img,(int)x,(int)y,null);
        if(left){//当键盘左键,横坐标左移一个单位
            x=x-speed;
        }
        if(right){
            x=x+speed;
        }
        if(up){
            y=y-speed;
        }
        if(down){
            y=y+speed;
        }
    }}

    public Plane(Image img,double x,double y){
        this.img=img;
        this.x=x;
        this.y=y;
        this.speed=10;
        this.width=img.getWidth(null);//宽度与你导入的飞机图片一致
        this.height=img.getHeight(null);
      }

      public void addDirection(KeyEvent e){//方法实现按下某个键,加方向
        switch(e.getKeyCode()){
            case KeyEvent.VK_LEFT:left=true;break;
            case KeyEvent.VK_UP:up=true;break;
            case KeyEvent.VK_RIGHT:right=true;break;
            case KeyEvent.VK_DOWN:down=true;break;
        }

      }
      public void minusDirection(KeyEvent e){//方法实现松开某个键,不再加方向
          switch(e.getKeyCode()){
              case KeyEvent.VK_LEFT:left=false;break;
              case KeyEvent.VK_UP:up=false;break;
              case KeyEvent.VK_RIGHT:right=false;break;
              case KeyEvent.VK_DOWN:down=false;break;
        }

    }
}

接下来是炮弹类,一样继承了物品类

import java.awt.*;

/*炮弹类
 */
public class Shell  extends GameObject{
    double degree;//角度,炮弹多了一个属性,可以各个方向移动
    public Shell(){
        x=200;//炮弹初速左边,可以设置为随机数
        y=200;
        width=10;
        height=10;
        speed=3;
        degree=Math.random()*Math.PI*2;//炮弹发射角度随机产生

    }

    public void draw(Graphics g){//自己新增的方法,因为画炮弹不用图片,与飞机类不太一样。

        Color c=g.getColor();//记录当前颜色以便还原
        g.setColor(Color.orange);//修改炮弹颜色
        g.fillOval((int)x,(int)y,width,height);//填充椭圆
        x=x+speed*Math.cos(degree);//炮弹移动轨迹
        y=y+speed*Math.sin(degree);
        if(x<=0||x>=Constant.GAME_WIDTH-width){//碰左右边界,角度对称变化
            degree=Math.PI-degree;
        }
        if(y<=30||y>=Constant.GAME_HEIGHT-height){//碰上下边界,角度反向变化
            degree=-degree;
        }
            g.setColor(c);}
    }

最后为了实现飞机与炮弹接触产生动画效果,写了一个爆炸类
类里使用图片数组轮播实现动态效果,使用数组保存图片信息

import java.awt.Graphics;
import java.awt.Image;

/*
 * 爆炸类
 */
public class Explode {
    double x,y;//设置效果显示位置
    Image[] imgs = new Image[16];
    public void newimgs() {
   for(int i=0;i<16;i++){
      imgs[i]=GameUtil.getImage("images/exp/e"+(i+1)+".gif");
}//图片命名规范,以e1~e16
    }
     int count;
    //计数器count来控制到底画哪张图片
    public void draw(Graphics g){
        if(count<=15){
            g.drawImage(imgs[count], (int)x, (int)y, null);
            count++;
        }
    }

    public Explode(double x,double y){
        this.x = x;
        this.y = y;
    }
}

效果图:
Java 实现飞机大战小游戏_第1张图片
Java 实现飞机大战小游戏_第2张图片

你可能感兴趣的:(java)