Java Game Development - Tank Battle

introduction:

Tank Battle is also a relatively classic game in my childhood. I also referred to Tank Battle written by teacher Han Shunping on the Internet, and made some improvements, and compiled it as a childhood memory!

Idea:

  1. Create the main window, loading menu and game panel.

  1. Initialize various parameters in the game panel and establish various functional components.

  1. Use thread fixation to refresh the game interface.

  1. Handle various collision issues

  1. game over.

Code:

This game uses JDK1.8 and encodes UTF-8;

The IDE I use here is Intellij Idea, and I created a new empty game project, with tankwar as one of the modules (of course this is not important, it’s just personal preference). There are many classes, and TankWar.java is the game entry class. GameFrame.java is the main window class. GamePanel.java is the game panel class. GameLogic.java is the game logic class. Let’s post all the code in one go first.

  1. TankWar.java game entry class

package lag.game.tankwar;

/**
 * 功能:坦克大战<br>
 * 作者:我是小木鱼(Lag)
 */
public class TankWar
{
    public static void main(String[] args)
    {
        new GameFrame();
    }
}
  1. GameFrame.java game window

package lag.game.tankwar;

import java.awt.*;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

/**
 * 游戏窗口
 */
public class GameFrame extends JFrame implements ActionListener
{
    /** 游戏面板 */
    private GamePanel gamePanel;

    /**
     * 构造函数
     */
    public GameFrame()
    {
        try
        {
            // 定制菜单
            JMenuBar mb_main = new JMenuBar();      // 菜单栏
            JMenu m_game = new JMenu("游戏");     // 游戏菜单
            m_game.setFont(new Font("微软雅黑",Font.PLAIN,12));    // 游戏菜单字体
            JMenuItem mi_new = new JMenuItem("新游戏");    // 游戏菜单中的新游戏子菜单
            mi_new.setFont(new Font("微软雅黑",Font.PLAIN,12));     // 子菜单字体
            mi_new.addActionListener(this);       // 为窗口增加新游戏菜单的事件监听
            mi_new.setActionCommand("new");         // 设置新游戏菜单的监听标识
            m_game.add(mi_new);                     // 将新游戏子菜单附加到游戏菜单上
            JMenuItem mi_grade = new JMenuItem("选关卡");
            mi_grade.setFont(new Font("微软雅黑",Font.PLAIN,12));
            mi_grade.addActionListener(this);
            mi_grade.setActionCommand("grade");
            m_game.add(mi_grade);
            m_game.addSeparator();                  // 菜单分隔符
            JMenuItem mi_exit = new JMenuItem("退出");
            mi_exit.setFont(new Font("微软雅黑",Font.PLAIN,12));
            mi_exit.addActionListener(this);
            mi_exit.setActionCommand("exit");
            m_game.add(mi_exit);
            mb_main.add(m_game);
            JMenu m_help = new JMenu("帮助");
            m_help.setFont(new Font("微软雅黑",Font.PLAIN,12));
            JMenuItem mi_about = new JMenuItem("关于");
            mi_about.setFont(new Font("微软雅黑",Font.PLAIN,12));
            mi_about.addActionListener(this);
            mi_about.setActionCommand("about");
            m_help.add(mi_about);
            mb_main.add(m_help);
            this.setJMenuBar(mb_main);

            // 定制面板
            this.gamePanel = new GamePanel();
            this.add(this.gamePanel);

            // 定制窗口
            this.setTitle("坦克大战");                   // 标题
            this.setLayout(null);                       // 清空布局管理器
            this.setSize(this.gamePanel.getWidth() + 10,this.gamePanel.getHeight() + 60);   // 根据游戏面板大小设置游戏窗口大小
            this.setResizable(false);                   // 程序运行时禁止改变窗口大小尺寸
            this.setLocationRelativeTo(null);           // 窗口居中
            this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    // 点击窗口X按钮时默认关闭程序
            this.setVisible(true);                      // 显示窗口
            //音乐(用线程播放)
//            new Thread(()->{
//                GameMusic gameMusic = new GameMusic(this.getClass().getClassLoader().getResourceAsStream("audio/start.wav"));
//                gameMusic.play(false);
//            }).start();
        }
        catch (Exception e)
        {
            e.printStackTrace();
            JOptionPane.showMessageDialog(this,"程序出现异常错误,即将退出!\r\n\r\n"+e.toString(),"提示",JOptionPane.ERROR_MESSAGE);
            System.exit(0);
        }
    }

    /**
     * 事件监听
     */
    @Override
    public void actionPerformed(ActionEvent e)
    {
        // 监听事件的标识
        String command = e.getActionCommand();
        switch (command)
        {
            case "new":     // 开始新游戏
                this.gamePanel.newGame();
                break;
            case "grade":
                int gradeCount = GameMap.getGradeCount();
                String[] options = new String[gradeCount];
                for (int i = 0; i < gradeCount; i++)
                {
                    options[i] = i + 1 + "";
                }
                String grade = (String)JOptionPane.showInputDialog(this, "请选择你要进行的关卡:","选关", JOptionPane.INFORMATION_MESSAGE, null, options, options[0]);
                gamePanel.setGrade(Integer.parseInt(grade));
                gamePanel.newGame();
                break;
            case "exit":
                System.exit(0);
                break;
            case "about":
                JOptionPane.showMessageDialog(this,"我是小木鱼(Lag)","提示",JOptionPane.INFORMATION_MESSAGE);
                break;

        }
    }

}
  1. GamePanel.java game panel

package lag.game.tankwar;

import java.awt.*;
import javax.swing.*;
import java.util.List;
import java.util.ArrayList;
import java.util.Vector;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;

/**
 * 游戏面板
 */
public class GamePanel extends JPanel implements KeyListener
{
    /** 游戏状态常量 */
    private final static int GAME_STATE_READY = 0;       // 游戏未开始
    private final static int GAME_STATE_RUNNING = 1;     // 游戏运行中
    private final static int GAME_STATE_OVER = 9;        // 游戏已结束

    /** 游戏运行场景范围常量 */
    public final static int GAME_ACTION_WIDTH = Block.BLOCK_WIDTH * 26;     // 游戏运行场景宽度
    public final static int GAME_ACTION_HEIGHT = Block.BLOCK_HEIGHT * 26;    // 游戏运行场景宽度

    /** 游戏面板范围常量 */
    public final static int GAME_PANEL_WIDTH = GAME_ACTION_WIDTH + 150;     // 游戏面板宽度
    public final static int GAME_PANEL_HEIGHT = GAME_ACTION_HEIGHT;         // 游戏面板高度

    /** 利用双缓冲机制防止画面闪烁(创建一张与面板画面一样大小的图片,所有的元素先绘制到该图片上,再将该图片一次性绘制到面板画面上) */
    private BufferedImage bufferedImage = new BufferedImage(GAME_ACTION_WIDTH,GAME_ACTION_HEIGHT,BufferedImage.TYPE_4BYTE_ABGR);

    /** 游戏刷新频率 */
    private int repaintInterval = 50;      // 游戏每秒刷新(1000/repaintInterval)次

    /** 游戏状态 */
    private int gameState = GAME_STATE_READY;

    /** 我方的坦克 */
    private Hero hero;

    /** 我方坦克模型(显示在计分榜上) */
    private Hero heroModel = new Hero(GAME_ACTION_WIDTH + 30,200,Tank.TANK_DIRECTION_RIGHT);

    /** 敌方坦克池(考虑线程安全用Vector,没用ArrayList) */
    private Vector<Enemy> enemyPool = new Vector<>();

    /** 敌方坦克池最大数量 */
    private int enemyPoolMaxNum = 0;

    /** 敌方坦克死亡数量 */
    private int enemyDeadNum = 0;

    /** 敌方坦克模型(显示在计分榜上) */
    private Enemy enemyModel = new Enemy(GAME_ACTION_WIDTH + 30,20,Tank.TANK_DIRECTION_RIGHT);

    /** 创建敌方坦克线程是否运行标识 */
    private boolean createEnemyTankThreadRunning = false;

    /** 上次投放时间(用来设置投放坦克最小时间间隔) */
    private long lastCreateEnemyTankTime = System.currentTimeMillis();

    /** 块列表 */
    private List<Block> blockList = new ArrayList<>();

    /** 爆炸列表 */
    private List<Bomb> bombList = new ArrayList<>();

    /** 游戏关卡 */
    private int grade = 1;

    /** 游戏某关地图 */
    private byte[][] gameGradeMap;

    /** 大本营 */
    private Camp camp = new Camp(12 * Block.BLOCK_WIDTH,24 * Block.BLOCK_HEIGHT);

    /**
     * 构造函数
     */
    public GamePanel()
    {
        // 设置面板大小
        this.setSize(GAME_PANEL_WIDTH,GAME_PANEL_HEIGHT);

        // 监听键盘事件
        this.setFocusable(true);           // 先让面板得到焦点
        this.addKeyListener(this);      // 将监听键盘事件附加到面板上

        // 创建游戏逻辑实例
        GameLogic.setInstance(new GameLogic(this));
    }

    /**
     * 开始新游戏
     */
    public void newGame()
    {
        // 游戏清空重置
        gameReset();

        // 设置游戏状态为运行中
        this.gameState = GAME_STATE_RUNNING;

        // 加载游戏地图
        loadGameMap();

        // 设置大本营
        camp.setState(Camp.CAMP_STATE_RUNNING);

        // 创建我方坦克
        int heroX = 8 * Block.BLOCK_WIDTH;
        int heroY = (this.gameGradeMap.length - 2) * Block.BLOCK_HEIGHT;
        hero = new Hero(heroX,heroY,Tank.TANK_DIRECTION_UP);
        hero.work();

        // 创建敌方坦克
        createEnemyTank();

        // 启动线程来定时刷新画面
        refresh();

    }

