整体复盘----java实现简单版的坦克对战

一、预备知识

1、java绘图机制

2、多线程基础

二、实现过程

  • Tank类(抽象出坦克的共有属性比如横纵坐标、方向、子弹对象及子弹数量,构造器只有横纵坐标并提供相应的getter和setter方法;共有方法比如坦克移动、创建子弹)
    @SuppressWarnings("all")
    public class Tank
    {
        private int x; //坦克横坐标
        private int y; //坦克纵坐标
        private int direct;//表示方向(0-上, 1-右, 2-下, 3-左)
        private int type;
        Bullet bullet = null; //定义一个子弹对象
        private int bulletNum = 1; //坦克的子弹数量-->给子类去设置
    
        private boolean isTankAlive = true;
    
        public boolean isTankAlive()
        {
            return isTankAlive;
        }
    
        public void setTankAlive(boolean tankAlive)
        {
            isTankAlive = tankAlive;
        }
    
        public int getBulletNum()
        {
            return bulletNum;
        }
    
        public void setBulletNum(int bulletNum)
        {
            this.bulletNum = bulletNum;
        }
    
        //再给个速度
        private int tank_speed = 1;
    
        public int getTank_speed()
        {
            return tank_speed;
        }
    
        public void setTank_speed(int tank_speed)
        {
            this.tank_speed = tank_speed;
        }
    
        //在父类中添加根据坦克方向创建子弹的方法
        //子弹的初始坐标就是坦克炮筒的终点坐标
        public Bullet makeBullet (){
            switch (getDirect())
            {
                case 0 : //上
                    bullet = new Bullet(getX() + 20, getY(), 0);
                    break;
                case 1 : //右
                    bullet = new Bullet(getX() + 60, getY() + 20, 1);
                    break;
                case 2 : //下
                    bullet = new Bullet(getX() + 20, getY() + 60, 2);
                    break;
                case 3 : //左
                    bullet = new Bullet(getX(), getY() + 20, 3);
                    break;
            }
           return bullet;
        }
    
        //上右下左移动方法
        public void moveUp()
        {
            y -= tank_speed;
        }
        public void moveRight()
        {
            x += tank_speed;
        }public void moveDown()
        {
            y += tank_speed;
        }public void moveLeft()
        {
            x -= tank_speed;
        }
        public int getDirect()
        {
            return direct;
        }
        public void setDirect(int direct)
        {
            this.direct = direct;
        }
        public Tank(int x, int y)
        {
            this.x = x;
            this.y = y;
        }
        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;
        }
    }
    
  • Bullet类(把它做成一个线程,在其run方法中实现子弹移动及销毁) 
@SuppressWarnings("all")
//把子弹做成线程
class Bullet implements Runnable
{
    //给子弹设置它自身的坐标
    //并根据坦克的方向进行初始化
    private int x;//子弹横坐标
    private int y;//子弹纵坐标
    private int direct = 0; //子弹方向
    private int speed = 5; //子弹速度
    private boolean isBulletAlive = true; //子弹是否还存活

    public int getX()
    {
        return x;
    }


    public int getY()
    {
        return y;
    }

    public int getDirect()
    {
        return direct;
    }

    public int getSpeed()
    {
        return speed;
    }

    public void setBulletAlive(boolean live)
    {
        isBulletAlive = live;
    }

    public boolean getBulletAlive()
    {
        return isBulletAlive;
    }

    public Bullet(int x, int y, int direct)
    {
        this.x = x;
        this.y = y;
        this.direct = direct;
    }

