Java实现坦克大战小游戏

Java实现坦克大战小游戏

主要内容

使用Java面向对象思想设计并开发一个有GUI界面的小游戏坦克大战,允许玩家与电脑玩家进行对战,玩家使用键盘操控坦克,人机具有一定的移动和开火逻辑。

实现功能

1.有游戏图像界面,让玩家能看到游戏给出反馈;

2.有人机交互功能,完成能操控指定游戏元素;

3.不同阵营的坦克,具备不同外观,可分别敌我;

4.敌我坦克皆能移动和开关。

遇到的问题

屏幕闪烁问题

  1. 屏幕刷新时产生闪烁的原因

在MFC对话框上画图形,当需要显示新的图形或使原来的图形显示发生变化时,就需要刷新背景,也就是用背景颜色的画刷把背景重新刷一遍,这样就把原来的图形覆盖掉了,然后画上新图形,即可完成图形的刷新显示或动态显示。但是由于背景颜色(一般是白色)与图形颜色之间的反差,在不断的刷新、显示、刷新、显示过程中就会产生闪烁。这里要强调一下,闪烁本质上就是反差,反差越大,闪烁就越厉害。所以总结来说就是,当窗口由于任何原因需要重绘时,总是先用背景色将显示区清除,然后才调用OnPaint进行重绘,而背景色往往与绘图内容反差很大,这样在短时间内背景色与显示图形的交替出现,使得显示窗口看起来在闪。

  1. 双缓冲技术基本原理

我们知道在我们电脑中,屏幕中显示的东西都会被放在一个称为显示缓存的地方,在通常情况下我们只有一个这样的缓冲区,也就是单缓冲,在单缓冲中任何绘图的过程都会被立即显示在屏幕中,而所谓双缓冲就是再这个显示的缓冲区之外再建立一个不显示的缓冲区,我们所有的绘图都将在这个不显示的缓冲区中进行,只有当一帧都绘制完了之后才会被拷贝到真正的现实缓冲区显示出来,这样中间过程对于最终用户就是不可见的了,那即使是速度比较慢也只会出现停顿而不会有闪烁的现象出现。

  1. 解决思路
    定义一个与屏幕大小一致的图片
    获取图片的画笔
    使用图片画笔将元素绘制到图片中
    使用系统画笔将图片绘制到窗口中

长时间运行导致游戏卡顿的问题

  1. 导致游戏卡顿的原因
    子弹和爆炸效果绘制时会new一个Bullet对象和Explode对象保存在堆内存中,但是在子弹和爆炸效果消失时并没有从内存中销毁Bullet对象和Explode对象,导致随着游戏时间的增加堆内存中的垃圾数据越来越来,进而导致游戏卡顿。
  2. 解决思路
    设置子弹对象池和爆炸效果对象池,在类加载时将子弹对象和爆炸效果对象添加到容器中。需要用要绘制子弹时就从对象池中取出子弹,当子弹超出并不范围时,将该子弹归还到子弹对象池。爆炸效果对象池同理。

游戏界面

主菜单

Java实现坦克大战小游戏_第1张图片

游戏界面

Java实现坦克大战小游戏_第2张图片

项目结构

app包下的GameMain类是游戏的入口函数。img目录下存放游戏的静态资源。
Java实现坦克大战小游戏_第3张图片

tank包

该包下的类是tank的实体类,tank类分为我方坦克MyTank类、敌方坦克EnemyTank类。两个坦克类都继承了坦克父类tank类。

tank.class

/**
 * 坦克类的属性
 */
public abstract class Tank {

    //坦克前进的四个方向
    public static final int DIR_UP = 0;
    public static final int DIR_DOWN = 1;
    public static final int DIR_LEFT = 2;
    public static final int DIR_RIGHT = 3;

    //坦克的半径
    public static final int RADIUS = 30;

    //默认速度
    public static final int DEFAULT_SPEED = 10;

    //坦克状态
    public static final int STATE_STAND = 0;
    public static final int STATE_MOVE = 1;
    public static final int STATE_DIE = 2;

