Java Swing 飞机大战游戏

呀~好久没有更新文章啦~~这次我们用Java的Swing图形界面框架写一个飞机大战小游戏

 飞机大战游戏分为几个部分:

com.mr // 主目录

        main

                Constants // 常量

                Start // 程序入口

        model

                Bullet // 子弹

                Enemy // 敌机

                Player // 玩家

        service

                Fresh // 刷新帧

                MouseMotion // 鼠标移动事件

                ScoreRecorder // 分数记录器

        view

                GameFrame // 窗体

                GamePanel // 面板

程序目录如上图 

首先来写Start.java的代码

package com.mr.main;

import com.mr.view.GameFrame;

public class Start {
    public static void main(String[] args){
        GameFrame gameFrame=new GameFrame();
    }
}

上面的代码导入GameFrame,实例化

接下来是GameFrame中的代码

package com.mr.view;

import com.mr.service.MouseMotion;

import javax.swing.*;
import java.awt.*;

public class GameFrame extends JFrame {
    private Container container;
    private GamePanel panel;
    private MouseMotion mm;
    public static final int SPACING=15;

    public GameFrame(){
        setBounds(300,200,720+SPACING,960);
        setTitle("FlightWar");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        container=getContentPane();
        container.removeAll();
        panel=new GamePanel();
        mm=new MouseMotion(panel);
        addMouseMotionListener(mm);
        addKeyListener(panel);
        container.add(panel);
        container.validate();
        setVisible(true);
    }
}

GameFrame里导入了javax.swing的所有类和java.awt的所有类,还有待会要写的MouseMotion鼠标移动事件监听类,GameFrame继承于JFrame,对窗体进行初始化,并实例化GamePanel主面板,添加到窗口

接下来我们要来写GamePanel的代码

package com.mr.view;

import com.mr.main.Constants;
import com.mr.model.Bullet;
import com.mr.model.Enemy;
import com.mr.model.Player;
import com.mr.service.Fresh;
import com.mr.service.ScoreRecorder;

import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

public class GamePanel extends JPanel implements KeyListener {
    private BufferedImage img;
    private Graphics2D g2;
    private BufferedImage backgroundImage;
    private Thread thread;
    private Player player;
    private final int INTERVAL;
    private int timer_create_bullet;
    private int timer_create_enemy;
    private ArrayList<Bullet> bullets;
    private ArrayList<Enemy> enemies;
    private int score;
    private int health;
    private boolean record=false;
    private int maxScore;
    public GamePanel(){
        img=new BufferedImage(720,960,BufferedImage.TYPE_INT_BGR);
        g2=img.createGraphics();
        File tmp=new File(Constants.IMAGES+"/background.png");
        try {
            backgroundImage=ImageIO.read(tmp);
        } catch (IOException e) {
            e.printStackTrace();
        }
        player=new Player();
        INTERVAL=Fresh.INTERVAL;
        timer_create_bullet=0;
        score=0;
        health=5;
        bullets=new ArrayList<>();
        enemies=new ArrayList<>();
        thread=new Fresh(this);
        thread.start();
    }
    private void paintImage(){
        g2.drawImage(player.img,player.x,player.y,this);
        if (!isLose()) {
            timer_create_bullet+=INTERVAL;
            timer_create_enemy+=INTERVAL;
            if (timer_create_bullet>=125) {
                timer_create_bullet=0;
                Bullet newBullet=new Bullet(player.x+player.img.getWidth()/2,player.y);
                bullets.add(newBullet);
            }
            if (timer_create_enemy>=700) {
                timer_create_enemy=0;
                Enemy newEnemy=new Enemy();
                enemies.add(newEnemy);
            }
            for (Bullet bullet:bullets) {
                g2.drawImage(bullet.img,bullet.x,bullet.y,this);
            }
            for (Enemy enemy:enemies) {
                g2.drawImage(enemy.img,enemy.x,enemy.y,this);
            }
            g2.setColor(Color.red);
            g2.setFont(new Font("simhei",Font.PLAIN,24));
            g2.drawString("得分:"+String.valueOf(score),10,30);
            g2.setColor(Color.blue);
            g2.drawString("生命值:"+String.valueOf(health),10,60);
        } else {
            g2.setColor(Color.red);
            g2.setFont(new Font("simhei",Font.PLAIN,24));
            g2.drawString("得分:"+String.valueOf(score),10,30);
            g2.setColor(Color.blue);
            g2.drawString("生命值:"+String.valueOf(health),10,60);
            g2.setColor(Color.green);
            g2.drawString("纪录:"+String.valueOf(maxScore),10,90);
        }
    }
    public void paint(Graphics g){
        g2.drawImage(backgroundImage,0,0,720,960,this);
        if (!isLose()) {
            freshBullets();
            freshEnemies();
            collide();
        } else {
            if (!record) {
                ScoreRecorder scoreRecorder=new ScoreRecorder();
                maxScore=scoreRecorder.start(score);
            }
            bullets.clear();
            enemies.clear();
        }
        paintImage();
        g.drawImage(img,0,0,this);
    }
    private void freshBullets(){
        for (int i=0;i<bullets.size();i++) {
            Bullet bullet=bullets.get(i);
            bullet.move();
            if (bullet.y+bullet.img.getHeight()<=0){
                bullets.remove(i);
                i--;
            }
        }
    }
    private void collide(){
        for (int i=0;i<enemies.size();i++) {
            if (enemies.get(i).getBound().intersects(player.getBound())) {
                enemies.remove(i);
                i--;
                health--;
                continue;
            }
            for (int j=0;j<bullets.size();j++) {
                Enemy enemy=enemies.get(i);
                if (enemy.getBound().intersects(bullets.get(j).getBound())) {
                    enemy.health--;
                    if (enemy.health<=0) {
                        score+=enemy.score;
                        enemies.remove(i);
                    }
                    bullets.remove(j);
                    i--;
                    break;
                }
            }
        }
    }
    private void freshEnemies(){
        for (int i=0;i<enemies.size();i++) {
            Enemy enemy=enemies.get(i);
            enemy.move();
            if (enemy.y>=960) {
                enemies.remove(i);
                i--;
            }
        }
    }
    public boolean isLose(){
        return health<=0;
    }
    public Player getPlayer(){
        return player;
    }
    @Override
    public void keyTyped(KeyEvent event){
        ;
    }
    @Override
    public void keyPressed(KeyEvent event){
        ;
    }
    @Override
    public void keyReleased(KeyEvent event){
        ;
    }
}