    /**
     * 加载游戏地图
     */
    private void loadGameMap()
    {
        // 得到本关地图
        this.gameGradeMap = GameMap.getGameMap(this.grade);

        // 得到地图位置(几行几列)
        int row = this.gameGradeMap.length;
        int column = this.gameGradeMap[0].length;

        // 开始循环
        for (int i = 0; i < row; i++)
        {
            for (int j = 0; j < column; j++)
            {
                int mapValue = this.gameGradeMap[i][j];
                switch (mapValue)
                {
                    case Block.BLOCK_KIND_BRICK:
                        Brick brick = new Brick(Block.BLOCK_WIDTH * j,Block.BLOCK_HEIGHT * i);
                        this.blockList.add(brick);
                        break;
                    case Block.BLOCK_KIND_IRON:
                        Iron iron = new Iron(Block.BLOCK_WIDTH * j,Block.BLOCK_HEIGHT * i);
                        this.blockList.add(iron);
                        break;
                }
            }
        }

        this.enemyPoolMaxNum = GameMap.getEnemyTankNum(this.grade);     // 本关敌方坦克数量
    }

    /**
     * 游戏重置
     */
    private void gameReset()
    {
        // 停止上局正在运行的线程
        if(this.gameState == GAME_STATE_RUNNING){this.gameState = GAME_STATE_READY;}
        while (true)
        {
            if(this.createEnemyTankThreadRunning)
            {
                try
                {
                    Thread.sleep(10);
                }
                catch (InterruptedException e)
                {
                    throw new RuntimeException(e);
                }
            }
            else
            {
                break;
            }
        }

        // 清空敌方坦克池中的所有坦克及其子弹
        for (int i = 0; i < enemyPool.size(); i++)
        {
            Enemy enemy = enemyPool.get(i);
            enemy.reset();
            for (int j = 0; j < enemy.getBulletPool().size(); j++)
            {
                Bullet bullet = enemy.getBulletPool().get(j);
                bullet.reset();
            }
            enemy.getBulletPool().clear();
        }
        enemyPool.clear();
        enemyDeadNum = 0;

        // 清空我方坦克及其子弹
        if(hero != null)
        {
            hero.reset();
            for (int i = 0; i < hero.getBulletPool().size(); i++)
            {
                Bullet bullet = hero.getBulletPool().get(i);
                bullet.reset();
            }
            hero.getBulletPool().clear();
        }

        // 清空块池中的所有块
        blockList.clear();

    }