    //坦克的初始生命值
    public static final int DEFAULT_HP = 1000;

    private int x, y;
    private int hp = DEFAULT_HP;
    private int atk;//攻击力
    private int speed = DEFAULT_SPEED;
    private int dir;//标识坦克的方向
    private int state = STATE_STAND;//标识坦克的状态
    private Color color;
    private boolean isEnemy = false;//标识是否是敌人
    get/set...
    }

tank类的方法

//使用ArrayList容器管理子弹
    private List bullets = new ArrayList();

    //使用ArrayList容器管理爆炸效果
    private List explodes = new ArrayList<>();

    public Tank(int x, int y, int dir) {
        this.x = x;
        this.y = y;
        this.dir = dir;
        color = MyUtil.getRandomColor();
    }

    /**
     * 绘制坦克
     *
     * @param g
     */
    public void draw(Graphics g) {

        logic();//调用坦克逻辑处理方法,包括move方法

        drawTankImg(g);

        drawBullet(g);//调用绘制子弹的方法

    }

    /**
     * 使用图片的方法绘制坦克
     *
     * @param g
     */
    public abstract void drawTankImg(Graphics g);

    /**
     * 使用系统的方法绘制坦克
     *
     * @param g
     */
    private void drawTank(Graphics g) {
        g.setColor(color);

        //绘制坦克圆
        g.fillOval(x - RADIUS, y - RADIUS, RADIUS << 1, RADIUS << 1);
        //绘制坦克炮管
        //炮管的坐标
        int endX = x;
        int endY = y;
        switch (dir) {
            case DIR_UP:
                endY = y - RADIUS * 2;
                break;
            case DIR_DOWN:
                endY = y + RADIUS * 2;
                break;
            case DIR_LEFT:
                endX = x - RADIUS * 2;
                break;
            case DIR_RIGHT:
                endX = x + RADIUS * 2;
                break;
        }
        g.drawLine(x, y, endX, endY);
    }

    //坦克逻辑处理
    private void logic() {
        switch (state) {
            case STATE_STAND:
                break;
            case STATE_MOVE:
                move();
                break;
            case STATE_DIE:
                break;
        }
    }

    //坦克移动的功能
    private void move() {
        switch (dir) {
            case DIR_UP:
                y -= speed;
                if (y < RADIUS + GameFrame.titleBarH) {
                    y = RADIUS + GameFrame.titleBarH;
                }
                break;
            case DIR_DOWN:
                y += speed;
                if (y > Constant.FRAME_HEIGHT - RADIUS - GameFrame.titleBarH) {
                    y = Constant.FRAME_HEIGHT - RADIUS - GameFrame.titleBarH;
                }
                break;
            case DIR_LEFT:
                x -= speed;
                if (x < RADIUS) {
                    x = RADIUS;
                }
                break;
            case DIR_RIGHT:
                x += speed;
                if (x > Constant.FRAME_WIDTH - RADIUS - GameFrame.titleBarH) {
                    x = Constant.FRAME_WIDTH - RADIUS - GameFrame.titleBarH;
                }
                break;
        }
    }

    /**
     * 坦克发射炮弹的方法
     * 创建一个子弹实例,并将子弹加入到子弹容器中
     */
    public void fire() {
        int bulletX = x;
        int bulletY = y;

        switch (dir) {
            case DIR_UP:
                bulletY -= RADIUS;
                break;
            case DIR_DOWN:
                bulletY += RADIUS;
                break;
            case DIR_LEFT:
                bulletX -= RADIUS;
                break;
            case DIR_RIGHT:
                bulletX += RADIUS;
                break;
        }
//        Bullet bullet = new Bullet(bulletX, bulletY, dir, atk, color);
        Bullet bullet = BulletPool.get();//从池塘中获取子弹
        bullet.setX(bulletX);
        bullet.setY(bulletY);
        bullet.setDir(dir);
        bullet.setAtk(atk);
        bullet.setColor(color);
        bullet.setVisible(true);
        bullets.add(bullet);
    }

    /**
     * 绘制出容器里的子弹
     *
     * @param g
     */
    private void drawBullet(Graphics g) {
        for (Bullet bullet : bullets) {
            bullet.draw(g);
        }

        //遍历所有子弹,并将超出frame的子弹还原到对象池
        for (int i = 0; i < bullets.size(); i++) {
            Bullet bullet = bullets.get(i);
            if (!bullet.isVisible()) {//如果当前子弹超出frame,则还原到对象池
                Bullet remove = bullets.remove(i);
                BulletPool.theReturn(remove);
                i--;
            }
        }
    }

    /**
     * 坦克与敌方子弹的碰撞检测
     *
     * @param bullets
     */
    public void collideBullet(List bullets) {
        //遍历集合的所有的子弹,依次进行碰撞检测
        for (Bullet bullet : bullets) {
            int bulletX = bullet.getX();
            int bulletY = bullet.getY();
            if (MyUtil.isCollide(x, y, RADIUS, bulletX, bulletY)) {
                //子弹消失
                bullet.setVisible(false);

                //添加爆炸效果
                Explode explode = ExplodePool.get();
                explode.setX(x + RADIUS * 2);
                explode.setY(y + RADIUS / 2);
                explode.setVisible(true);
                explode.setIndex(0);
                explodes.add(explode);
            }
        }
    }

    /**
     * 绘制当前坦克的爆炸效果
     *
     * @param g
     */
    public void drawExplodes(Graphics g) {
        for (Explode explode : explodes) {
            explode.draw(g);
        }

        for (int i = 0; i < explodes.size(); i++) {
            Explode explode = explodes.get(i);

            if (!explode.isVisible()) {
                Explode remove = explodes.remove(i);
                ExplodePool.theReturn(remove);
                i--;
            }
        }
    }