GamePanel是整个程序中特别重要的一个部分,代码量也很多,在这里面,我们导入了所有模型,com.mr.model包下的模型,com.mr.service下的刷新帧和分数记录器,还有我们写的常量,java.awt和javax.swing,流对象javax.imageio.ImageIO和java.io.File等,还有ArrayList数组等,后面代码都会用到。GamePanel继承自JPanel,实现了KeyListener接口,实现KeyListener接口需要写入3个事件监听方法KeyTyped,KeyPressed,KeyReleased,不过在飞机大战的示例中,我们不需要用到键盘事件,先空着用分号代替,接下来就要生命很多private变量,img主图片,g2为img对应的Graphics2D对象,将东西绘制在g2中,最后将主图片绘制于Panel中;Thread类型变量thread表示刷新帧的线程,Player模型,刷新间隔常量Interval,间隔创建子弹和敌机的timer_create_bullet和timer_create_enemy,存储子弹和敌机的ArrayList<Bullet>和ArrayList<Enemy>类型的bullets和enemies,分数score,生命health,是否已经将最高分写入文件的bool值,还有从文件读入的最高分。接下来我们来看看构造,首先创建一个长宽为720,960的BufferedImage对象作为主图片,创建g2,写一个tmp临时变量,储存File,然后尝试读入背景图片(这里IMAGES为com.mr.main.Constants下的公有静态字符串型常量,待会会讲到Constants下的代码),实例Player,从刷新帧方法读入刷新帧时间作为刷新率,初始化得分生命数组线程等。然后有一个paint方法,这个是重写自JPanel用于绘制的方法(自动调用),默认传入一个Graphics类型的变量g,我们不把东西画在g中,要画在我们自己创建的g2里,所以g2先绘制背景图,进行是否已经失败的判断,做出读写得分、清除子弹敌机等数组、检测碰撞collide(),刷新freshBullets()等等,然后运行paintImage方法,这个是我们自己写的方法,最后将img主图片画在g中即可,paintImage就绘制一些东西,代码不难理解。freshBullets和freshEnemies方法就是刷新子弹和敌机啦,collide方法就是检测碰撞,理清思路来写代码就不会太难理解或者是太乱。最后isLose判断输赢。

下面补上Constants的代码

package com.mr.main;

public class Constants {
    public static final String BASEPATH=System.getProperty("user.dir");
    public static final String RESOURCES=BASEPATH+"/resources";
    public static final String IMAGES=RESOURCES+"/images";
    public static final String DATA=RESOURCES+"/data";
    public static final String SOUNDS=RESOURCES+"/sounds";
}

Constants就是一些路径的常量

然后我们来写model中的代码

首先来写bullet

package com.mr.model;

import com.mr.main.Constants;

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

public class Bullet {
    public BufferedImage img;
    public int x;
    public int y;
    private int speed;
    public Bullet(int x,int y){
        File tmp=new File(Constants.IMAGES+"/bullet1.png");
        try {
            img=ImageIO.read(tmp);
        } catch (IOException e){
            e.printStackTrace();
        }
        this.x=x;
        this.y=y;
        speed=15;
    }
    public void move(){
        y-=speed;
    }
    public Rectangle getBound(){
        return new Rectangle(x,y,img.getWidth(),img.getHeight());
    }
}