    /**
     * 创建敌方坦克
     */
    private void createEnemyTank()
    {
        this.createEnemyTankThreadRunning = true;   // 线程运行中
        new Thread(()->{
            //System.out.println("创建敌方坦克线程开始启动...");
            // 记录已投放的坦克数量
            int createEnemyTankNum = 0;
            try
            {
                while (this.gameState == GAME_STATE_RUNNING)
                {
                    // 休眠一会
                    try
                    {
                        Thread.sleep(repaintInterval);      // 休眠时间短主要是考虑可以迅速退出本线程
                    }
                    catch (InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    // 控制连续投放间隔
                    long curCreateEnemyTankTime = System.currentTimeMillis();
                    if((curCreateEnemyTankTime - this.lastCreateEnemyTankTime) >= 3000)  // 3秒投放
                    {
                        this.lastCreateEnemyTankTime = curCreateEnemyTankTime;
                    }
                    else
                    {
                        continue;
                    }
                    // 投放敌方坦克到达最大值后退出本线程
                    if(createEnemyTankNum >= enemyPoolMaxNum){break;}
                    // 在敌方坦克池中寻找空闲的坦克随机显示在左上角或右上角
                    Enemy enemy = null;
                    for (int i = 0; i < enemyPool.size(); i++)
                    {
                        Enemy tmpEnemy = enemyPool.get(i);
                        if(tmpEnemy.getState() == Tank.TANK_STATE_FREE)
                        {
                            enemy = tmpEnemy;
                            //System.out.println("找到空闲的敌方坦克了..................");
                            break;
                        }
                    }
                    // 没有就增加一个
                    if(enemy == null)
                    {
                        enemy = new Enemy(-100,-100,Tank.TANK_DIRECTION_DOWN);
                        enemyPool.add(enemy);
                        //System.out.println("新建敌方坦克了.................");
                    }
                    // 开始投放(随机投放到左上角与右上角,坦克不能重叠)
                    int enemyX;     // 坦克X坐标
                    int enemyDirection = Tank.TANK_DIRECTION_DOWN;  // 坦克方向默认向下
                    int randomPos = GameLogic.getRandomInt(0,1);    // 随机生成位置(0-左上角,1-右上角)
                    if(randomPos == 0)  // 左上角(方向下/右)
                    {
                        enemyX = 0;
                        if(GameLogic.getRandomInt(0,1) == 1){enemyDirection = Tank.TANK_DIRECTION_RIGHT;}
                    }
                    else    // 右上角(方向下/左)
                    {
                        enemyX = GAME_ACTION_WIDTH - Tank.TANK_WIDTH;
                        if(GameLogic.getRandomInt(0,1) == 1){enemyDirection = Tank.TANK_DIRECTION_LEFT;}
                    }
                    enemy.setX(enemyX);
                    enemy.setY(0);
                    enemy.setDirection(enemyDirection);
                    // 判断该范围内是否可以投放运行
                    boolean workFlag = true;
                    for (int i = 0; i < enemyPool.size(); i++)
                    {
                        Enemy tmpEnemy = enemyPool.get(i);
                        if(tmpEnemy.getState() == Tank.TANK_STATE_RUNNING && enemy != tmpEnemy)
                        {
                            if(GameLogic.getInstance().tankCollideTank(enemy,tmpEnemy))  // 敌方坦克占位,不能投放
                            {
                                workFlag = false;
                                continue;
                            }
                        }
                    }
                    if(GameLogic.getInstance().tankCollideTank(enemy,hero))      // 我方坦克占位,不能投放
                    {
                        workFlag = false;
                    }
                    // 不能投放准备下一次
                    if(!workFlag){continue;}
                    // 可以投放了
                    enemy.work();   // 启动线程开始工作了
                    createEnemyTankNum++;
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            finally
            {
                this.createEnemyTankThreadRunning = false;      // 线程结束
            }
            //System.out.println("创建敌方坦克线程退出历史舞台了......");
        }).start();

    }

    /**
     * 游戏结束
     */
    private void gameOver()
    {

        // 设置游戏状态
        this.gameState = GAME_STATE_OVER;

        // 游戏清空重置
        gameReset();

    }

    /**
     * 游戏胜利
     */
    private void gameWin()
    {
        if(enemyPoolMaxNum - enemyDeadNum <= 0)
        {
            this.grade++;
            newGame();
        }
    }

    /**
     * 碰撞处理
     */
    private void collide()
    {
        // 判断我方子弹是否与敌方坦克或块或大本营碰撞
        for (int i = 0; i < hero.getBulletPool().size(); i++)
        {
            Bullet bullet = hero.getBulletPool().get(i);
            if(bullet.getState() == Bullet.BULLET_STATE_RUNNING)    // 我方子弹飞行中
            {
                // 判断是否与敌方坦克碰撞
                for (int j = 0; j < enemyPool.size(); j++)
                {
                    Enemy enemy = enemyPool.get(j);
                    if(enemy.getState() == Tank.TANK_STATE_RUNNING)     // 敌方坦克爬行中
                    {
                        // 开始判断
                        if(GameLogic.getInstance().bulletCollideTank(bullet,enemy))   // 这还真碰上了
                        {
                            // 记录坦克坐标,因为坦克销毁时会改变其坐标
                            int bombX = enemy.getX();
                            int bombY = enemy.getY();
                            // 子弹销毁
                            bullet.reset();
                            // 坦克受到伤害
                            enemy.hurt(bullet);
                            // 判断坦克是否死亡
                            if(enemy.isDead())
                            {
                                // 坦克销毁
                                enemy.reset();
                                enemyDeadNum++;
                                // 坦克爆炸
                                addBomb(bombX,bombY,Tank.TANK_WIDTH,Tank.TANK_HEIGHT);
                                // 判断是否胜利
                                gameWin();
                            }
                        }
                    }
                }
                // 判断是否与块碰撞
                for (int m = blockList.size() - 1; m >= 0; m--)
                {
                    Block block = blockList.get(m);
                    // 开始判断
                    if(GameLogic.getInstance().bulletCollideBlock(bullet,block))   // 这还真碰上了
                    {
                        // 记录块坐标
                        int bombX = block.getX();
                        int bombY = block.getY();
                        // 子弹销毁
                        bullet.reset();
                        if(block.getBlockKind() == Block.BLOCK_KIND_BRICK)
                        {
                            // 砖块销毁
                            blockList.remove(m);
                            // 砖块爆炸
                            addBomb(bombX,bombY,Block.BLOCK_WIDTH,Block.BLOCK_HEIGHT);
                        }
                        else
                        {
                            addBomb(bombX,bombY,Block.BLOCK_WIDTH,Block.BLOCK_HEIGHT);
                        }
                    }
                }
                // 判断是否与大本营碰撞
                if(camp.getState() == Camp.CAMP_STATE_RUNNING)
                {
                    if(GameLogic.getInstance().bulletCollideCamp(bullet,camp))   // 完蛋了啊
                    {
                        // 子弹销毁
                        bullet.reset();
                        // 大本营销毁
                        camp.setState(Camp.CAMP_STATE_FREE);
                        // 大本营爆炸
                        addBomb(camp.getX(),camp.getY(),camp.getWidth(),camp.getHeight());
                        // 游戏结束
                        gameOver();
                    }
                }
            }
        }

        // 判断敌方子弹是否与我方坦克或块或大本营碰撞(不需要判断敌方坦克状态,因为即使坦克销毁子弹可能仍然在飞)
        for (int i = 0; i < enemyPool.size(); i++)
        {
            // 判断是否与我方坦克碰撞
            Enemy enemy = enemyPool.get(i);
            for (int j = 0; j < enemy.getBulletPool().size(); j++)
            {
                Bullet bullet = enemy.getBulletPool().get(j);
                if(bullet.getState() == Bullet.BULLET_STATE_RUNNING)
                {
                    // 判断子弹是否与坦克碰撞
                    if(GameLogic.getInstance().bulletCollideTank(bullet,hero))   // 完蛋了啊
                    {
                        // 记录坦克坐标,因为坦克销毁时会改变其坐标
                        int bombX = hero.getX();
                        int bombY = hero.getY();
                        // 子弹销毁
                        bullet.reset();
                        // 坦克受到伤害
                        hero.hurt(bullet);
                        // 判断坦克是否死亡
                        if(hero.isDead())
                        {
                            // 坦克销毁
                            hero.reset();
                            // 坦克爆炸
                            addBomb(bombX,bombY,Tank.TANK_WIDTH,Tank.TANK_HEIGHT);
                            // 游戏结束
                            gameOver();
                        }
                    }
                    // 判断子弹是否与块碰撞
                    for (int m = blockList.size() - 1; m >= 0; m--)
                    {
                        Block block = blockList.get(m);
                        // 开始判断
                        if(GameLogic.getInstance().bulletCollideBlock(bullet,block))   // 这还真碰上了
                        {
                            // 记录块坐标
                            int bombX = block.getX();
                            int bombY = block.getY();
                            // 子弹销毁
                            bullet.reset();
                            if(block.getBlockKind() == Block.BLOCK_KIND_BRICK)
                            {
                                // 砖块销毁
                                blockList.remove(m);
                                // 砖块爆炸
                                addBomb(bombX,bombY,Block.BLOCK_WIDTH,Block.BLOCK_HEIGHT);
                            }
                            else
                            {
                                addBomb(bombX,bombY,Block.BLOCK_WIDTH,Block.BLOCK_HEIGHT);
                            }
                        }
                    }
                    // 判断子弹是否与大本营碰撞
                    if(camp.getState() == Camp.CAMP_STATE_RUNNING)
                    {
                        if(GameLogic.getInstance().bulletCollideCamp(bullet,camp))   // 完蛋了啊
                        {
                            // 子弹销毁
                            bullet.reset();
                            // 大本营销毁
                            camp.setState(Camp.CAMP_STATE_FREE);
                            // 大本营爆炸
                            addBomb(camp.getX(),camp.getY(),camp.getWidth(),camp.getHeight());
                            // 游戏结束
                            gameOver();
                        }
                    }
                }
            }
        }

    }

    /**
     * 添加爆炸
     */
    private void addBomb(int x,int y,int w,int h)
    {
        // 查找空闲的爆炸
        Bomb bomb = null;
        for (int i = 0; i < bombList.size(); i++)
        {
            Bomb tmpBomb = bombList.get(i);
            if(tmpBomb.getState() == Bomb.BOMB_STATE_FREE)
            {
                bomb = tmpBomb;
                //System.out.println("找到空闲的爆炸了..................");
                break;
            }
        }
        // 没有就增加一个
        if(bomb == null)
        {
            bomb = new Bomb(-100,-100);
            bombList.add(bomb);
            //System.out.println("新建爆炸了.................");
        }
        bomb.setX(x);
        bomb.setY(y);
        bomb.setWidth(w);
        bomb.setHeight(h);
        bomb.work();
    }

    /**
     * 字符被输入
     */
    @Override
    public void keyTyped(KeyEvent e){}

    /**
     * 某键被按下
     */
    @Override
    public void keyPressed(KeyEvent e)
    {
        // 游戏未开始或结束禁止按键
        if(this.gameState != GAME_STATE_RUNNING){return;}

        int keyCode = e.getKeyCode();
        switch (keyCode)
        {
            case KeyEvent.VK_W:
            case KeyEvent.VK_UP:
                hero.move(Tank.TANK_DIRECTION_UP);
                break;
            case KeyEvent.VK_D:
            case KeyEvent.VK_RIGHT:
                hero.move(Tank.TANK_DIRECTION_RIGHT);
                break;
            case KeyEvent.VK_S:
            case KeyEvent.VK_DOWN:
                hero.move(Tank.TANK_DIRECTION_DOWN);
                break;
            case KeyEvent.VK_A:
            case KeyEvent.VK_LEFT:
                hero.move(Tank.TANK_DIRECTION_LEFT);
                break;
            case KeyEvent.VK_SPACE:
                hero.shoot();
                break;
        }
    }

    /**
     * 某键被释放
     */
    @Override
    public void keyReleased(KeyEvent e){}

    /**
     * 利用线程来定时刷新重绘游戏画面,适用于飞机类刷新频率较高的游戏。<br>
     * 动画原理就是在极短时间内连续显示多个图片,给人眼一种画面动起来的错觉(人眼的识别静态图时间为0.1秒)。<br>
     * 屏幕刷新频率(FPS->帧/每秒),单位赫兹(Hz)。<br>
     * 20Hz ->每秒刷新画面20次,即每隔(1000/20)毫秒刷新一次画面。<br>
     */
    private void refresh()
    {
        // 利用Lambda表达式替代内部类更方便
        new Thread(() ->
        {
            //System.out.println("游戏自动刷新线程开始启动...");
            while (this.gameState == GAME_STATE_RUNNING)
            {
                // 碰撞处理
                collide();

                // 开始刷新
                repaint();

                // 延时睡眠
                try
                {
                    Thread.sleep(repaintInterval);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
            //System.out.println("游戏自动刷新线程退出历史舞台了......");
        }).start();

    }

    /**
     * 绘制游戏画面(利用双缓冲机制防止画面闪烁)
     */
    @Override
    public void paint(Graphics g)
    {
        // 完成初始化工作,该语句千万不要动
        super.paint(g);

        // 准备开始游戏
        if(this.gameState == GAME_STATE_READY)
        {
            // 清一下背景
            g.setColor(Color.BLACK);
            g.fillRect(0,0,this.getWidth(),this.getHeight());
            // 显示Logo
            g.setColor(Color.ORANGE);
            g.setFont(new Font("微软雅黑",Font.BOLD,60));
            g.drawString("坦  克  大  战", 160,170);
            g.setColor(Color.CYAN);
            g.setFont(new Font("微软雅黑",Font.BOLD,40));
            g.drawString("2023", 280,280);
            g.setColor(Color.WHITE);
            g.setFont(new Font("宋体",Font.PLAIN,20));
            g.drawString("我是小木鱼(Lag)制作", 240,420);
            return;
        }

        // 游戏结束
        if(this.gameState == GAME_STATE_OVER)
        {
            g.setColor(Color.BLACK);
            g.fillRect(0,0,this.getWidth(),this.getHeight());
            g.setColor(Color.RED);
            g.setFont(new Font("宋体",Font.BOLD,60));
            g.drawString("游戏结束", 200,170);
            g.drawString("大虾请重新来过吧!", 70,280);
        }

        // 游戏进行中
        if(this.gameState == GAME_STATE_RUNNING)
        {
            // 设置游戏面板区域
            g.setColor(Color.LIGHT_GRAY);
            g.fillRect(0,0,this.getWidth(),this.getWidth());
            g.setColor(Color.WHITE);
            g.fill3DRect(GAME_ACTION_WIDTH,0,2,GAME_ACTION_HEIGHT,true);

            // 绘制分数
            // 敌方坦克信息
            enemyModel.draw(g);
            g.setColor(Tank.TANK_COLOR_ENEMY);
            g.setFont(new Font("微软雅黑",Font.BOLD,20));
            g.drawString("总数:" + enemyPoolMaxNum,enemyModel.getX() ,enemyModel.getY() + 70);
            g.drawString("死亡:" + enemyDeadNum,enemyModel.getX() ,enemyModel.getY() + 100);
            g.drawString("剩余:" + (enemyPoolMaxNum - enemyDeadNum),enemyModel.getX() ,enemyModel.getY() + 130);
            // 我方坦克信息
            heroModel.draw(g);
            g.setColor(Tank.TANK_COLOR_HERO);
            g.setFont(new Font("微软雅黑",Font.BOLD,20));
            g.drawString("总数:1" ,heroModel.getX() ,heroModel.getY() + 70);
            g.drawString("生命:" + hero.getHp() ,heroModel.getX() ,heroModel.getY() + 100);
            // 关口信息
            g.setColor(Color.BLACK);
            int gradeModelX = heroModel.getX();
            int gradeModelY = heroModel.getY() + 150;
            g.fillRect(gradeModelX,gradeModelY,3,40);
            Polygon triangle = new Polygon();
            triangle.addPoint(gradeModelX + 3, gradeModelY);
            triangle.addPoint(gradeModelX + 3, gradeModelY + 20);
            triangle.addPoint(gradeModelX  + 40, gradeModelY + 20);
            g.setColor(Color.RED);
            g.fillPolygon(triangle);
            g.setColor(Color.WHITE);
            g.setFont(new Font("微软雅黑",Font.BOLD,20));
            g.drawString("关数:" + this.grade ,gradeModelX ,gradeModelY + 70);

            // 利用双缓冲机制来重绘画面,防止画面闪烁(先把所有的元素绘制到缓冲图片上,再将该图片一次性绘制到画面上)
            Graphics ig = bufferedImage.getGraphics();      // 得到缓冲图片的画笔

            // 设置游戏面板区域
            ig.setColor(Color.BLACK);
            ig.fillRect(0,0,GAME_ACTION_WIDTH,GAME_ACTION_HEIGHT);

            // 绘制地图
            for (Block block : this.blockList){block.draw(ig);}

            // 绘制大本营
            if(camp.getState() == Camp.CAMP_STATE_RUNNING){camp.draw(ig);}

            // 绘制我方坦克
            hero.draw(ig);

            // 绘制我方子弹
            for (Bullet bullet : hero.getBulletPool()){if(bullet.getState() == Bullet.BULLET_STATE_RUNNING){bullet.draw(ig);}}

            // 绘制敌方坦克、子弹
            for (int i = 0; i < enemyPool.size(); i++)
            {
                Enemy enemy = enemyPool.get(i);
                // 绘制坦克
                if(enemy.getState() == Tank.TANK_STATE_RUNNING){enemy.draw(ig);}
                // 绘制子弹
                for (Bullet bullet : enemy.getBulletPool()){if(bullet.getState() == Bullet.BULLET_STATE_RUNNING){bullet.draw(ig);}}
            }

            // 绘制爆炸
            for (Bomb bomb : bombList){if(bomb.getState() == Bomb.BOMB_STATE_RUNNING){bomb.draw(ig);}}

            // 将缓冲图片一次性显示到画面上
            g.drawImage(bufferedImage,0,0,null);
        }

    }

    public Hero getHero(){return hero;}

    public Vector<Enemy> getEnemyPool(){return enemyPool;}

    public List<Block> getBlockList(){return blockList;}

    public Camp getCamp(){return camp;}

    public void setGrade(int grade){this.grade = grade;}

}
  1. GameLogic.java game logic

package lag.game.tankwar;

/**
 * 游戏逻辑(牛逼类)
 */
public class GameLogic
{
    /** 唯一实例 */
    private static GameLogic instance;

    /** 游戏面板 */
    private GamePanel gamePanel;

    /**
     * 构造函数
     * @param gamePanel 游戏面板
     */
    public GameLogic(GamePanel gamePanel)
    {
        this.gamePanel = gamePanel;
    }

    /**
     * 得到唯一实例
     */
    public static GameLogic getInstance()
    {
        return instance;
    }

    /**
     * 设置唯一实例
     * @param gameLogic 游戏逻辑
     */
    public static void setInstance(GameLogic gameLogic)
    {
        instance = gameLogic;
    }

    /**
     * 判断矩形1是否碰撞到矩形2
     * @param x1 矩形1左上角X坐标
     * @param y1 矩形1左上角Y坐标
     * @param w1 矩形1宽度
     * @param h1 矩形1高度
     * @param x2 矩形2左上角X坐标
     * @param y2 矩形2左上角Y坐标
     * @param w2 矩形2宽度
     * @param h2 矩形2高度
     */
    public boolean rectCollideRect(int x1,int y1,int w1,int h1,int x2,int y2,int w2,int h2)
    {
        // 判断原理(1、矩形1的四个顶点是否落在矩形2里;2、矩形2的四个顶点是否落在矩形1里。若高度或宽阔相等时特殊判断)

        // 这个碰撞判断比较严,必须进入才算碰撞,挨着不算

        // 判断矩形1的四个顶点是否落在矩形2里
        if((x1 > x2 && x1 < x2 + w2 && y1 > y2 && y1 < y2 + h2)
                || (x1 + w1 > x2 && x1 + w1 < x2 + w2 && y1 > y2 && y1 < y2 + h2)
                || (x1 > x2 && x1 < x2 + w2 && y1 + h1 > y2 && y1 + h1 < y2 + h2)
                || (x1 + w1 > x2 && x1 + w1 < x2 + w2 && y1 + h1 > y2 && y1 + h1 < y2 + h2))
        {return true;}

        // 判断矩形2的四个顶点是否落在矩形1里
        if((x2 > x1 && x2 < x1 + w1 && y2 > y1 && y2 < y1 + h1)
                || (x2 + w2 > x1 && x2 + w2 < x1 + w1 && y2 > y1 && y2 < y1 + h1)
                || (x2 > x1 && x2 < x1 + w1 && y2 + h2 > y1 && y2 + h2 < y1 + h1)
                || (x2 + w2 > x1 && x2 + w2 < x1 + w1 && y2 + h2 > y1 && y2 + h2 < y1 + h1))
        {return true;}

        // 特殊情况处理

        // 若2个矩形的宽度相等时
        if(w1 == w2){if(x1 == x2 && x1 + w1 == x2 + w2 && ((y1 > y2 && y1 < y2 + h2) || (y1 + h1 > y2 && y1 + h1 < y2 + h2))){return true;}}

        // 若2个矩形的高度相等时
        if(h1 == h2){if(y1 == y2 && y1 + h1 == y2 + h2 && ((x1 > x2 && x1 < x2 + w2) || (x1 + w1 > x2 && x1 + w1 < x2 + w2))){return true;}}

        // 2个矩形完全重合
        if(x1 == x2 && y1 == y2 && w1 == w2 && h1 == h2){return true;}

        return false;
    }

    /**
     * 判断坦克移动时的下一个位置是否碰撞到任何对象
     */
    public boolean tankMoveCollide(Tank tank)
    {
        int newX = tank.getX();
        int newY = tank.getY();

        // 得到移动后的新坐标
        switch (tank.getDirection())
        {
            case Tank.TANK_DIRECTION_UP:
                newY = tank.getY() - tank.getSpeed();
                break;
            case Tank.TANK_DIRECTION_RIGHT:
                newX = tank.getX() + tank.getSpeed();
                break;
            case Tank.TANK_DIRECTION_DOWN:
                newY = tank.getY() + tank.getSpeed();
                break;
            case Tank.TANK_DIRECTION_LEFT:
                newX = tank.getX() - tank.getSpeed();
                break;
        }

        // 判断是否碰撞到边界
        switch (tank.getDirection())
        {
            case Tank.TANK_DIRECTION_UP:
                if(newY < 0){return true;}
                break;
            case Tank.TANK_DIRECTION_RIGHT:
                if(newX + Tank.TANK_WIDTH > GamePanel.GAME_ACTION_WIDTH){return true;}
                break;
            case Tank.TANK_DIRECTION_DOWN:
                if(newY + Tank.TANK_HEIGHT > GamePanel.GAME_ACTION_HEIGHT){return true;}
                break;
            case Tank.TANK_DIRECTION_LEFT:
                if(newX < 0){return true;}
                break;
        }

        // 判断是否碰撞到敌方坦克
        for (int i = 0; i < gamePanel.getEnemyPool().size(); i++)
        {
            Enemy enemy = gamePanel.getEnemyPool().get(i);
            if(enemy.getState() == Tank.TANK_STATE_RUNNING && tank != enemy)
            {
                if(rectCollideRect(newX,newY,Tank.TANK_WIDTH,Tank.TANK_HEIGHT,enemy.getX(),enemy.getY(),Tank.TANK_WIDTH,Tank.TANK_HEIGHT)){return true;}
            }
        }

        // 判断是否碰撞到我方坦克
        if(tank.getTankKind() == Tank.TANK_KIND_ENEMY)
        {
            if(rectCollideRect(newX,newY,Tank.TANK_WIDTH,Tank.TANK_HEIGHT,gamePanel.getHero().getX(),gamePanel.getHero().getY(),Tank.TANK_WIDTH,Tank.TANK_HEIGHT)){return true;}
        }

        // 判断是否碰撞到块
        for (Block block : gamePanel.getBlockList())
        {
            if(rectCollideRect(newX,newY,Tank.TANK_WIDTH,Tank.TANK_HEIGHT,block.getX(),block.getY(),Block.BLOCK_WIDTH,Block.BLOCK_HEIGHT)){return true;}
        }

        // 判断是否碰撞大本营
        if(gamePanel.getCamp().getState() == Camp.CAMP_STATE_RUNNING)
        {
            if(rectCollideRect(newX,newY,Tank.TANK_WIDTH,Tank.TANK_HEIGHT,gamePanel.getCamp().getX(),gamePanel.getCamp().getY(),gamePanel.getCamp().getWidth(),gamePanel.getCamp().getHeight())){return true;}
        }

        return false;
    }

    /**
     * 判断坦克是否碰撞到坦克<br>
     * @param tank1 坦克1
     * @param tank2 坦克2
     */
    public boolean tankCollideTank(Tank tank1,Tank tank2)
    {
        return rectCollideRect(tank1.getX(),tank1.getY(),Tank.TANK_WIDTH,Tank.TANK_HEIGHT,tank2.getX(),tank2.getY(),Tank.TANK_WIDTH,Tank.TANK_HEIGHT);
    }

    /**
     * 判断子弹是否碰撞到坦克<br>
     */
    public boolean bulletCollideTank(Bullet bullet,Tank tank)
    {
        return rectCollideRect(bullet.getX(),bullet.getY(),bullet.getWidth(),bullet.getHeight(),tank.getX(),tank.getY(),Tank.TANK_WIDTH,Tank.TANK_HEIGHT);
    }

    /**
     * 判断子弹是否碰撞到块
     */
    public boolean bulletCollideBlock(Bullet bullet,Block block)
    {
        return rectCollideRect(bullet.getX(),bullet.getY(),bullet.getWidth(),bullet.getHeight(),block.getX(),block.getY(),Block.BLOCK_WIDTH,Block.BLOCK_HEIGHT);
    }

    /**
     * 判断子弹是否碰撞到大本营
     */
    public boolean bulletCollideCamp(Bullet bullet,Camp camp)
    {
        return rectCollideRect(bullet.getX(),bullet.getY(),bullet.getWidth(),bullet.getHeight(),camp.getX(),camp.getY(),camp.getWidth(),camp.getHeight());
    }

    /**
     * 得到某范围内的随机整数
     * @param min 最小值
     * @param max 最大值
     */
    public static int getRandomInt(int min,int max)
    {
        return (int)(min + Math.random() * (max - min + 1));
    }

}
  1. GameMap.java game map

package lag.game.tankwar;

/**
 * 游戏地图
 */
public class GameMap
{
    /** 各关地图[26行26列](0-空地,1-砖块,2-铁块) */
    private static byte[][][] gradeMap =
        {
            // 第1关地图
            {
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,2,2,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,2,2,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0},
                {0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0},
                {1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1},
                {2,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2},
                {0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0}
            },
            // 第2关地图
            {
                {0,0,0,0,0,0,2,2,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,2,2,0,0,0,0,0,0,2,2,0,0,0,0,0,0,0,0,0,0},
                {0,0,1,1,0,0,2,2,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,2,2,0,0,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,2,2,1,1,0,0},
                {0,0,1,1,0,0,0,0,0,0,0,0,1,1,1,1,0,0,1,1,2,2,1,1,0,0},
                {0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0},
                {0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,2,2,0,0,0,0,0,0},
                {0,0,0,0,0,0,1,1,0,0,0,0,2,2,0,0,0,0,1,1,0,0,1,1,2,2},
                {0,0,0,0,0,0,1,1,0,0,0,0,2,2,0,0,0,0,1,1,0,0,1,1,2,2},
                {0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,2,2,0,0,0,0,0,0,0,0},
                {0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,2,2,0,0,0,0,0,0,0,0},
                {0,0,1,1,1,1,1,1,0,0,0,0,0,0,2,2,0,0,0,0,0,0,1,1,0,0},
                {0,0,1,1,1,1,1,1,0,0,0,0,0,0,2,2,0,0,0,0,0,0,1,1,0,0},
                {0,0,0,0,0,0,2,2,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},
                {0,0,0,0,0,0,2,2,0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0,0},
                {2,2,1,1,0,0,2,2,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,1,0,0},
                {2,2,1,1,0,0,2,2,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,0,1,1,2,2,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,0,1,1,2,2,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
                {0,0,1,1,0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0},
                {0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,0,1,0,0,1,0,0,0,1,1,1,1,1,1,0,0},
                {0,0,1,1,0,0,1,1,0,0,0,1,0,0,1,0,0,0,1,1,1,1,1,1,0,0}
            }
            // 其他关地图...
        };

    /** 总关卡数 */
    private static int gradeCount = gradeMap.length;

    /** 各关敌方坦克数量 */
    private static int[] enemyTankNum = {10,20};

    /**
     * 返回某关地图
     * @param grade 关数
     */
    public static byte[][] getGameMap(int grade)
    {
        // 由于数组是个对象,而原始地图是不允许被修改的,所以不能直接赋值(引用地址),得复制一个新的地图让游戏随便修改。
        byte[][] tempMap = null;
        if(grade > 0 && grade <= gradeCount)
        {
            tempMap = gradeMap[grade - 1];
        }
        else
        {
            tempMap = gradeMap[0];
        }
        //开始复制
        int row = tempMap.length;
        int column = tempMap[0].length;
        byte[][] returnMap = new byte[row][column];
        for (int i = 0; i < row; i++)
        {
            for (int j = 0; j < column; j++)
            {
                returnMap[i][j] = tempMap[i][j];
            }
        }

        return returnMap;
    }

    /**
     * 功能:返回总关卡数<br>
     */
    public static int getGradeCount(){return gradeCount;}

    /**
     * 返回某关敌方坦克数量
     * @param grade 关数
     */
    public static int getEnemyTankNum(int grade)
    {
        if(grade < 1 || grade > gradeCount){grade = gradeCount;}
        return enemyTankNum[grade - 1];
    }

}
  1. GameMusic.java

package lag.game.tankwar;

import java.io.File;
import java.io.InputStream;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioInputStream;

/**
 * 游戏音乐<br>
 * 支持音乐格式(AITF、AU、WAV),就是加载有点慢啊。
 */
public class GameMusic
{
    /** 音频输入流 */
    private AudioInputStream stream;

    /** 音频格式 */
    private AudioFormat format;

    /** 音频剪辑 */
    private Clip clip;

    /**
     * 功能:构造函数<br>
     */
    public GameMusic(String fileName)
    {
        try
        {
            File file = new File(fileName);
            //将音乐文件转为音频输入流
            this.stream = AudioSystem.getAudioInputStream(file);
            //得到音频格式
            this.format = stream.getFormat();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 构造函数
     * @param fileName URL文件名
     */
    public GameMusic(InputStream fileName)
    {
        try
        {
            //将音乐文件转为音频输入流
            this.stream = AudioSystem.getAudioInputStream(fileName);
            //得到音频格式
            this.format = stream.getFormat();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 功能:播放音乐<br>
     * 参数:boolean _loop -> 是否循环(True-无限循环,False-不循环)<br>
     */
    public void play(boolean _loop)
    {
        try
        {
            //设置音频行信息
            DataLine.Info info = new DataLine.Info(Clip.class,this.format);
            //建立音频行信息
            this.clip = (Clip)AudioSystem.getLine(info);
            this.clip.open(this.stream);
            if(_loop)    //无限循环
            {
                this.clip.loop(Clip.LOOP_CONTINUOUSLY);
            }
            else
            {
                this.clip.loop(0);
            }
            this.clip.start();
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 功能:停止音乐<br>
     */
    public void stop()
    {
        try
        {
            if(this.clip.isRunning())
            {
                this.clip.stop();
                this.clip.close();
            }
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }

}
  1. Tank.javatank parent class

package lag.game.tankwar;

import java.awt.*;
import java.util.Vector;

/**
 * 坦克父类
 */
public class Tank
{
    /** 坦克类别常量 */
    public final static int TANK_KIND_HERO = 0;                 // 我方坦克
    public final static int TANK_KIND_ENEMY = 1;                // 敌方坦克

    /** 坦克颜色常量 */
    public final static Color TANK_COLOR_HERO = Color.YELLOW;   // 我方坦克颜色
    public final static Color TANK_COLOR_ENEMY = Color.CYAN;    // 敌方坦克颜色

    /** 坦克方向常量 */
    public final static int TANK_DIRECTION_UP = 0;              // 向上
    public final static int TANK_DIRECTION_RIGHT = 1;           // 向右
    public final static int TANK_DIRECTION_DOWN = 2;            // 向下
    public final static int TANK_DIRECTION_LEFT = 3;            // 向左

    /** 坦克状态常量 */
    public final static int TANK_STATE_FREE = 0;                // 空闲中
    public final static int TANK_STATE_RUNNING = 1;             // 运行中

    /** 坦克最大血量值 */
    public final static int TANK_MAX_BLOOD = 100;

    /** 坦克尺寸常量 */
    public final static int TANK_WIDTH = 40;
    public final static int TANK_HEIGHT = 40;

    /** 坦克左上角X坐标 */
    private int x;

    /** 坦克左上角Y坐标 */
    private int y;

    /** 坦克速度 */
    private int speed = 8;

    /** 坦克血量 */
    private int hp = TANK_MAX_BLOOD;

    /** 坦克方向 */
    private int direction = TANK_DIRECTION_UP;

    /** 坦克类别 */
    private int tankKind = TANK_KIND_HERO;

    /** 坦克状态 */
    private int state = TANK_STATE_FREE;

    /** 子弹池(考虑线程安全用Vector,没用ArrayList) */
    private Vector<Bullet> bulletPool = new Vector<>();

    /** 上次射击时间(用来设置2次射击最小时间间隔,禁止连续射击) */
    private long lastShootTime = System.currentTimeMillis();

    /**
     * 构造函数
     * @param tankKind 坦克类别
     * @param x 坦克左上角X坐标
     * @param y 坦克左上角Y坐标
     * @param direction 坦克方向
     */
    public Tank(int tankKind, int x, int y, int direction)
    {
        this.tankKind = tankKind;
        this.x = x;
        this.y = y;
        this.direction = direction;
    }

    /**
     * 画坦克
     */
    public void draw(Graphics g)
    {
        // 绘制血条
        g.setColor(Color.YELLOW);
        g.fill3DRect(this.x,this.y,TANK_WIDTH,3,false);   // 底色
        g.setColor(Color.RED);
        g.fill3DRect(this.x,this.y,(hp * TANK_WIDTH)/TANK_MAX_BLOOD,3,false);   // 血量(计算宽度时由于都是int类型,因此除法是放到最后计算的,防止出现结果为0的问题【int取整了】)
        g.setColor(Color.WHITE);
        g.draw3DRect(this.x,this.y,TANK_WIDTH,3,false);   // 边框
        int tankToBloodHeight = 5;  // 坦克到血条的高度
        // 设置坦克颜色
        if(this.tankKind == TANK_KIND_HERO)    // 我方坦克颜色
        {
            g.setColor(TANK_COLOR_HERO);
        }
        else    // 敌方坦克颜色
        {
            g.setColor(TANK_COLOR_ENEMY);
        }
        // 绘制四周虚线
        for (int i = 0; i <= 10; i++)
        {
            g.drawOval(x - 1,y + 4 * i - 1, 1,1);
            g.drawOval(x + TANK_WIDTH - 1,y + 4 * i - 1, 1,1);
            if(i < 10){g.drawOval(x + 4 * i - 1,y + TANK_HEIGHT - 1, 1,1);}
        }
        // 根据方向开始绘制坦克
        switch (direction)
        {
            case TANK_DIRECTION_UP:
                g.fill3DRect(this.x + tankToBloodHeight,this.y + tankToBloodHeight,7,30,false);            // 左边轮子
                g.fill3DRect(this.x + 23 + tankToBloodHeight,this.y + tankToBloodHeight,7,30,false);       // 右边轮子
                g.fill3DRect(this.x + 7 + tankToBloodHeight,this.y + 5 + tankToBloodHeight,16,20,false);   // 驾驶室
                g.fillOval(this.x + 9 + tankToBloodHeight,this.y + 9 + tankToBloodHeight,12,12);                  // 炮台
                g.fill3DRect(x + 14 + tankToBloodHeight,y + tankToBloodHeight,3,15,false);                 // 炮管
                break;
            case TANK_DIRECTION_RIGHT:
                g.fill3DRect(this.x + tankToBloodHeight,this.y + tankToBloodHeight,30,7,false);            // 上边轮子
                g.fill3DRect(this.x + tankToBloodHeight,this.y + 23 + tankToBloodHeight,30,7,false);       // 下边轮子
                g.fill3DRect(this.x + 5 + tankToBloodHeight,this.y + 7 + tankToBloodHeight,20,16,false);   // 驾驶室
                g.fillOval(this.x + 9 + tankToBloodHeight,this.y + 9 + tankToBloodHeight,12,12);                  // 炮台
                g.fill3DRect(x + 15 + tankToBloodHeight,y + 14 + tankToBloodHeight,15,3,false);            // 炮管
                break;
            case TANK_DIRECTION_DOWN:
                g.fill3DRect(this.x + tankToBloodHeight,this.y + tankToBloodHeight,7,30,false);            // 左边轮子
                g.fill3DRect(this.x + 23 + tankToBloodHeight,this.y + tankToBloodHeight,7,30,false);       // 右边轮子
                g.fill3DRect(this.x + 7 + tankToBloodHeight,this.y + 5 + tankToBloodHeight,16,20,false);   // 驾驶室
                g.fillOval(this.x + 9 + tankToBloodHeight,this.y + 9 + tankToBloodHeight,12,12);                  // 炮台
                g.fill3DRect(x + 14 + tankToBloodHeight,y + 15 + tankToBloodHeight,3,15,false);            // 炮管
                break;
            case TANK_DIRECTION_LEFT:
                g.fill3DRect(this.x + tankToBloodHeight,this.y + tankToBloodHeight,30,7,false);            // 上边轮子
                g.fill3DRect(this.x + tankToBloodHeight,this.y + 23 + tankToBloodHeight,30,7,false);       // 下边轮子
                g.fill3DRect(this.x + 5 + tankToBloodHeight,this.y + 7 + tankToBloodHeight,20,16,false);   // 驾驶室
                g.fillOval(this.x + 9 + tankToBloodHeight,this.y + 9 + tankToBloodHeight,12,12);                  // 炮台
                g.fill3DRect(x + tankToBloodHeight,y + 14 + tankToBloodHeight,15,3,false);                 // 炮管
                break;
        }

    }

    /**
     * 坦克移动
     * @param direction 移动方向
     */
    public void move(int direction)
    {
        if(this.direction == direction)     // 方向相同,加速前进(要判断边界及是否碰撞到别的对象)
        {
            if(!GameLogic.getInstance().tankMoveCollide(this))   // 没有碰撞,可以移动
            {
                switch (direction)
                {
                    case TANK_DIRECTION_UP:
                        this.y -= this.speed;
                        break;
                    case TANK_DIRECTION_RIGHT:
                        this.x += this.speed;
                        break;
                    case TANK_DIRECTION_DOWN:
                        this.y += this.speed;
                        break;
                    case TANK_DIRECTION_LEFT:
                        this.x -= this.speed;
                        break;
                }
            }
        }
        else    // 方向不同,仅调整方向不前进
        {
            this.direction = direction;
        }
    }

    /**
     * 坦克射击
     */
    public void shoot()
    {
        // 禁止连续射击
        long curShootTime = System.currentTimeMillis();
        if((curShootTime - this.lastShootTime) >= 500)  // 2发/秒
        {
            this.lastShootTime = curShootTime;
        }
        else
        {
            return;
        }

        // 在子弹池中寻找空闲的子弹
        Bullet bullet = null;
        for (int i = 0; i < bulletPool.size(); i++)
        {
            Bullet tmpBullet = bulletPool.get(i);
            if(tmpBullet.getState() == Bullet.BULLET_STATE_FREE)
            {
                bullet = tmpBullet;
                //System.out.println("找到空闲的子弹了..................");
                break;
            }
        }

        // 没有就增加一个
        if(bullet == null)
        {
            bullet = new Bullet(this);
            //System.out.println("新建子弹了.................");
            bulletPool.add(bullet);
        }

        // 设置子弹位置
        switch (this.direction)
        {
            case TANK_DIRECTION_UP:
                bullet.setPosition(this.x + TANK_WIDTH/2 - bullet.getWidth()/2,this.y - bullet.getHeight(),this.direction);
                break;
            case TANK_DIRECTION_RIGHT:
                bullet.setPosition(this.x + TANK_WIDTH,this.y + TANK_HEIGHT/2 - bullet.getHeight()/2,this.direction);
                break;
            case TANK_DIRECTION_DOWN:
                bullet.setPosition(this.x + TANK_WIDTH/2 - bullet.getWidth()/2,this.y + TANK_HEIGHT,this.direction);
                break;
            case TANK_DIRECTION_LEFT:
                bullet.setPosition(this.x - bullet.getWidth(),this.y + TANK_HEIGHT/2 - bullet.getHeight()/2,this.direction);
                break;
        }

        // 让子弹飞一会
        bullet.fly();
    }

    /** 坦克受到伤害 */
    public void hurt(Bullet bullet)
    {
        this.hp -= bullet.getAtk();
        if(this.hp < 0){this.hp = 0;}
    }

    /**
     * 坦克是否死亡
     */
    public boolean isDead()
    {
        return this.hp <= 0;
    }

    /**
     * 坦克开始工作了
     */
    public void work()
    {
        this.state = TANK_STATE_RUNNING;
    }

    /** 坦克重置 */
    public void reset()
    {
        this.setX(-100);
        this.setY(-100);
        this.setHp(Tank.TANK_MAX_BLOOD);
        this.setState(TANK_STATE_FREE);
    }

    public int getX(){return x;}

    public void setX(int x){this.x = x;}

    public int getY(){return y;}

    public void setY(int y){this.y = y;}

    public int getSpeed(){return speed;}

    public void setSpeed(int speed){this.speed = speed;}

    public int getHp(){return hp;}

    public void setHp(int hp){this.hp = hp;}

    public int getDirection(){return direction;}

    public void setDirection(int direction){this.direction = direction;}

    public int getState(){return state;}

    public void setState(int state){this.state = state;}

    public int getTankKind(){return tankKind;}

    public Vector<Bullet> getBulletPool(){return bulletPool;}

}
  1. Hero.javaour tank

package lag.game.tankwar;

/**
 * 我方的坦克
 */
public class Hero extends Tank
{
    /**
     * 构造函数
     * @param x 坦克左上角X坐标
     * @param y 坦克左上角Y坐标
     * @param direction 坦克方向
     */
    public Hero(int x, int y, int direction)
    {
        super(TANK_KIND_HERO, x, y, direction);
    }

}
  1. Enemy.java enemy tank

package lag.game.tankwar;

/**
 * 敌方的坦克
 */
public class Enemy extends Tank
{
    /**
     * 构造函数
     * @param x 坦克左上角X坐标
     * @param y 坦克左上角Y坐标
     * @param direction 坦克方向
     */
    public Enemy(int x, int y, int direction)
    {
        super(TANK_KIND_ENEMY, x, y, direction);
    }

    /**
     * 起来干活了
     */
    @Override
    public void work()
    {
        super.work();
        // 启动线程让坦克跑(看看敌方坦克的AI)
        new Thread(()->{
            //System.out.println("坦克线程启动...");
            while (this.getState() == TANK_STATE_RUNNING)
            {
                try
                {
                    Thread.sleep(200);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                // 随机确认是原地不动还是移动(0-不动,其它-移动)
                if(GameLogic.getRandomInt(0,8) != 0)   // 移动(概率设大点)
                {
                    // 随机确认是改变方向还是向前移动(0-改变方向,其它-向前移动)
                    if(GameLogic.getRandomInt(0,12) == 0)   // 改变方向(概率设小点)
                    {
                        int newDirection = GameLogic.getRandomInt(TANK_DIRECTION_UP, TANK_DIRECTION_LEFT + 3);
                        if(newDirection > TANK_DIRECTION_LEFT){newDirection = TANK_DIRECTION_DOWN;}    // 向下概率设大点
                        this.setDirection(newDirection);
                    }
                    else    // 向前移动
                    {
                        // 如果到边界了就别顶牛了,赶紧换方向
                        boolean toBorder = false;     // 默认未到边界
                        switch (this.getDirection())
                        {
                            case TANK_DIRECTION_UP:
                                if((this.getY() - this.getSpeed() < 0))
                                {
                                    toBorder = true;
                                }
                                break;
                            case TANK_DIRECTION_RIGHT:
                                if((this.getX() + TANK_WIDTH + this.getSpeed() > GamePanel.GAME_ACTION_WIDTH))
                                {
                                    toBorder = true;
                                }
                                break;
                            case TANK_DIRECTION_DOWN:
                                if((this.getY() + TANK_HEIGHT + this.getSpeed() > GamePanel.GAME_ACTION_HEIGHT))
                                {
                                    toBorder = true;
                                }
                                break;
                            case TANK_DIRECTION_LEFT:
                                if((this.getX() - this.getSpeed() < 0))
                                {
                                    toBorder = true;
                                }
                                break;
                        }
                        if(toBorder)  // 到边界了赶紧换方向
                        {
                            int newDirection = GameLogic.getRandomInt(TANK_DIRECTION_UP, TANK_DIRECTION_LEFT);
                            this.setDirection(newDirection);
                        }
                        else    // 继续前进
                        {
                            this.move(this.getDirection());
                        }
                    }
                }
                // 随机确认是发射子弹还是省子弹(1-发射,其它-不发射)
                if(GameLogic.getRandomInt(0,8) == 1)
                {
                    this.shoot();
                }
            }
            //System.out.println("坦克线程退出历史舞台了...");
        }).start();
    }

}
  1. Bullet.java bullet class

package lag.game.tankwar;

import java.awt.*;

/**
 * 子弹类<br>
 */
public class Bullet
{
    /** 子弹状态常量 */
    public final static int BULLET_STATE_FREE = 0;      // 空闲中
    public final static int BULLET_STATE_RUNNING = 1;   // 运行中

    /** 子弹攻击力常量 */
    public final static int BULLET_MAX_ATTACK = 100;    // 最大攻击力
    public final static int BULLET_MIN_ATTACK = 20;     // 最小攻击力

    /** 子弹左上角X坐标 */
    private int x;

    /** 子弹左上角Y坐标 */
    private int y;

    /** 子弹宽度 */
    private int width = 10;

    /** 子弹高度 */
    private int height = 10;

    /** 子弹方向 */
    private int direction;

    /** 子弹速度 */
    private int speed = 16;

    /** 子弹状态 */
    private int state = BULLET_STATE_FREE;

    /** 所属坦克 */
    private Tank tank;

    /** 子弹攻击力 */
    private int atk;

    /**
     * 构造函数
     * @param tank 所属坦克
     */
    public Bullet(Tank tank)
    {
        this.tank = tank;
        this.atk = GameLogic.getRandomInt(BULLET_MIN_ATTACK,BULLET_MAX_ATTACK);
    }

    /**
     * 设置子弹位置
     */
    public void setPosition(int x,int y,int direction)
    {
        this.x = x;
        this.y = y;
        this.direction = direction;
    }

    /**
     * 功能:子弹重置<br>
     */
    public void reset()
    {
        this.x = -100;
        this.y = -100;
        this.atk = GameLogic.getRandomInt(BULLET_MIN_ATTACK,BULLET_MAX_ATTACK);     // 重新随机生成攻击力
        this.state = BULLET_STATE_FREE;
    }

    /**
     * 让子弹飞一会
     */
    public void fly()
    {
        // 设置子弹状态为运行中
        this.state = BULLET_STATE_RUNNING;

        // 启动线程让子弹飞
        new Thread(()->{
            //System.out.println(tank.getTankKind() + "子弹线程启动...");
            while (this.state == BULLET_STATE_RUNNING)  //tank.getState() == Tank.TANK_STATE_RUNNING &&
            {
                try
                {
                    Thread.sleep(50);
                }
                catch (InterruptedException e)
                {
                    e.printStackTrace();
                }

                switch (direction)
                {
                    case Tank.TANK_DIRECTION_UP:
                        if(this.y - this.speed >= 0)
                        {
                            this.y -= this.speed;
                        }
                        else
                        {
                            this.reset();
                        }
                        break;
                    case Tank.TANK_DIRECTION_RIGHT:
                        if(this.x + this.width + this.speed <= GamePanel.GAME_ACTION_WIDTH)
                        {
                            this.x += this.speed;
                        }
                        else
                        {
                            this.reset();
                        }
                        break;
                    case Tank.TANK_DIRECTION_DOWN:
                        if(this.y + this.height + this.speed <= GamePanel.GAME_ACTION_HEIGHT)
                        {
                            this.y += this.speed;
                        }
                        else
                        {
                            this.reset();
                        }
                        break;
                    case Tank.TANK_DIRECTION_LEFT:
                        if(this.x >= this.speed)
                        {
                            this.x -= this.speed;
                        }
                        else
                        {
                            this.reset();
                        }
                        break;
                }
            }
            //System.out.println("子弹线程退出历史舞台了......");
        }).start();
    }

    /**
     * 画子弹
     */
    public void draw(Graphics g)
    {
        // 开始画子弹
        if(tank.getTankKind() == Tank.TANK_KIND_HERO)    // 我方子弹
        {
            g.setColor(Tank.TANK_COLOR_HERO);
        }
        else    // 敌方子弹
        {
            g.setColor(Tank.TANK_COLOR_ENEMY);
        }
        g.fillOval(this.x,this.y,this.width,this.height);
        // 根据攻击力画加强子弹
        g.setColor(Color.RED);
        if(this.atk == 100)
        {
            g.fillOval(this.x,this.y,this.width,this.height);
        }
        else if(this.atk >= 90)
        {
            g.fillOval(this.x + 1,this.y + 1,this.width - 2,this.height - 2);
        }
        else if(this.atk >= 70)
        {
            g.fillOval(this.x + 2,this.y + 2,this.width - 4,this.height - 4);
        }
        else if(this.atk >= 50)
        {
            g.fillOval(this.x + 3,this.y + 3,this.width - 6,this.height - 6);
        }
    }

    public int getX(){return x;}

    public int getY(){return y;}

    public int getWidth(){return width;}

    public int getHeight(){return height;}

    public int getDirection(){return direction;}

    public int getSpeed(){return speed;}

    public int getState(){return state;}

    public int getAtk(){return atk;}

}
  1. Bomb.java explosion effect

package lag.game.tankwar;

import java.awt.*;

/**
 * 爆炸效果
 */
public class Bomb
{
    /** 爆炸状态常量 */
    public final static int BOMB_STATE_FREE = 0;      // 空闲中
    public final static int BOMB_STATE_RUNNING = 1;   // 运行中

    /** 爆炸左上角X坐标 */
    private int x;

    /** 爆炸左上角Y坐标 */
    private int y;

    /** 爆炸宽度 */
    private int width = 20;

    /** 爆炸高度 */
    private int height = 20;

    /** 状态 */
    private int state = BOMB_STATE_FREE;

    /** 爆炸进度,根据它来决定显示哪种爆炸形状(-1-不显示任何爆炸形状,0-准备显示,1-显示第一种爆炸形状,2-显示第二种爆炸形状,3-显示第三种爆炸形状) */
    public int bombProgress = -1;

    /**
     * 构造函数
     * @param x 爆炸左上角X坐标
     * @param y 爆炸左上角Y坐标
     */
    public Bomb(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    /**
     * 起来干活,准备爆炸。
     */
    public void work()
    {
        // 设置爆炸状态为运行中
        this.state = BOMB_STATE_RUNNING;

        // 爆炸进度,准备显示
        bombProgress = 0;
    }

    /**
     * 爆炸重置
     */
    public void reset()
    {
        this.setX(-100);
        this.setY(-100);
        this.state = BOMB_STATE_FREE;
        this.bombProgress = -1;
    }

    /**
     * 画爆炸<br>
     * 每帧显示一种形状,共三种,即3帧结束爆炸<br>
     */
    public void draw(Graphics g)
    {
        // 进度走起
        this.bombProgress++;

        // 根据进度显示某种爆炸形状
        switch (this.bombProgress)
        {
            case 1:         // 第一种形状
                g.setColor(Color.WHITE);
                g.fillRoundRect(this.x + 5,this.y + 5,20,20,10,10);
                g.fillOval(this.x + 10,this.y,10,30);
                g.setColor(Color.YELLOW);
                g.drawOval(this.x + 10,this.y,10,30);
                g.setColor(Color.WHITE);
                g.fillOval(this.x,this.y + 10,30,10);
                g.setColor(Color.YELLOW);
                g.drawOval(this.x,this.y + 10,30,10);
                break;
            case 2:         // 第二种形状
                g.setColor(Color.WHITE);
                g.fillOval(this.x + 12,this.y + 5,6,20);
                g.setColor(Color.YELLOW);
                g.drawOval(this.x + 12,this.y + 5,6,20);
                g.setColor(Color.WHITE);
                g.fillOval(this.x + 5,this.y + 12,20,6);
                g.setColor(Color.YELLOW);
                g.drawOval(this.x + 5,this.y + 12,20,6);
                break;
            case 3:         // 第三种形状
                g.setColor(Color.WHITE);
                g.fillOval(this.x + 10,this.y + 10,10,10);
                g.setColor(Color.YELLOW);
                g.drawOval(this.x + 10,this.y + 10,10,10);
                break;
            default:
                this.reset();
                break;
        }
    }

    public int getX(){return x;}

    public void setX(int x){this.x = x;}

    public int getY(){return y;}

    public void setY(int y){this.y = y;}

    public int getWidth(){return width;}

    public void setWidth(int width){this.width = width;}

    public int getHeight(){return height;}

    public void setHeight(int height){this.height = height;}

    public int getState(){return state;}

}
  1. Block.javaThe parent class of bricks and iron blocks

package lag.game.tankwar;

import java.awt.*;

/**
 * 砖块与铁块的父类
 */
public class Block
{
    /** 块类别常量 */
    public final static int BLOCK_KIND_BRICK = 1;     // 砖块
    public final static int BLOCK_KIND_IRON = 2;      // 铁块

    /** 块尺寸常量(默认4个块=1坦克大小) */
    public final static int BLOCK_WIDTH = Tank.TANK_WIDTH / 2;
    public final static int BLOCK_HEIGHT = Tank.TANK_HEIGHT / 2;

    /** 块左上角X坐标 */
    private int x;

    /** 块左上角Y坐标 */
    private int y;

    /** 块类别 */
    private int blockKind = BLOCK_KIND_BRICK;

    /**
     * 构造函数
     * @param blockKind 块类别
     * @param x 块左上角X坐标
     * @param y 块左上角Y坐标
     */
    public Block(int blockKind,int x, int y)
    {
        this.blockKind = blockKind;
        this.x = x;
        this.y = y;
    }

    /**
     * 画块
     */
    public void draw(Graphics g)
    {
        if(this.blockKind == BLOCK_KIND_BRICK)   // 画砖块
        {
            g.setColor(new Color(210, 105, 30));
            g.fillRect(this.x,this.y,BLOCK_WIDTH,BLOCK_HEIGHT);
            g.setColor(new Color(244, 164, 96));
            g.drawLine(this.x,this.y,this.x + BLOCK_WIDTH - 1,this.y);
            g.drawLine(this.x,this.y + BLOCK_HEIGHT / 2,this.x + BLOCK_WIDTH - 1,this.y + BLOCK_HEIGHT / 2);
            g.drawLine(this.x,this.y,this.x,this.y + BLOCK_HEIGHT / 2);
            g.drawLine(this.x + BLOCK_WIDTH / 2,this.y + BLOCK_HEIGHT / 2, this.x + BLOCK_WIDTH / 2, this.y + BLOCK_HEIGHT - 1);
        }
        else if(this.blockKind == BLOCK_KIND_IRON)      // 画铁块
        {
            g.setColor(new Color(190, 190, 190));
            g.fillRect(this.x,this.y,BLOCK_WIDTH,BLOCK_HEIGHT);
            g.setColor(Color.WHITE);
            g.fillRect(this.x + 3,this.y + 3,BLOCK_WIDTH - 6,BLOCK_HEIGHT - 6);
            g.draw3DRect(this.x + 3,this.y + 3,BLOCK_WIDTH - 6,BLOCK_HEIGHT - 6,true);
            g.drawLine(this.x + 1,this.y + 1,this.x + 3,this.y + 3);
            g.drawLine(this.x + BLOCK_WIDTH - 1,this.y + 1,this.x + BLOCK_WIDTH - 3,this.y + 3);
            g.drawLine(this.x + 1,this.y + BLOCK_HEIGHT - 1,this.x + 3,this.y + BLOCK_HEIGHT - 3);
            g.drawLine(this.x + BLOCK_WIDTH - 1,this.y + BLOCK_HEIGHT - 1,this.x + BLOCK_WIDTH - 3,this.y + BLOCK_HEIGHT - 3);
        }
    }

    public int getX(){return x;}

    public void setX(int x){this.x = x;}

    public int getY(){return y;}

    public void setY(int y){this.y = y;}

    public int getBlockKind(){return blockKind;}

}
  1. Brick.java brick

package lag.game.tankwar;

/**
 * 砖块
 */
public class Brick extends Block
{
    /**
     * 构造函数
     * @param x 砖块左上角X坐标
     * @param y 砖块左上角Y坐标
     */
    public Brick(int x, int y)
    {
        super(BLOCK_KIND_BRICK, x, y);
    }

}
  1. Iron.java iron block

package lag.game.tankwar;

/**
 * 铁块
 */
public class Iron extends Block
{
    /**
     * 构造函数
     * @param x 铁块左上角X坐标
     * @param y 铁块左上角Y坐标
     */
    public Iron(int x, int y)
    {
        super(BLOCK_KIND_IRON, x, y);
    }

}
  1. Camp.javacamp

package lag.game.tankwar;

import java.awt.*;

/**
 * 营地
 */
public class Camp
{
    /** 营地状态常量 */
    public final static int CAMP_STATE_FREE = 0;                // 空闲中
    public final static int CAMP_STATE_RUNNING = 1;             // 运行中

    /** 营地左上角X坐标 */
    private int x;

    /** 营地左上角Y坐标 */
    private int y;

    /** 营地宽度 */
    private int width = Tank.TANK_WIDTH;

    /** 营地高度 */
    private int height = Tank.TANK_HEIGHT;

    /** 营地状态 */
    private int state = CAMP_STATE_FREE;

    /** 元素宽度 */
    private int elementWidth = 2;

    /** 元素高度 */
    private int elementHeight = 2;

    /** 营地地图(20 * 20) */
    private static byte[][] campMap =
        {
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
            {0,0,1,0,0,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0},
            {1,1,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1},
            {0,2,1,1,1,0,0,0,1,0,0,1,1,0,0,1,1,1,2,0},
            {1,1,2,0,1,0,0,0,1,1,1,1,0,0,0,1,0,2,1,1},
            {0,0,1,2,1,1,0,0,1,1,1,1,0,0,1,1,2,1,0,0},
            {0,1,1,1,2,1,0,0,1,1,1,1,0,0,1,2,1,1,1,0},
            {0,0,1,1,1,2,1,1,1,1,1,1,1,1,2,1,1,1,0,0},
            {0,0,0,1,1,1,2,1,1,1,1,1,1,2,1,1,1,0,0,0},
            {0,0,1,1,1,1,1,1,2,1,1,1,2,1,1,1,1,1,0,0},
            {0,0,0,1,1,1,1,1,1,2,1,2,1,1,1,1,1,0,0,0},
            {0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0},
            {0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0},
            {0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0},
            {0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0},
            {0,0,0,0,0,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0},
            {0,0,0,0,0,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0},
            {0,0,0,0,0,1,1,1,1,0,1,1,1,1,0,0,0,0,0,0},
            {0,0,0,0,0,1,1,0,1,0,1,0,1,1,0,0,0,0,0,0},
            {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
        };

    /**
     * 构造函数
     */
    public Camp(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    /**
     * 画营地
     */
    public void draw(Graphics g)
    {
        int row = campMap.length;
        int column = campMap[0].length;
        for (int i = 0; i < row; i++)
        {
            for (int j = 0; j < column; j++)
            {
                int mapValue = campMap[i][j];
                if(mapValue == 1)
                {
                    g.setColor(Color.WHITE);
                    drawElement(g,this.x + elementWidth * j,this.y + elementHeight * i);
                }
                else if(mapValue == 2)
                {
                    g.setColor(Color.LIGHT_GRAY);
                    drawElement(g,this.x + elementWidth * j,this.y + elementHeight * i);
                }
            }
        }
    }

    /**
     * 画元素
     * @param x 元素左上角X坐标
     * @param y 元素左上角Y坐标
     */
    private void drawElement(Graphics g,int x,int y)
    {
        g.fillRect(x,y,elementWidth,elementHeight);
        g.setColor(Color.WHITE);
        g.draw3DRect(x,y,elementWidth,elementHeight,true);
    }

    public int getX(){return x;}

    public void setX(int x){this.x = x;}

    public int getY(){return y;}

    public void setY(int y){this.y = y;}

    public int getWidth(){return width;}

    public int getHeight(){return height;}

    public int getState(){return state;}

    public void setState(int state){this.state = state;}

}

download:

Connection:https://pan.baidu.com/s/1aqNjqATCjteiAdv90YDByw?pwd=mylx

Extraction code: mylx

Remarks:

This game does not use pictures. They are all drawn in Java. Friends who are interested can use pictures instead. I have packaged the picture materials. Friends who use pictures should pay attention to one problem, that is, it takes time to load pictures. The next interface may be refreshed before it is loaded successfully for the first time. This is also the reason why Teacher Han Shunping did not have an explosion effect when shooting a tank for the first time. There is a BUG in the Toolkit.getDefaultToolkit().createImage method. Just use new ImageIcon (picture path)).getImage() instead.

Guess you like

Origin blog.csdn.net/lag_csdn/article/details/128832853