我方坦克MyTank.class

public class MyTank extends Tank {

    private static Image[] tankImg;

    static {
        tankImg = new Image[4];
        tankImg[0] = getImage("img/p1tankU.gif");
        tankImg[1] = getImage("img/p1tankD.gif");
        tankImg[2] = getImage("img/p1tankL.gif");
        tankImg[3] = getImage("img/p1tankR.gif");


    }

    public MyTank(int x, int y, int dir) {
        super(x, y, dir);
    }

    @Override
    public void drawTankImg(Graphics g) {
        g.drawImage(tankImg[getDir()], getX() - RADIUS, getY() - RADIUS, null);
    }
}

敌方坦克EnemyTank.class

敌方坦克为电脑控制,有简单的AI,可自动移动开火。

public class EnemyTank extends Tank {

    private static Image[] enemyImg;

    static {
        enemyImg = new Image[4];
        enemyImg[0] = getImage("img/p2tankU.gif");
        enemyImg[1] = getImage("img/p2tankD.gif");
        enemyImg[2] = getImage("img/p2tankL.gif");
        enemyImg[3] = getImage("img/p2tankR.gif");
    }

    //记录ai开始运行的时间
    private long aiTime;

    public EnemyTank(int x, int y, int dir) {
        super(x, y, dir);

        //敌人一旦开始创建就计时
        aiTime = System.currentTimeMillis();
    }

    //创建一个敌人的坦克
    public static Tank createEnemy() {
        int x = MyUtil.getRandomNumber(0, 2) == 0 ? RADIUS : Constant.FRAME_WIDTH - GameFrame.titleBarH;
        int y = GameFrame.titleBarH + RADIUS;
        int dir = DIR_DOWN;
        Tank enemy = new EnemyTank(x, y, dir);
        enemy.setEnemy(true);
        enemy.setState(STATE_MOVE);
        return enemy;
    }

    public void drawTankImg(Graphics g) {
        ai();
        g.drawImage(enemyImg[getDir()], getX() - RADIUS, getY() - RADIUS, null);
    }