这个model里面的类代码不算太多,也不难理解,导入需要的包,声明变量并初始化,这里就包括图片和xy坐标以及移动速度等,move方法移动,getBound方法获取对应的Rectangle长方形对象,用于检测碰撞。

下面是Enemy的代码

package com.mr.model;

import com.mr.main.Constants;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;

public class Enemy {
    public BufferedImage img;
    public int x;
    public int y;
    private int speed;
    public String type;
    public int health;
    public int score;
    public Enemy(){
        Random r=new Random();
        int v=r.nextInt(1,10);
        switch (v){
            case 1: case 2: case 3: case 4: case 5:
                type="small";
                health=r.nextInt(1,4);
                break;
            case 6: case 7: case 8:
                type="mid";
                health=r.nextInt(4,7);
                break;
            case 9:
                type="big";
                health=r.nextInt(7,10);
                break;
        }
        score=health;
        File tmp=new File(Constants.IMAGES+"/"+type+"_enemy.png");
        try {
            img=ImageIO.read(tmp);
        } catch (IOException e){
            e.printStackTrace();
        }
        speed=r.nextInt(2,8);
        y=-img.getHeight();
        x=r.nextInt(0,720-img.getWidth());
    }
    public void move(){
        y+=speed;
    }
    public Rectangle getBound(){
        return new Rectangle(x,y,img.getWidth(),img.getHeight());
    }
}

因为这些代码思路都差不多所以就不再详细讲解

下面是Player类

package com.mr.model;

import com.mr.main.Constants;

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

public class Player {
    public BufferedImage img;
    public int x;
    public int y;
    private int speed;
    public Player(){
        File tmp=new File(Constants.IMAGES+"/myplane.png");
        try {
            img=ImageIO.read(tmp);
        } catch (IOException e){
            e.printStackTrace();
        }
        y=960-80-img.getHeight();
        x=720/2-img.getWidth()/2;
        speed=10;
    }
    public void checkBound(){
        if (x<0) {
            x=0;
        } else if (x+img.getWidth()>720) {
            x=720-img.getWidth();
        }
    }
    public Rectangle getBound(){
        return new Rectangle(x,y,img.getWidth(),img.getHeight());
    }
}

然后我们来写刷新帧的代码

package com.mr.service;

import com.mr.view.GamePanel;

public class Fresh extends Thread {
    private GamePanel panel;
    public static final int INTERVAL=20;
    public Fresh(GamePanel panel){
        this.panel=panel;
    }
    public void run(){
        while(true){
            panel.repaint();
            try {
                Thread.sleep(INTERVAL);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

刷新帧继承Thread,重复执行repaint方法重绘即可

接下来是MouseMotion

package com.mr.service;

import com.mr.model.Player;
import com.mr.view.GamePanel;

import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;

public class MouseMotion implements MouseMotionListener {
    private GamePanel panel;
    public MouseMotion(GamePanel panel){
        this.panel=panel;
    }
    @Override
    public void mouseDragged(MouseEvent event){
        ;
    }
    @Override
    public void mouseMoved(MouseEvent event){
        if (!panel.isLose()) {
            Player player = panel.getPlayer();
            player.x = event.getX() - player.img.getWidth() / 2;
            player.checkBound();
        }
    }
}

这个MouseMotion要实现MouseMotionListener接口,写mouseMoved方法,如果没有失败则更新飞机位置,也就是说飞机的x坐标随着鼠标移动

最后一个小功能,实现计分效果

package com.mr.service;

import com.mr.main.Constants;
import com.mr.model.Player;

import java.io.*;

public class ScoreRecorder {
    private int maxScore;
    private int myScore;
    private File tmp;
    public void read(){
        tmp=new File(Constants.DATA+"/data.txt");
        if (!tmp.exists()) {
            maxScore=0;
            try {
                tmp.createNewFile();
                FileWriter fw=new FileWriter(tmp);
                BufferedWriter bw=new BufferedWriter(fw);
                String tmp2="0";
                bw.write(tmp2);
                bw.close();
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            try {
                FileReader fr=new FileReader(tmp);
                BufferedReader br=new BufferedReader(fr);
                String tmp2=br.readLine();
                maxScore=Integer.valueOf(tmp2.strip());
                br.close();
                fr.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    public int start(int score){
        myScore=score;
        read();
        if (myScore>maxScore) {
            write();
        }
        return maxScore;
    }
    public void write(){
        try {
            FileWriter fw=new FileWriter(tmp);
            BufferedWriter bw=new BufferedWriter(fw);
            String tmp2=String.valueOf(myScore);
            bw.write(tmp2);
            bw.close();
            fw.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

计分效果用到了io输入输出流,运用了文件的读写操作

这里resources的资源目录如下,可以根据自己的实际情况修改Constants的值

LastOne!!!画上点睛之笔,写个Start程序入口就完工啦!!

package com.mr.main;

import com.mr.view.GameFrame;

public class Start {
    public static void main(String[] args){
        GameFrame gameFrame=new GameFrame();
    }
}

喜欢的话就点赞收藏关注吧!

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值