Java-坦克攻击(多线程实现子弹)

Java-坦克攻击(多线程实现子弹)

实现过程

  • 定义子弹类(Bullet)
/**
 * 子弹类
 * @author: SEA
 * @date: 2023/3/18
 */
public class Bullet {
    //子弹坐标和发射方向
    int x;
    int y;
    int direction;

    int speed = 5;//子弹初始速度为 5
    public Bullet(int x, int y, int direction) {
        this.x = x;
        this.y = y;
        this.direction = direction;
    }

    public int getSpeed() {
        return speed;
    }

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

  • 普通坦克类(Tank)
    • 新增属性:
      • (1)子弹对象Bullet;
    • 新增方法:
      • (1)setBullet() 和 getBullet();
      • (2)坦克攻击方法attack(),计算坦克发射出的子弹初始坐标;
/**
 * 坦克
 * @author: SEA
 * @date: 2023/3/2
 */
public class Tank{
    private int x;//坦克的横坐标
    private int y;//坦克的纵坐标

    private int direction;//坦克的朝向:0 => 向上 ,1 => 向下 ,2 => 向左 , 3 => 向右

    private int speed = 5;//坦克速度

    private Bullet bullet;//坦克子弹

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

    public void moveUp(){
        this.y -= this.speed;
    }

    public void moveDown(){
        this.y += this.speed;
    }

    public void moveRight(){
        this.x += this.speed;
    }

    public void moveLeft(){
        this.x -= this.speed;
    }

    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 getDirection() {
        return direction;
    }

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

    public int getSpeed() {
        return speed;
    }

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

    public Bullet getBullet() {
        return bullet;
    }

    public void setBullet(Bullet bullet) {
        this.bullet = bullet;
    }

    public void attack(){//坦克攻击
        int bullet_x = -10, bullet_y = -10;
        switch (this.getDirection()){//根据当前坦克的位置和方向来计算子弹的发射方向
            case 0:
                bullet_x = this.getX()+20;
                bullet_y = this.getY()-6;
                break;
            case 1:
                bullet_x = this.getX()+20;
                bullet_y = this.getY()+64;
                break;
            case 2:
                bullet_x = this.getX()-6;
                bullet_y = this.getY()+20;
                break;
            case 3:
                bullet_x = this.getX()+64;
                bullet_y = this.getY()+20;
                break;
            default:
                break;
        }
        this.bullet = new Bullet(bullet_x, bullet_y, this.direction);
        TankAttackThread tankAttackThread = new TankAttackThread(bullet);
        Thread thread = new Thread(tankAttackThread);
        thread.start();
    }
}
  • 子弹发射的实现:

    • 新建线程类TankAttackThread,用来实现坦克发射子弹进行攻击;(子弹移动
    • 为了能够同时存在多颗子弹,需要使用多线程实现;
    /**
     * 子弹发射(多线程)
     * @author: SEA
     * @date: 2023/3/18
     */
    public class TankAttackThread implements Runnable{
        Bullet bullet;
    
        public TankAttackThread(Bullet bullet) {
            this.bullet = bullet;
        }
    
        @Override
        public void run() {
            while(true){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                switch (this.bullet.direction){
                    case 0:
                        this.bullet.y -= this.bullet.speed;
                        break;
                    case 1:
                        this.bullet.y += this.bullet.speed;
                        break;
                    case 2:
                        this.bullet.x -= this.bullet.speed;
                        break;
                    case 3:
                        this.bullet.x += this.bullet.speed;
                        break;
                }
                System.out.println(this.bullet.x+"==asa=" + this.bullet.y);
    
                if(this.bullet.x < -3 || this.bullet.y < -3 || this.bullet.x > 1003 || this.bullet.y > 803  ){
                    break;
                }
            }
    
        }
    }
    
  • 根据子弹的坐标和子弹的方向在GamePanel上绘制出子弹:

    • 绘制必须在paint()方法中调用,才能在GamePanel上绘制出来;
    • 考虑子弹的移动,需要不断地重绘面板,因此需要:
      • 1.让GamePanel实现Runnable接口成为线程类,并且实现Runnable接口中的run()方法,使得每间隔100ms重新绘制GamePanel;
      • 2.在GameFrame类中启动GamePanel线程,让子弹动起来;
/**
 * 坦克大战绘图区域
 * @author: SEA
 * @date: 2023/3/2
 */
class GamePanel extends JPanel implements KeyListener, Runnable{//画板

......

    //绘制    
    @Override
    public void paint(Graphics g) {
        super.paint(g);

        //游戏窗口背景
        g.setColor(Color.BLACK);
        g.fillRect(0, 0, 1000, 800);

        //标题
        g.setFont(new Font("楷体", Font.BOLD, 32));
        g.setColor(Color.RED);
        g.drawString("TankGame V0.2 by SEA-365", 60, 40);

        //画出坦克-封装成方法
        drawTank(hero.getX(), hero.getY(), g, hero.getDirection(), 0);

        //绘制玩家坦克的子弹
        if(hero.getBullet()!=null)
            drawBullet(hero.getBullet().x, hero.getBullet().y, g, hero.getBullet().direction, 0);

        for (EnemyTank enemyTank : enemyTanks) {
            drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 1);
        }
    }
    
    ......
        
   	//绘制子弹具体实现
    public void drawBullet(int x, int y, Graphics g, int direction, int type){
        //不同坦克类型,子弹颜色不同
        switch (type){
            case 0://玩家坦克
                g.setColor(Color.YELLOW);
                break;
            case 1://敌方坦克
                g.setColor(Color.CYAN);
                break;
        }
       
        g.fill3DRect(x, y, 6, 6, false);//绘制矩形子弹
      
    }

    ......
    //在
    @Override
    public void keyPressed(KeyEvent e) {
        /**
         * 监听玩家坦克移动
         * 方向键  /  wsad /  WSAD  ==> 上下左右
         */
        ......
    	/**
         * 监听玩家坦克攻击
         */
        if(e.getKeyCode() == 'j' || e.getKeyCode() == 'J'){
            this.hero.attack();
        }
    }
        
    ......

    //run方法,实现每间隔100ms重新绘制GamePanel
    @Override
    public void run() {
        while(true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            this.repaint();
        }

    }
}



//最后,在GameFrame中启动GamePanel线程

实现效果

在这里插入图片描述

存在的问题

  • 子弹射击时存在问题:
    • 1.玩家不能连续发射多颗子弹,发射新的子弹,旧的子弹就会消失;
      • 根据输出,旧子弹仅仅是没有绘制上panel,但是坐标依然在变;