    /**
     * 敌人的AI
     */
    private void ai() {

        //间隔五秒切换一个状态
        if (System.currentTimeMillis() - aiTime > Constant.ENEMY_AI_INTERVAL) {

            //五秒随机一个方向
            setDir(MyUtil.getRandomNumber(DIR_UP, DIR_RIGHT + 1));

            //五秒随机一个状态
            setState(MyUtil.getRandomNumber(0, 2) == 0 ? STATE_STAND : STATE_MOVE);

            //重新计时
            aiTime = System.currentTimeMillis();
        }

        //设置开火逻辑
        if (Math.random() < Constant.ENEMY_AI_PERCENT) {
            fire();
        }
    }
}

game包

game包下的类是关于绘制可视化图像,使用Graphics类绘制游戏窗口。

控制子弹效果 Bullet.class

/***
  * 控制子弹效果 
  */
public class Bullet {

    //子弹的默认速度是坦克的两倍
    private static final int DEFAULT_SPEED = Tank.DEFAULT_SPEED << 1;

    //子弹的半径
    private static final int RADIUS = 4;

    private int x, y;
    private int speed = DEFAULT_SPEED;
    private int dir;
    private int atk;
    private Color color;
    private boolean visible = true;//子弹是否可见,默认可见

    public Bullet(int x, int y, int dir, int atk, Color color) {
        this.x = x;
        this.y = y;
        this.dir = dir;
        this.atk = atk;
        this.color = color;
    }

    /**
     * 默认无参的构造方法,给对象池使用
     */
    public Bullet() {

    }

    /**
     * 绘制炮弹的方法
     *
     * @param g
     */
    public void draw(Graphics g) {
        if (!visible) {
            return;
        }
        logic();
        g.setColor(color);
        g.fillOval(x - RADIUS, y - RADIUS, RADIUS << 1, RADIUS << 1);
    }

    /**
     * 炮弹的逻辑处理方法
     */
    private void logic() {
        move();
    }

    /**
     * 炮弹的运动方法
     *
     * @return
     */
    private void move() {
        switch (dir) {
            case Tank.DIR_UP:
                y -= speed;
                if (y < 0) {
                    visible = false;
                }
                break;
            case Tank.DIR_DOWN:
                y += speed;
                if (y > Constant.FRAME_HEIGHT) {
                    visible = false;
                }
                break;
            case Tank.DIR_LEFT:
                x -= speed;
                if (x < 0) {
                    visible = false;
                }
                break;
            case Tank.DIR_RIGHT:
                x += speed;
                if (x > Constant.FRAME_WIDTH) {
                    visible = false;
                }
                break;
        }
    }
}

控制爆炸效果Explode.class

/**
 * 用于控制爆炸效果的类
 */
public class Explode {
    //爆炸帧数的个数
    private static final int EXPLODE_FRAME_COUNT = 4;

    //爆炸图片
    private static Image[] boom_img;
    private static int explodeWidth;//爆炸图片的宽
    private static int explodeHeight;//爆炸图片的宽

    static {
        boom_img = new Image[EXPLODE_FRAME_COUNT];
        boom_img[0] = getImage("img/blast1.gif");
        boom_img[1] = getImage("img/blast2.gif");
        boom_img[2] = getImage("img/blast3.gif");
        boom_img[3] = getImage("img/blast4.gif");
    }

    //爆炸属性
    private int x, y;//坐标
    private int index;//当前图片帧数下标【0-3】
    private boolean visible = true;//是否可见

    public Explode(int x, int y) {
        this.x = x;
        this.y = y;
        index = 0;
    }

    public Explode() {
        index = 0;
    }

    //绘制爆炸效果
    public void draw(Graphics g) {

        if (explodeHeight <= 0) {
            explodeWidth = boom_img[0].getWidth(null);
            explodeHeight = boom_img[0].getHeight(null) >> 1;
        }
        if (!visible) {
            return;
        }
        g.drawImage(boom_img[index], x - explodeWidth, y - explodeHeight, null);
        index++;

        //播放到最后一帧,设置为不可见
        if (index >= EXPLODE_FRAME_COUNT) {
            visible = false;
        }
    }
}