    @Override
    public void run() //射击
    {
        while (true) {
            //休眠->以便看到效果
            try {
                Thread.sleep(50);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            //根据方向改变x, y坐标
            //代码简单就不用封装为方法了
            switch (direct)
            {
                case 0 : //上
                    y -= speed;
                    break;
                case 1 : //右
                    x += speed;
                    break;
                case 2 : //下
                    y += speed;
                    break;
                case 3 : //左
                    x -= speed;
                    break;
            }
            //测试, 输出x,y 坐标
            //System.out.println("子弹 x= " + x + " y= " + y);
            //如果
            //1.子弹移动到面板边界,则销毁(即退出循环->结束线程)
            //2.子弹击中敌方坦克, 也销毁
            //注意条件的判断方式->先列出正确的条件 再取反
            if (!(x >= 0 && x <= 1000 && y >= 0 && y <= 750 && isBulletAlive == true))
            {
                isBulletAlive = false;
                System.out.println("子弹已销毁..");
                break;
            }
        }
    }
}
  • Bomb类(实现爆炸效果) 
public class Bomb
{
    int x, y;//炸弹坐标
    int life = 18; //炸弹生命周期
    boolean isLive = true;

    public Bomb(int x, int y)
    {
        this.x = x;
        this.y = y;
    }
    //减少生命值
    public void lifeDown()
    {
        if(life > 0) {
            life--;
        }
        else {
            isLive = false;
        }
    }
}
  •  Hero类(我方坦克,继承Tank类)
@SuppressWarnings("all")
//自己的坦克
public class Hero extends Tank
{
    //定义一个Vector, 用来存放我方子弹
    Vector<Bullet> bullet_hero = new Vector<Bullet>();

    public Hero(int x, int y)
    {
        super(x, y);
    }

    //把创建一个Bullet对象和启动Bullet线程 封装到shotEnemy方法
    public void shotEnemy()
    {
        //如果我方坦克发射的子弹大于5颗, 就不再创建新的子弹了
        setBulletNum(5);
        if (bullet_hero.size() > getBulletNum()) {
            return;
        }
        //根据当前Hero对象的位置和方向, 创建一个Bullet对象
        //-->调用父类方法
        bullet = makeBullet();

        bullet_hero.add(bullet); //把我方子弹加入集合
        new Thread(bullet).start();//启动Bullet线程

    }
}

Enemy类(敌方坦克,同样继承Tank类, 做成线程)

@SuppressWarnings("all")
public class Enemy extends Tank implements Runnable
{
    //定义一个Vector, 用来存放敌方子弹
    private Vector<Bullet> bullet_enemy = new Vector<Bullet>();

    public Vector<Bullet> getBullet_enemy()
    {
        return bullet_enemy;
    }

    public Enemy(int x, int y)
    {
        super(x, y);
    }


