代码实现
创立窗口
首先创立一个游戏窗体类GameFrame,继承至JFrame,用来显示在屏幕上(window的对象),每个游戏都有一个窗口,设置好窗口题目、尺寸、布局等就能够。

/* * 游戏窗体类 */public class GameFrame extends JFrame {        public GameFrame() {        setTitle("飞机大战");//设置题目        setSize(526, 685);//设定尺寸        setLayout(new BorderLayout());        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//点击敞开按钮是关闭程序        setLocationRelativeTo(null);   //设置居中        setResizable(false); //不容许批改界面大小    }}

创立面板容器GamePanel继承至JPanel

package main;import java.awt.Graphics;import javax.swing.JFrame;import javax.swing.JPanel;/* * 画布类 */public class GamePanel extends JPanel{    GamePanel gamePanel=this;    private JFrame mainFrame=null;    //结构外面初始化相干参数    public GamePanel(JFrame frame){        this.setLayout(null);        mainFrame = frame;                mainFrame.setVisible(true);    }        @Override    public void paint(Graphics g) {            }}

再创立一个Main类,来启动这个窗口,用来启动。

package main;public class Main {    //主类    public static void main(String[] args) {        GameFrame frame = new GameFrame();        GamePanel panel = new GamePanel(frame);        frame.add(panel);        frame.setVisible(true);//设定显示    }}

右键执行这个Main类,窗口建进去了

创立菜单及菜单选项
创立菜单

private void  initMenu(){    // 创立菜单及菜单选项    jmb = new JMenuBar();    JMenu jm1 = new JMenu("游戏");    jm1.setFont(new Font("微软雅黑", Font.BOLD, 15));// 设置菜单显示的字体    JMenu jm2 = new JMenu("帮忙");    jm2.setFont(new Font("微软雅黑", Font.BOLD, 15));// 设置菜单显示的字体        JMenuItem jmi1 = new JMenuItem("开始新游戏");    JMenuItem jmi2 = new JMenuItem("退出");    jmi1.setFont(new Font("微软雅黑", Font.BOLD, 15));    jmi2.setFont(new Font("微软雅黑", Font.BOLD, 15));        JMenuItem jmi3 = new JMenuItem("操作阐明");    jmi3.setFont(new Font("微软雅黑", Font.BOLD, 15));    JMenuItem jmi4 = new JMenuItem("胜利条件");    jmi4.setFont(new Font("微软雅黑", Font.BOLD, 15));        jm1.add(jmi1);    jm1.add(jmi2);        jm2.add(jmi3);    jm2.add(jmi4);        jmb.add(jm1);    jmb.add(jm2);    mainFrame.setJMenuBar(jmb);// 菜单Bar放到JFrame上    jmi1.addActionListener(this);    jmi1.setActionCommand("Restart");    jmi2.addActionListener(this);    jmi2.setActionCommand("Exit");        jmi3.addActionListener(this);    jmi3.setActionCommand("help");    jmi4.addActionListener(this);    jmi4.setActionCommand("win");}

实现ActionListener并重写办法actionPerformed

actionPerformed办法的实现

@Overridepublic void actionPerformed(ActionEvent e) {    String command = e.getActionCommand();    UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("宋体", Font.ITALIC, 18)));    UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("宋体", Font.ITALIC, 18)));    if ("Exit".equals(command)) {        Object[] options = { "确定", "勾销" };        int response = JOptionPane.showOptionDialog(this, "您确认要退出吗", "",                JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,                options, options[0]);        if (response == 0) {            System.exit(0);        }     }else if("Restart".equals(command)){        if(startFlag){            Object[] options = { "确定", "勾销" };            int response = JOptionPane.showOptionDialog(this, "游戏中,您确认要从新开始吗", "",                    JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,                    options, options[0]);            if (response == 0) {                //须要先完结游戏                realGameEnd(1);                restart();            }         }else{            restart();        }    }else if("help".equals(command)){        JOptionPane.showMessageDialog(null, "游戏开始后,要先动鼠标到飞机处,触发挪动成果,而后飞机就会追随鼠标挪动!",                "提醒!", JOptionPane.INFORMATION_MESSAGE);    }else if("win".equals(command)){        JOptionPane.showMessageDialog(null, "得分1000,获得胜利!",                "提醒!", JOptionPane.INFORMATION_MESSAGE);    }}


创立背景
在GamePanel类中重写paint办法,绘制背景图即可

//绘图办法@Overridepublic void paint(Graphics g) {    gameHeight = this.getHeight();    gameWidth = this.getWidth();    //绘制背景    g.drawImage((BufferedImage)imageMap.get("bg"), 0, -150, null);}


开启主线程
主线程,用来重绘页面,重绘全副交给主线程,主线程调用 repaint办法就行,要产生动画就要靠这个repaint。

//刷新线程,用来从新绘制页面private class RefreshThread implements Runnable {    @Override    public void run() {        while (startFlag) {            repaint();            try {                Thread.sleep(50);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

在GamePanel的结构外面启动这个主线程

有了这个主线程刷新,待会咱们更新飞机的地位,飞机就会挪动,不须要另外的代码去调用repaint办法了(这是我的做法,仅供参考)。

创立我方飞机
创立MyPlane类,属性有坐标x、y,宽高、图片、是否存活、是否能够挪动等;办法次要有绘制、挪动、射击等。

public class MyPlane {    private int x = 0;    private int y = 0;    private int width = 0;    private int height = 0;    private BufferedImage image = null;    private GamePanel panel=null;    private HashMap imageMap=null;    private boolean alive=true;    private boolean canMove=false;    private int key=1;    private HashMap boomImageMap=null;    private boolean hitFlag=false;//正在碰撞        public MyPlane(int x,int y,int width,int height,GamePanel panel) {        this.x=x;        this.y=y;        this.width=width;        this.height=height;        this.panel=panel;        this.imageMap=panel.imageMap;        this.image=(BufferedImage)imageMap.get("myplane1");        this.boomImageMap=panel.mypalneBoomImageMap;            }    //绘制    public void draw(Graphics g) {        g.drawImage(image, x, y, width,height, null);    }}

创立(这里只是创立好了飞机对象,须要绘制)

//创立本人飞机private void initMyPlane() {    myPlane = new MyPlane(200, 530, 132, 86, this);}

在paint办法中绘制

//绘图办法@Overridepublic void paint(Graphics g) {    gameHeight = this.getHeight();    gameWidth = this.getWidth();    //绘制背景    g.drawImage((BufferedImage)imageMap.get("bg"), 0, -150, null);        //绘制飞机    if(myPlane!=null){        myPlane.draw(g);    }}


鼠标事件监听
退出监听是为了让飞机追随鼠标挪动,我这里定的规定是第一次鼠标必须挪动到飞机上,而后飞机才会追随。

代码外面用一个属性canMove来管制,默认是false,只有鼠标第一次移入到飞机上时,这个属性设置为true,而后就能够追随鼠标挪动了。

//鼠标事件的创立private void createMouseListener() {    MouseAdapter mouseAdapter = new MouseAdapter() {        @Override        public void mouseMoved(MouseEvent e) {            int x = e.getX();            int y = e.getY();            if(myPlane==null) return ;            //飞机第一次是不容许挪动的,只有飞机的canMove为true才去追随            if(myPlane.isCanMove()){                myPlane.move(x,y);                return;            }            //判断鼠标的移入,如果挪动到飞机上则canMove设置为true            if(myPlane.isPoint(x,y)){                myPlane.setCanMove(true);            }        }    };    addMouseMotionListener(mouseAdapter);    addMouseListener(mouseAdapter);}

来实现一下MyPlane的move办法,这里解决了边界,保障飞机不出界,同时保障鼠标在飞机的两头地位

//飞机追随鼠标挪动public void move(int x,int y) {    //判断范畴,当横向挪动在窗口范畴内    if(x-width/2>=0 && x<=panel.getWidth()-width/2){        this.x=x-width/2;    }    //判断范畴,当纵向挪动在窗口范畴内    if(y-height/2>=0 && y<=panel.getHeight()-height/2){        this.y=y-height/2;    }}


创立子弹类
属性也就是坐标、宽高这些,给子弹退出挪动办法

//挪动void move(){    new Thread(new Runnable() {        @Override        public void run() {            while(alive){                y-=speed;                if(y<=0){                    clear();                }                                try {                    Thread.sleep(50);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }    }).start();}

飞机类退出射击办法,200毫秒创立一发子弹

//射击void shoot() {    new Thread(new Runnable() {        @Override        public void run() {            while(alive){                //创立子弹                createBullet();                try {                    Thread.sleep(200);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        }        private void createBullet() {            Bullet bullet = new Bullet(x+width/2-10, y, 20, 30, panel);            panel.bulletList.add(bullet);            new MusicPlayer("/music/shoot.wav").play();        }    }).start();}

别忘记在paint办法外面绘制子弹进去

//绘制子弹Bullet bullet=null;for (int i = 0; i < bulletList.size(); i++) {    bullet = (Bullet)bulletList.get(i);    bullet.draw(g);}


创立敌机
创立抽象类Plane

package main;import java.awt.Graphics;public abstract class Plane {    public abstract void move();    public abstract void draw(Graphics g);    public abstract void boom();    public abstract void clear();        abstract int getX();    abstract int getY();    abstract int getWidth();    abstract int getHeight();}

创立敌机子类

有个非凡一点的中央: 因为有4种敌机,这里随机1、2、3、4这4个数字作为属性index,而后依据这个index来展示不同的飞机图片,当然也能够通过这个index来设置敌机不同的挪动速度,然而我为了偷懒,就全副一样的挪动速度,嘿嘿。
挪动就是开启线程让y坐标减少,没什么好讲的,这里加一个飞机碰撞,就是当敌机跟我方飞机如何判断碰撞的问题。

撞机剖析(敌机与我机的撞机)



从下面几个图可看出什么?因为图片是方形的,他们的4个顶点肯定至多有一个在对方的范畴内。再看一下从右边撞击的图:



从上图看到也是这样,其余两个方向的也是一样的情理,为了稳点我还加了一种状况:

1.判断敌机的4个点是否在飞机范畴内,如果有则示意碰撞了。
2.如果1不成立,则反过来,判断我机的4个点是否在敌机的范畴内,如果是示意碰撞了。

//判断飞机与子弹是否碰撞private boolean isPoint(MyPlane plane) {    /*     *      * 两种状况     * 1.须要判断敌机的4个点是否在飞机范畴内,如果有则示意碰撞了     * 2.如果步骤1不成立,则反过来,判断我机的4个点是否在敌机的范畴内,如果是标记碰撞了    */        //形式1        //左上角    int x1 = x;    int y1 = y;    //右上角    int x2 = x+width;    int y2 = y;    //右下角    int x3 = x+width;    int y3 = y+height;    //左下角    int x4 = x;    int y4 = y+height;    //只有有一个点在范畴内,则判断为碰撞    if(comparePointMyPlane(x1,y1,plane)|| comparePointMyPlane(x2,y2,plane)||comparePointMyPlane(x3,y3,plane)||comparePointMyPlane(x4,y4,plane) ){        return true;    }        //形式1没成立则用形式2判断        //形式2    x1 = plane.getX();    y1 = plane.getY();    //右上角    x2 = plane.getX()+plane.getWidth();    y2 = plane.getY();    //右下角    x3 = plane.getX()+plane.getWidth();    y3 =plane.getY()+plane.getHeight();    //左下角    x4 = plane.getX();    y4 = plane.getY()+plane.getHeight();    if(comparePoint(x1,y1)|| comparePoint(x2,y2)||comparePoint(x3,y3)||comparePoint(x4,y4) ){        return true;    }    return false;}//用敌机的坐标来判断private boolean comparePointMyPlane(int x,int y,MyPlane plane){    //大于左上角,小于右下角的坐标则必定在范畴内    if(x>plane.getX() && y >plane.getY()        && x<plane.getX()+plane.getWidth() && y <plane.getY()+plane.getHeight()    ){        return  true;    }    return false;}//用我机的坐标来判断private boolean comparePoint(int x,int y){    //大于左上角,小于右下角的坐标则必定在范畴内    if(x>this.x && y >this.y        && x<this.x+this.width && y <this.y+this.height){        return  true;    }    return false;}

测试一下成果

遗记说击中敌机的了(原理跟方才差不多,代码间接放了)

//判断击中敌机protected void hitEnemy() {    EnemyPlane enemyPlane=null;    List enemys = panel.enemyList;    for (int i = 0; i < enemys.size(); i++) {        try {            enemyPlane = (EnemyPlane)enemys.get(i);        } catch (Exception e) {        }        if(enemyPlane==null) continue;        if(this.isPoint(enemyPlane)){                        panel.curCount+=enemyPlane.getCount();            //删除以后子弹            clear();                        //飞机爆炸            enemyPlane.boom();                        if(panel.curCount>=panel.totalCount){                panel.myPlane.setCanMove(false);                panel.gameWin();            }        }    }}//判断飞机与子弹是否碰撞private boolean isPoint(EnemyPlane plane) {    //因为子弹比飞机小,所以只须要判断子弹的4个点是否在飞机范畴内,如果有则示意碰撞了    //左上角    int x1 = x;    int y1 = y;    //右上角    int x2 = x+width;    int y2 = y;    //右下角    int x3 = x+width;    int y3 = y+height;    //左下角    int x4 = x;    int y4 = y+height;    //只有有一个点在范畴内,则判断为碰撞    if(comparePoint(x1,y1,plane)|| comparePoint(x2,y2,plane)||comparePoint(x3,y3,plane)||comparePoint(x4,y4,plane) ){        return true;    }    return false;}private boolean comparePoint(int x,int y,EnemyPlane plane){    //大于左上角,小于右下角的坐标则必定在范畴内    if(x>plane.getX() && y >plane.getY()        && x<plane.getX()+plane.getWidth() && y <plane.getY()+plane.getHeight()    ){        return  true;    }    return false;}

最初加上计分的、胜利、失败等提醒就实现了!到这里一款飞机游戏就制作实现了