游戏主窗口GameFrame.class

/**
 * 游戏主窗口
 */
public class GameFrame extends Frame implements Runnable {

    /**
     * 双缓冲技术解决屏幕闪烁问题
     * 1.定义一个与屏幕大小一致的图片
     * 2.获取图片的画笔
     * 3.使用图片画笔将元素绘制到图片中
     * 4.使用系统画笔将图片绘制到窗口中
     */
    private BufferedImage bufImg = new BufferedImage(FRAME_WIDTH, FRAME_HEIGHT, BufferedImage.TYPE_4BYTE_ABGR);

    //游戏状态
    public static int gameState;

    //菜单指向
    private static int menuIndex;

    //定义坦克对象
    private Tank myTank;

    //定义敌人坦克
    private List enemies = new ArrayList<>();

    //标题栏的高度
    public static int titleBarH;

    //对窗口进行初始化
    public GameFrame() {
        initFrame();
        initEventListener();

        //启动刷新窗口的线程
        new Thread(this).start();
    }

    //对游戏进行初始化
    public void initGame() {
        gameState = STATE_MENU;
    }

    //对窗口属性进行初始化
    private void initFrame() {

        setTitle(GAME_TITLE);
        //设置窗口可见
        setVisible(true);

        setSize(FRAME_WIDTH, FRAME_HEIGHT);

        setLocation(FRAME_X, FRAME_Y);

        setResizable(false);

        titleBarH = getInsets().top;

    }


    /**
     * 该方法负责绘制所有在屏幕中显示的内容,继承Frame类
     * 该方法不能主动调用,只能通过repaint方法回调该方法
     *
     * @param graphics
     */
    @Override
    public void update(Graphics graphics) {
        //实例化图片画笔g
        Graphics g = bufImg.getGraphics();
        //使用画笔g绘制元素
        g.setFont(GAME_FONT);
        switch (gameState) {
            case STATE_MENU:
                drawMenu(g);
                break;
            case STATE_HELP:
                drawHelp(g);
                break;
            case STATE_ABOUT:
                drawAbout(g);
                break;
            case STATE_RUN:
                drawRun(g);
                break;
            case STATE_OVER:
                drawOver(g);
                break;
        }
        //使用系统画笔将bufImg绘制到frame中
        graphics.drawImage(bufImg, 0, 0, null);
    }

    private void drawOver(Graphics g) {
    }

    private void drawAbout(Graphics g) {
    }

    /**
     * 绘制游戏状态的内容
     *
     * @param g
     */
    private void drawRun(Graphics g) {
        //绘制背景
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, FRAME_WIDTH, FRAME_HEIGHT);

        //绘制敌方坦克
        drawEnemy(g);

        //绘制我方坦克
        myTank.draw(g);

        //绘制爆炸效果
        drawExplodes(g);

