ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2021-09-20 09:58:02  阅读:220  来源: 互联网

标签:enemy java bullet int public ---- hero 坦克 复盘


一、预备知识

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();
    }
}

 

标签:enemy,java,bullet,int,public,----,hero,坦克,复盘
来源: https://blog.csdn.net/qq_46076656/article/details/120385287

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有