    @Override
    public void run()
    {
        while (isTankAlive()) {

            setBulletNum(2);//如果敌方坦克的子弹为2颗
            if(bullet_enemy.size() <= getBulletNum()){

                //判断敌方坦克的方向来创建子弹对象-->调用父类方法
                bullet = makeBullet();

                bullet_enemy.add(bullet); //把敌方子弹加入集合
                new Thread(bullet).start();//启动该Bullet线程
            }

            //生成一个1~3的随机整数(即不往上打),表示敌方坦克的方向
            int direct_random = (int) (1 + Math.random() * 3);
            System.out.println("随机方向:" + direct_random);
            System.out.println("敌方坐标(" + getX() + "," + getY() + ")");
            setDirect(direct_random);
            System.out.println("实际方向: " + getDirect());

            //设置敌方坦克的随机速度
            int speed_random = (int)(Math.random() * 3 + 1);
            setTank_speed(speed_random);

            switch (getDirect())
            {
                case 0 : //上
                    //让坦克保持一个方向走30步
                    for (int i = 0; i < 30; i++) {
                        if(getY() > 0) { //控制敌方坦克移动范围
                            moveUp();
                        }
                        try {
                            Thread.sleep(50);
                        }
                        catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 1 : //右
                    for (int i = 0; i < 30; i++) {
                        if(getX() + 60 < 1000) { //控制敌方坦克移动范围
                            moveRight();
                        }
                        try {
                            Thread.sleep(50);
                        }
                        catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 2 : //下
                    for (int i = 0; i < 30; i++) {
                        if(getY() + 60 < 750) { //控制敌方坦克移动范围
                            moveDown();
                        }
                        try {
                            Thread.sleep(50);
                        }
                        catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 3 : //左
                    for (int i = 0; i < 30; i++) {
                        if(getX() > 0) { //控制敌方坦克移动范围
                            moveLeft();
                        }
                        try {
                            Thread.sleep(50);
                        }
                        catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }
        }
    }
}
  • MyPanel类(用于绘制坦克、子弹、爆炸,做成线程-->这样才能看到子弹行进)
@SuppressWarnings("all")
//坦克大战的绘图区域
//1.为了监听键盘事件,实现KeyListener
//2.为了看到子弹前进的效果->MyPanel要一直repaint->把MyPanel也做成一个线程
public class MyPanel extends JPanel implements KeyListener,Runnable
{
    //定义我的坦克
    Hero hero = null;
    //定义敌方坦克,放入到vector集合
    Vector<Enemy> enemyTanks = new Vector<Enemy>();
   int enemyTankNum = 3;

   //定义一个Vector, 用于存放炸弹
    //当敌人击中坦克时, 就加入一个Bomb对象到bombs
    Vector<Bomb> bombs = new Vector<Bomb>();

    //定义三张炸弹图片, 用于`显示爆炸效果
    Image image1 = null;
    Image image2 = null;
    Image image3 = null;

    public MyPanel()
    {
        //初始化自己的坦克
        hero = new Hero(100, 200);
        hero.setTank_speed(10);//设置我方坦克速度

        //初始化敌方的坦克
        for(int i = 0; i < enemyTankNum; i++)
        {
            Enemy enemy = new Enemy(200 * (i + 1), 0);
            enemy.setDirect(2);//设置敌方坦克方向朝下

            //在此处创建敌方坦克的一颗子弹, 并加入到bullet_enemy 集合
            Bullet b = new Bullet(enemy.getX() + 20, enemy.getY() + 60, enemy.getDirect());
            enemy.getBullet_enemy().add(b);//加入到bullet_enemy 集合
            new Thread(b).start(); //启动敌方坦克子弹

            enemyTanks.add(enemy);
            new Thread(enemy).start(); //启动敌方坦克
        }

        //初始化图片对象
        image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_1.gif"));
        image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_2.gif"));
        image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb_3.gif"));
    }



    @Override
    public void paint(Graphics g)
    {
        super.paint(g);
        //填充矩形,默认是黑色
        g.fillRect(0, 0, 1000, 750);
        //画出我方坦克-->封装到方法
        if(hero.isTankAlive()) {
            drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1);
        }

        
        //遍历我方子弹集合, 画出我方坦克发射的子弹
        for(int i = 0; i < hero.bullet_hero.size(); i++){
            Bullet bullet = hero.bullet_hero.get(i);
            if (bullet != null && bullet.getBulletAlive() == true) {
                g.setColor(Color.yellow);
                g.fillOval(bullet.getX(), bullet.getY(), 5, 5);
            }
            else { //当我方子弹不存活时就将它移除
                hero.bullet_hero.remove(bullet);
            }
        }

        //如果bombs集合中有对象, 就画出
        for(int i = 0; i < bombs.size(); i++)
        {
            Bomb bomb = bombs.get(i); //取出炸弹对象

            //根据当前bomb的life值去画出对应的图片
            if (bomb.life > 12) {
                g.drawImage(image1, bomb.x, bomb.y, 60, 60, this);
            }
            else if (bomb.life > 6) {
                g.drawImage(image2, bomb.x, bomb.y, 60, 60, this);
            }
            else{
                g.drawImage(image3, bomb.x, bomb.y, 60, 60, this);
            }

            //让炸弹的生命值减少
            bomb.lifeDown(); //配合出现动态效果
            //如果life为0, 就从bombs集合中删除
            if (bomb.life == 0) {
                bombs.remove(bomb);
            }
        }

        //画出敌方坦克, 遍历Vecror
        for(int i = 0; i < enemyTanks.size(); i++)
        {
            //取出坦克
            Enemy enemy = enemyTanks.get(i);
            //调用drawTank方法
            //如果当前敌方坦克还存活,才去画
            if (enemy.isTankAlive() == true) {

                drawTank(enemy.getX(), enemy.getY(), g, enemy.getDirect(), 0);

                //画出敌方坦克的子弹
                for (int j = 0; j < enemy.getBullet_enemy().size(); j++) {
                    Bullet b = enemy.getBullet_enemy().get(j);//取出敌方子弹
                    if (b.getBulletAlive() == true) {
                        g.setColor(Color.cyan);
                        g.fillOval(b.getX(), b.getY(), 5, 5);
                    }
                    else {
                        enemy.getBullet_enemy().remove(b);//移除敌方子弹
                    }
                }
            }
            else{
                //当敌方坦克不存活了, 就移除它
                enemyTanks.remove(enemy);
            }
        }


    }
    //画出坦克-->封装到方法
    /**
     *
     * @param x 坦克的左上角横坐标
     * @param y 坦克的左上角纵坐标
     * @param g 画笔
     * @param direct 坦克的方向
     * @param type 坦克的类型
     */
    public void drawTank(int x, int y, Graphics g, int direct, int type)
    {
        //根据不同类的坦克设置颜色
        switch (type)
        {
            case 0 : //敌人的坦克
                g.setColor(Color.cyan);//青色
                break;
            case 1 : //我们的坦克
                g.setColor(Color.yellow);
                break;
        }
        //根据坦克的方向来绘制对应形状的坦克
        //direct 表示方向(0-上, 1-右, 2-下, 3-左
        switch (direct)
        {
            case 0 : //向上的坦克
                g.fill3DRect(x, y, 10, 60, false);//左履带
                g.fill3DRect(x + 30, y, 10, 60, false);//右履带
                g.fill3DRect(x + 10, y + 10, 20, 40, false);//机身
                g.fillOval(x + 10, y + 20, 20, 20);//圆盖子
                g.drawLine(x + 20, y + 30, x + 20, y);//炮筒
                break;
            case 1 : //向右的坦克
                g.fill3DRect(x, y, 60, 10, false);//左履带
                g.fill3DRect(x, y + 30, 60, 10, false);//右履带
                g.fill3DRect(x + 10, y + 10, 40, 20, false);//机身
                g.fillOval(x + 20, y + 10, 20, 20);//圆盖子
                g.drawLine(x + 30, y + 20, x + 60, y+20);//炮筒
                break;
            case 2 : //向下的坦克
                g.fill3DRect(x, y, 10, 60, false);//左履带
                g.fill3DRect(x + 30, y, 10, 60, false);//右履带
                g.fill3DRect(x + 10, y + 10, 20, 40, false);//机身
                g.fillOval(x + 10, y + 20, 20, 20);//圆盖子
                g.drawLine(x + 20, y + 30, x + 20, y+60);//炮筒
                break;
            case 3 : //向左的坦克
                g.fill3DRect(x, y, 60, 10, false);//左履带
                g.fill3DRect(x, y + 30, 60, 10, false);//右履带
                g.fill3DRect(x + 10, y + 10, 40, 20, false);//机身
                g.fillOval(x + 20, y + 10, 20, 20);//圆盖子
                g.drawLine(x + 30, y + 20, x, y+20);//炮筒
                break;
            default:
                System.out.println("其他情况暂时没有...");
        }
    }

    @Override
    public void keyTyped(KeyEvent e)
    {

    }


    @Override
    public void keyPressed(KeyEvent e)
    {
        //处理wdsa键按下的情况
        if(e.getKeyCode() == KeyEvent.VK_W)//按下w键
        {
            //向上
            hero.setDirect(0);
            if(hero.getY() > 0) { //控制我方坦克移动范围
                hero.moveUp();
            }
        }
        else if(e.getKeyCode() == KeyEvent.VK_D)//按下d键
        {
            //向右
            hero.setDirect(1);
            if(hero.getX() + 60 < 1000) {//控制我方坦克移动范围
                hero.moveRight();
            }
        }
        else if(e.getKeyCode() == KeyEvent.VK_S)//按下s键
        {
            //向下
            hero.setDirect(2);
            if(hero.getY() + 60 < 750) {//控制我方坦克移动范围
                hero.moveDown();
            }
        }
        else if(e.getKeyCode() == KeyEvent.VK_A)//按下a键
        {
            //向左
            hero.setDirect(3);
            if(hero.getX() > 0) {  //控制我方坦克移动范围
                hero.moveLeft();
            }
        }

        //如果用户按下J键, 发射子弹
        if(e.getKeyCode() == KeyEvent.VK_J)
        {
            System.out.println("用户按下J键, 发射子弹...");
            hero.shotEnemy();

        }

        this.repaint();//面板重绘
    }

    @Override
    public void keyReleased(KeyEvent e)
    {

    }

    //敌方坦克被击中
    //什么时候判断坦克是否被击中呢?? -->面板重绘时
    //alt + shift + R -->批量更改变量名
    public void hitted(Bullet b, Tank t)
    {

        switch (t.getDirect()) {
                case 0: //上
                case 2: //下
                    if (b.getX() >= t.getX() && b.getX() <= t.getX() + 40
                            && b.getY() >= t.getY() && b.getY() <= t.getY() +60)
                    {
                        b.setBulletAlive(false);
                        t.setTankAlive(false);

                        //创建一个Bomb对象, 加入到bombs集合中
                        Bomb bomb = new Bomb(t.getX(), t.getY());
                        bombs.add(bomb);
                    }
                    break;
                case 1: //右
                    if (b.getX() >= t.getX() && b.getX() <= t.getX() + 60
                            && b.getY() >= t.getY() && b.getY() <= t.getY() + 40)
                    {
                        b.setBulletAlive(false);
                        t.setTankAlive(false);
                        //创建一个Bomb对象, 加入到bombs集合中
                        Bomb bomb = new Bomb(t.getX(), t.getY());
                        bombs.add(bomb);
                    }
                case 3: //左
                    break;
        }
    }


    //判断敌方坦克是否被击中
    public void hitEnemy(){
        //如果我方发射的子弹还存活->不为空且没有碰到边界
        //注意: 这里如果用 逻辑与-> &  就会报空指针异常

        //遍历我方子弹集合 bullet_hero,
        //并调用hitted方法,判断敌方坦克是否被击中
        for (int j = 0; j < hero.bullet_hero.size(); j++) {
            Bullet bullet = hero.bullet_hero.get(j);
            if (bullet != null && bullet.getBulletAlive()) {
                //遍历敌方坦克
                for (int i = 0; i < enemyTanks.size(); i++) {
                    Enemy e = enemyTanks.get(i);
                    //调用hitted方法,判断坦克是否被击中
                    hitted(bullet, e);

                }
            }
        }
    }

    //判断我方坦克是否被击中
    public void hitHero(){
        //遍历敌方子弹集合 bullet_enemy,
        //并调用hitted方法,判断我方坦克是否被击中
        for (int i = 0; i < enemyTanks.size(); i++) {
            Enemy enemy = enemyTanks.get(i);
            for(int j = 0; j < enemy.getBullet_enemy().size(); j++){
                Bullet bullet = enemy.getBullet_enemy().get(j);
                if (bullet != null && bullet.getBulletAlive()) {
                    hitted(bullet, hero);
                }
            }
        }
    }

    //使MyPanel不断重绘->这样才能显示子弹的行进
    @Override
    public void run()
    {
        while (true) {
            try {
                Thread.sleep(100);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }

            //判断坦克是否被击中
            hitEnemy();
            hitHero();

            this.repaint();
        }
    }
}
  • TankGame类(相当于一个窗口,在此主方法启动MyPanel线程)
public class TankGame04 extends JFrame
{
    //定义MyPanel
    MyPanel mp = null;
    public static void main(String[] args)
    {
        TankGame04 tankGame01 = new TankGame04();
    }
    //构造器
    public TankGame04()
    {
        mp = new MyPanel();
        this.add(mp);
        this.setSize(1050, 900);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
        this.addKeyListener(mp);//监听面板发生的键盘事件

        //启功MyPanel mp 线程
        Thread thread_mp = new Thread(mp);
        thread_mp.start();
    }
}

 

上一篇:unity


下一篇:六大设计模式原则