        //子弹与坦克的碰撞检测
        bulletCollideTank();
    }

    /**
     * 绘制敌人
     *
     * @param g
     */
    private void drawEnemy(Graphics g) {
        for (int i = 0; i < enemies.size(); i++) {
            Tank enemy = enemies.get(i);
            enemy.draw(g);
        }
    }

    private void drawHelp(Graphics g) {
    }

    /**
     * 绘制菜单的方法
     *
     * @param g
     */
    private void drawMenu(Graphics g) {

        //绘制背景
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, FRAME_WIDTH, FRAME_HEIGHT);

        //菜单栏行间距和选项宽度
        final int DIS = 50;
        final int STR_WIDTH = 76;

        //菜单栏坐标
        int x = FRAME_WIDTH - STR_WIDTH >> 1;
        int y = FRAME_HEIGHT / 3;

        for (int i = 0; i < MENUS.length; i++) {
            if (i == menuIndex) {
                g.setColor(Color.RED);
            } else {
                g.setColor(Color.WHITE);
            }
            g.drawString(MENUS[i], x, y + DIS * i);
        }
    }

    //初始化窗口事件监听
    private void initEventListener() {

        //注册窗口监听事件
        addWindowListener(new WindowAdapter() {

            //点击关闭按钮时该方法会被自动调用
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);//结束虚拟机
            }
        });

        //注册窗口监听事件
        addKeyListener(new KeyAdapter() {

            //监听按键被按下的事件
            @Override
            public void keyPressed(KeyEvent e) {

                //捕获按下的键值
                int keyCode = e.getKeyCode();

                //不同的游戏状态给出不同的处理
                switch (gameState) {
                    case STATE_MENU:
                        keyPressedEventMenu(keyCode);
                        break;
                    case STATE_HELP:
                        keyPressedEventHelp(keyCode);
                        break;
                    case STATE_ABOUT:
                        keyPressedEventAbout(keyCode);
                        break;
                    case STATE_RUN:
                        keyPressedEventRun(keyCode);
                        break;
                    case STATE_OVER:
                        keyPressedEventOver(keyCode);
                        break;
                }
            }


            //监听按键被松开的事件
            @Override
            public void keyReleased(KeyEvent e) {
                //捕获按下的键值
                int keyCode = e.getKeyCode();
                if (gameState == STATE_RUN) {
                    keyReleasedEventRun(keyCode);
                }
            }
        });
    }

    //按键松开时,游戏处理的方法
    private void keyReleasedEventRun(int keyCode) {
        switch (keyCode) {
            case KeyEvent.VK_UP:
            case KeyEvent.VK_W:
            case KeyEvent.VK_DOWN:
            case KeyEvent.VK_S:
            case KeyEvent.VK_LEFT:
            case KeyEvent.VK_A:
            case KeyEvent.VK_RIGHT:
            case KeyEvent.VK_D:
                myTank.setState(Tank.STATE_STAND);
                break;
        }
    }

    private void keyPressedEventOver(int keyCode) {

    }

    private void keyPressedEventHelp(int keyCode) {

    }

    /**
     * 游戏进行中的按键处理
     *
     * @param keyCode
     */
    private void keyPressedEventRun(int keyCode) {
        switch (keyCode) {
            case KeyEvent.VK_UP:
            case KeyEvent.VK_W:
                myTank.setDir(Tank.DIR_UP);
                myTank.setState(Tank.STATE_MOVE);
                break;
            case KeyEvent.VK_DOWN:
            case KeyEvent.VK_S:
                myTank.setDir(Tank.DIR_DOWN);
                myTank.setState(Tank.STATE_MOVE);
                break;
            case KeyEvent.VK_LEFT:
            case KeyEvent.VK_A:
                myTank.setDir(Tank.DIR_LEFT);
                myTank.setState(Tank.STATE_MOVE);
                break;
            case KeyEvent.VK_RIGHT:
            case KeyEvent.VK_D:
                myTank.setDir(Tank.DIR_RIGHT);
                myTank.setState(Tank.STATE_MOVE);
                break;
            case KeyEvent.VK_SPACE:
                myTank.fire();
                break;
        }
    }

    private void keyPressedEventAbout(int keyCode) {

    }

    //菜单状态下的按键处理
    private void keyPressedEventMenu(int keyCode) {
        switch (keyCode) {
            case KeyEvent.VK_UP:
            case KeyEvent.VK_W:
                menuIndex--;
                if (menuIndex < 0) {
                    menuIndex = MENUS.length - 1;
                }
                break;
            case KeyEvent.VK_DOWN:
            case KeyEvent.VK_S:
                menuIndex++;
                if (menuIndex > MENUS.length - 1) {
                    menuIndex = 0;
                }
                break;
            case KeyEvent.VK_ENTER:
                //TODO
                newGame();
                break;
        }
    }

    private void newGame() {

        gameState = STATE_RUN;

        //创建坦克对象
        myTank = new MyTank(400, 200, Tank.DIR_DOWN);

        //生产敌方坦克的线程
        new Thread() {
            @Override
            public void run() {
                while (true) {
                    if (enemies.size() < ENEMY_MAX_COUNT) {
                        Tank enemy = EnemyTank.createEnemy();
                        enemies.add(enemy);
                    }
                    try {
                        Thread.sleep(ENEMY_BORN_INTERVAL);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }

    @Override
    public void run() {
        while (true) {
            repaint();
            try {
                Thread.sleep(REPAINT_INTERVAL);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 碰撞检测
     */
    private void bulletCollideTank() {
        //我方子弹与所有敌人的碰撞检测
        for (Tank enemy : enemies) {
            enemy.collideBullet(myTank.getBullets());
        }
        //敌坦克与我方子弹碰撞
        for (Tank enemy : enemies) {
            myTank.collideBullet(enemy.getBullets());
        }
    }

    /**
     * 绘制坦克效果
     *
     * @param g
     */
    private void drawExplodes(Graphics g) {
        //绘制敌方坦克
        for (Tank enemy : enemies) {
            enemy.drawExplodes(g);
        }
        //绘制我方坦克
        myTank.drawExplodes(g);
    }
}

utils包

该包下有工具类、爆炸对象池、子弹对象池、游戏常量对象

工具类MyUtil.class

/*
* 工具类
* */
public class MyUtil {
    private MyUtil() {
    }//构造方法私有化,将该类进行封装

    /**
     * 得到指定区间的随机数
     *
     * @param min
     * @param max
     * @return
     */
    public static final int getRandomNumber(int min, int max) {
        return (int) (Math.random() * (max - min) + min);
    }

    /**
     * 获取随机颜色
     *
     * @return
     */
    public static final Color getRandomColor() {
        int red = getRandomNumber(0, 256);
        int green = getRandomNumber(0, 256);
        int blue = getRandomNumber(0, 256);
        return new Color(red, green, blue);
    }

    /**
     * 获取图片对象
     *
     * @param image_path
     * @return
     */
    public static Image getImage(String image_path) {
        ImageIcon imageIcon = new ImageIcon(MyUtil.class.getClassLoader().getResource(image_path));
        return imageIcon.getImage();
    }

    /**
     * 求坦克和子弹是否碰撞
     *
     * @param rectX  坦克的坐标
     * @param rectY
     * @param radius 坦克的边长
     * @param pointX 子弹的坐标
     * @param pointY
     * @return
     */
    public static final boolean isCollide(int rectX, int rectY, int radius, int pointX, int pointY) {

        //坦克中心点和子弹的xy轴距离
        int disX = Math.abs(rectX - pointX);
        int disY = Math.abs(rectY - pointY);
        if (disX < radius && disY < radius) {
            return true;
        }
        return false;
    }

}

爆炸效果对象池

/**
  *爆炸效果对象池
  */
public class ExplodePool {
    //默认的爆炸效果数量
    public static final int DEFAULT_POOL_SIZE = 200;

    //最大爆炸效果容量
    public static final int POOL_MAX_SIZE = 300;

    //用于保存所有爆炸的对象池
    private static List pool = new ArrayList<>();

    //在类加载时将爆炸效果对象添加到容器中
    static {
        for (int i = 0; i < DEFAULT_POOL_SIZE; i++) {
            pool.add(new Explode());
        }
    }

    /**
     * 从爆炸效果对象池中取出一颗爆炸效果
     *
     * @return
     */
    public static Explode get() {
        Explode explode = null;

        //当对对象池中无爆炸效果时,new一个新的爆炸效果对象
        if (pool.size() == 0) {
            explode = new Explode();
        } else {
            explode = pool.remove(0);
        }
        return explode;
    }

    /**
     * 当爆炸效果被销毁时,归还爆炸效果
     *
     * @param
     */
    public static void theReturn(Explode explode) {
        if (pool.size() == POOL_MAX_SIZE) {
            return;
        }
        pool.add(explode);
    }
}

子弹对象池

/**
 * 子弹对象池
 */
public class BulletPool {
    //默认的子弹数量
    public static final int DEFAULT_POOL_SIZE = 200;

    //最大子弹容量
    public static final int POOL_MAX_SIZE = 300;

    //用于保存所有子弹的对象池
    private static List pool = new ArrayList<>();

    //在类加载时将子弹对象添加到容器中
    static {
        for (int i = 0; i < DEFAULT_POOL_SIZE; i++) {
            pool.add(new Bullet());
        }
    }

    /**
     * 从子弹对象池中取出一颗子弹
     *
     * @return
     */
    public static Bullet get() {
        Bullet bullet = null;

        //当对对象池中无子弹时,new一个新的子弹对象
        if (pool.size() == 0) {
            bullet = new Bullet();
        } else {
            bullet = pool.remove(0);
        }
        return bullet;
    }

    /**
     * 当子弹被销毁时,归还子弹
     *
     * @param bullet
     */
    public static void theReturn(Bullet bullet) {
        if (pool.size() == POOL_MAX_SIZE) {
            return;
        }
        pool.add(bullet);
    }
}

游戏常量类

/**
 * 游戏常量类
 */
public class Constant {

    /****************游戏窗口相关属性*********************/
    public static final String GAME_TITLE = "坦克大战";

    public static final int FRAME_WIDTH = 1000;
    public static final int FRAME_HEIGHT = 900;

    //获取系统屏幕的宽高
    public static final int SCREEN_W = Toolkit.getDefaultToolkit().getScreenSize().width;
    public static final int SCREEN_H = Toolkit.getDefaultToolkit().getScreenSize().height;

    //游戏窗口顶点坐标
    public static final int FRAME_X = SCREEN_W - FRAME_WIDTH >> 1;
    public static final int FRAME_Y = SCREEN_H - FRAME_HEIGHT >> 1;

    /****************游戏菜单相关属性********************************/

    public static final int STATE_MENU = 0;
    public static final int STATE_HELP = 1;
    public static final int STATE_ABOUT = 2;
    public static final int STATE_RUN = 3;
    public static final int STATE_OVER = 4;

    public static final String[] MENUS = {
            "开始游戏",
            "继续游戏",
            "游戏帮助",
            "游戏相关",
            "退出游戏"
    };

    public static final Font GAME_FONT = new Font("宋体", Font.BOLD, 24);

    //窗口刷新间隔
    public static final int REPAINT_INTERVAL = 30;

    //最大敌人数量
    public static final int ENEMY_MAX_COUNT = 10;

    //敌方坦克生产间隔
    public static final int ENEMY_BORN_INTERVAL = 5000;

    //敌方坦克状态切换的间隔
    public static final int ENEMY_AI_INTERVAL = 1000;

    //敌方坦克每帧发射子弹的概率
    public static final double ENEMY_AI_PERCENT = 0.10;
}
Size().width;
    public static final int SCREEN_H = Toolkit.getDefaultToolkit().getScreenSize().height;

    //游戏窗口顶点坐标
    public static final int FRAME_X = SCREEN_W - FRAME_WIDTH >> 1;
    public static final int FRAME_Y = SCREEN_H - FRAME_HEIGHT >> 1;

    /****************游戏菜单相关属性********************************/

    public static final int STATE_MENU = 0;
    public static final int STATE_HELP = 1;
    public static final int STATE_ABOUT = 2;
    public static final int STATE_RUN = 3;
    public static final int STATE_OVER = 4;

    public static final String[] MENUS = {
            "开始游戏",
            "继续游戏",
            "游戏帮助",
            "游戏相关",
            "退出游戏"
    };

    public static final Font GAME_FONT = new Font("宋体", Font.BOLD, 24);

    //窗口刷新间隔
    public static final int REPAINT_INTERVAL = 30;

    //最大敌人数量
    public static final int ENEMY_MAX_COUNT = 10;

    //敌方坦克生产间隔
    public static final int ENEMY_BORN_INTERVAL = 5000;

    //敌方坦克状态切换的间隔
    public static final int ENEMY_AI_INTERVAL = 1000;

    //敌方坦克每帧发射子弹的概率
    public static final double ENEMY_AI_PERCENT = 0.10;
}

你可能感兴趣的:(Java项目)