坦克大战2.0,3.0,4.0版本

1.坦克大战 0.3

在坦克大战游戏(0.2版)基础上添加如下功能:当玩家按一下j键,就发射一颗子弹.

1672660265805

编写Shot类

1672665042333

package com.yt.tankgame03;

/**
 * 射击子弹
 */
public class Shot implements Runnable{
    int x;//子弹的x坐标
    int y;//子弹的y坐标
    int direct;//子弹的方向
    int speed = 2;//子弹的速度
    boolean isLive = true;//子弹是否存活

    //构造器
    public Shot(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://表示方向向zuo
                    x -= speed;
                    break;

            }
            //测试下,输出x,y的坐标
            System.out.println("子弹 x=" + x + " y=" + y);
            //子弹出边界了
            if (!(x>= 0 && x <=1000 && y>=0 && y<=750)){
                isLive = false;
                break;
            }
        }

    }
}

在坦克中编写射击地方坦克的方法

1672665167793

1672665143463

//射击
public void shotEnemyTank(){
    //创建Shot对象,根据当前Hero对象的位置和方向来创建Shot对象
    switch (getDirect()){//得到坦克的方向
        case 0://向上
            shot = new Shot(getX()+20,getY(),0);
            break;
        case 1://向右
            shot = new Shot(getX()+60,getY()+20,1);
            break;
        case 2://向下
            shot = new Shot(getX()+20,getY()+60,2);
            break;
        case 3://向左
            shot = new Shot(getX(),getY()+20,3);
            break;
    }
    //启动射击的线程
    new Thread(shot).start();

绘制射击的子弹

1672665270885

将面板设置成线程

1672665311805

1672665330559

启动线程

1672665371693

2.坦克大战 0.4 版

  1. 让敌人的坦克也能够发射子弹(可以有多颗子弹)
  2. 当我方坦克击中敌人坦克时,敌人的坦克就消失,如果能做出爆炸效果更好
  3. 让敌人的坦克也可以自由随机的上下左右移动
  4. 控制我方的坦克和敌人的坦克在规定的范围移动分析->解决

让敌人的坦克也能够发射子弹(可以有多颗子弹)

  1. 在敌人坦克类,使用Vector保存多个Shot
  2. 当每创建一个敌人坦克对象,给该敌人坦克对象初始化一个Shot对象,同时启动Shot
  3. 在绘制敌人坦克时,需要遍历敌人坦克对象Vector,绘制所有的子弹,当子弹isLive == false时,就从Vector移除.

当我方坦克击中敌人坦克时,敌人的坦克就消失,如果能做出爆炸效果更好

1672708284085

1672710589458

1672710765063

编写炸弹类

1672712110073

1672712637370

1672712894280

1672713151203

1672713615102

坦克大战为什么第一个坦克不显示爆炸效果?增加休眠时间之后反而变好了呢?

1672716191612

让敌人的坦克也可以自由随机的上下左右移动

让敌人的坦克也可以自由随机的上下左右移动思路分析

  1. 因为要求敌人的坦克,可以自由移动,因此需要将敌人坦克当做线程使用
  2. 我们需要Enemy Tank implements Runnable
  3. 在run方法写上我们相应的业务代码
  4. 在创建敌人坦克对象时,启动线程

写多线程一定要考虑什么时候退出多线程

将敌人的坦克设置为多线程

1672717411549

启动线程

1672717558262

3.坦克大战0.5

  1. 我方坦克在发射的子弹消亡后,才能发射新的子弹.=>扩展(发多颗子弹怎么办,控制在我们的面板上,最多只有5颗)-》在课后完善..
  2. 让敌人坦克发射的子弹消亡后,可以再发射子弹.[口头说.]
  3. 当敌人的坦克击中我方坦克时,我方坦克消失,并出现爆炸效果. 思路:编写方法,判断敌人的坦克是否击中我的坦克
  4. 让敌人坦克可以最多发射3颗(在面板E),我们的坦克可以发射3颗.并且能够出现正常的爆炸效果即可.

我方坦克在发射的子弹消亡后,才能发射新的子弹.=>扩展(发多颗子弹怎么办)

  1. 在按下J键,我们判断当前hero对象的子弹,是否已经销毁
  2. 如果没有销毁,就不去触发shotEnemyTank
  3. 如果已经销毁,才去触发shotEnemyTank
  4. 如果要发射多颗子弹,就使用Vector保存
  5. 在绘制我方子弹时,需要遍历该Vector集合

1672732761333

我方坦克发射多颗子弹

1672733079617

1672733420698

1672733558494

控制只能发射5颗子弹

1672733838276

多颗子弹打多个坦克

1672734703463

1672734727896

让敌人坦克发射的子弹消亡后,可以再发射子弹

1672736216664

当敌人的坦克击中我方坦克时,我方坦克消失,并出现爆炸效果.

1672737230425

1672737297989

4.项目目录

1672737709194

Bomb类

package com.yt.tankgame04;

/**
 * 炸弹
 */
public class Bomb {
    int x,y;//炸弹的x,y坐标
    int life = 9;//炸弹的生命周期
    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;
        }
    }
}

EnemyTank类

package com.yt.tankgame04;

import java.util.Vector;

public class EnemyTank extends Tank implements Runnable{
    //在敌人坦克类,使用Vector保存多个Shot
    Vector<Shot> shots = new Vector<>();
    boolean isLive = true;
    public EnemyTank(int x, int y) {
        super(x, y);
    }

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

            //判断如果shots的size()==0,说明集合已经没有子弹了
            //创建一颗子弹,放入的集合中,并启动
            if (isLive && shots.size() < 3){
                Shot s = null;
                //判读坦克的方向,创建对应的子弹
                switch (getDirect()){
                    case 0:
                        s = new Shot(getX()+20,getY(),0);
                        break;
                    case 1:
                        s = new Shot(getX()+60,getY()+20,1);
                        break;
                    case 2://向下
                        s = new Shot(getX()+20,getY()+60,2);
                        break;
                    case 3://向左
                        s = new Shot(getX(),getY()+20,0);
                        break;
                }
                shots.add(s);
                //启动
                new Thread(s).start();
            }

            //根据坦克的方向来继续移动
            switch (getDirect()){
                case 0://向上
                    //让坦克保持一个方向,走30步
                    for (int i = 0; i < 30; i++) {
                        if (getY()>0) {
                            moveUp();
                        }
                        //休眠50ms,不休眠会到处乱窜
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 1://向右
                    //让坦克保持一个方向,走30步
                    for (int i = 0; i < 30; i++) {
                        if (getX()+60 < 1000){
                            moveRight();
                        }
                        //休眠50ms,不休眠会到处乱窜
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 2://向下
                    //让坦克保持一个方向,走30步
                    for (int i = 0; i < 30; i++) {
                        if (getY()+60 < 750) {
                            moveDown();
                        }
                        //休眠50ms,不休眠会到处乱窜
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                case 3://向左
                    //让坦克保持一个方向,走30步
                    for (int i = 0; i < 30; i++) {
                        if (getX() > 0) {
                            moveLeft();
                        }
                        //休眠50ms,不休眠会到处乱窜
                        try {
                            Thread.sleep(50);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
            }

            //然后随机的改变坦克方向,0-3的一个整数
            setDirect((int)(Math.random()*4));
            //一旦写并发程序,一定要考虑什么时候结束
            if (!isLive){
                break;//退出线程
            }
        }
    }
}

Hero类

package com.yt.tankgame04;

import java.util.Vector;

//创建自己的坦克
public class Hero extends Tank {
    //定义一个Shot对象,表示射击的行为(线程)
    Shot shot = null;
    //可以发射多颗子弹
    Vector<Shot> shots = new Vector<>();

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

    //射击
    public void shotEnemyTank(){

        //控制在面板上只能有5颗子弹
        if (shots.size() == 5){
            return;
        }

        //创建Shot对象,根据当前Hero对象的位置和方向来创建Shot对象
        switch (getDirect()){//得到坦克的方向
            case 0://向上
                shot = new Shot(getX()+20,getY(),0);
                break;
            case 1://向右
                shot = new Shot(getX()+60,getY()+20,1);
                break;
            case 2://向下
                shot = new Shot(getX()+20,getY()+60,2);
                break;
            case 3://向左
                shot = new Shot(getX(),getY()+20,3);
                break;
        }
        //把新创建的shot放入到shots中
        shots.add(shot);
        //启动射击的线程
        new Thread(shot).start();

    }
}

MyPanel类

package com.yt.tankgame04;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;

//坦克大战的绘图区域
//为了监听键盘事件,实现KeyListener
//为了让Panel不停的重绘子弹,需要将MyPanel实现Runable,当做一个线程使用
public class MyPanel extends JPanel implements KeyListener ,Runnable{
    //定义我的坦克
    Hero hero = null;
    //定义敌人的坦克,放入到Vector中,敌人的坦克不止一个
    Vector<EnemyTank> enemyTanks = new Vector<>();
    int enemyTankSize = 3;
    //定义一个Vector,用于存放炸弹
    //说明当子弹击中坦克时,就加入一个Bomb对象到bombs
    Vector<Bomb> bombs = new Vector<>();

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


    public MyPanel(){
        hero = new Hero(500,100);//初始化自己的坦克
        hero.setSpeed(5);
        //初始化敌人的坦克
        for (int i = 0; i < enemyTankSize; i++) {
            //创建敌人的坦克
            EnemyTank enemyTank = new EnemyTank((100 * (i + 1)), 0);
            //设置方向
            enemyTank.setDirect(2);
            //启动敌人坦克线程,让它动起来
            new Thread(enemyTank).start();
            //给该enemyTank加入一颗子弹
            Shot shot = new Shot(enemyTank.getX()+20,enemyTank.getY()+60,enemyTank.getDirect());
            //加入到enemyTank的Vector成员
            enemyTank.shots.add(shot);
            //启动shot对象
            new Thread(shot).start();
            //加入
            enemyTanks.add(enemyTank);
        }

        //初试化图片对象
        image1 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb1.png"));
        image2 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb2.png"));
        image3 = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bomb3.png"));
        //


    }

    @Override
    public void paint(Graphics g) {
        super.paint(g);
        g.fillRect(0,0,1000,750);//填充矩形,默认是黑色

        if (hero != null && hero.isLive) {
            //画出坦克-封装到方法
            drawTank(hero.getX(), hero.getY(), g, hero.getDirect(), 1);
        }

        //画出敌人的坦克,遍历Vector
        for (int i=0; i< enemyTanks.size(); i++){
            //从Vector中取出坦克
            EnemyTank enemyTank = enemyTanks.get(i);
            //判断当前坦克是否还存活
            if (enemyTank.isLive) {//当敌人坦克是存活的才画出该坦克
                drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirect(), 0);
                //画出enemyTank 的所有子弹
                for (int j = 0; j < enemyTank.shots.size(); j++) {
                    //取出子弹,准备绘制
                    Shot shot = enemyTank.shots.get(j);
                    //绘制
                    if (shot.isLive) {
                        g.draw3DRect(shot.x, shot.y, 2, 1, false);
                    } else {
                        //从Vector移除,否则一直画
                        enemyTank.shots.remove(shot);
                    }
                }
            }
            //当坦克被打爆之后,将其重从集合中移除
//            if (!enemyTank.isLive){
//                enemyTanks.remove(i);
//            }

        }

        //画出hero射击的子弹
        /*
        if (hero.shot != null && hero.shot.isLive == true){
//            System.out.println("子弹被绘制。。。");
//            g.fill3DRect(hero.shot.x,hero.shot.y,1,1,false);
            g.draw3DRect(hero.shot.x,hero.shot.y,1,2,false);
        }
         */
        //将hero的子弹集合shots遍历取出绘制
        for (int i = 0; i < hero.shots.size(); i++) {
            Shot shot = hero.shots.get(i);
            if (shot != null && shot.isLive == true){
                g.draw3DRect(shot.x,shot.y,1,2,false);
            } else {//如果该shot对象已经无效,就从shot集合中移除
                hero.shots.remove(shot);
            }
        }

        //如果bombs集合中有对象,就画出
        for (int i = 0; i < bombs.size(); i++) {
            //取出炸弹
            Bomb bomb = bombs.get(i);
            //根据当前这个bomb对象的life值去画出对应的图片
            if (bomb.life > 6){
                //为什么休眠一下第一个坦克才会显示爆炸效果呢?
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                g.drawImage(image1,bomb.x,bomb.y,60,60,this);
            } else if (bomb.life>3){
                g.drawImage(image2,bomb.x,bomb.y,60,60,this);
            } else {
                g.drawImage(image3,bomb.x,bomb.y,60,60,this);
            }
            //让炸弹的生命值减少
            bomb.lifeDown();
            //如果bomb life为0,就从bombs的集合中删除
            if (bomb.life == 0){
                bombs.remove(bomb);
            }
        }


    }

    //编写方法,画出坦克

    /**
     *
     * @param x 坦克的左上x坐标
     * @param y 坦克的左上y坐标
     * @param g 画笔
     * @param direction 方向 ,上下左右
     * @param type 我们的坦克,还是敌方坦克
     */
    public void drawTank(int x, int y,Graphics g, int direction,int type){
        //根据不同类型的坦克,设置不同的颜色
        switch (type){
            case 0://我们的坦克
                g.setColor(Color.cyan);
                break;
            case 1://敌方的坦克
                g.setColor(Color.yellow);
                break;
        }

        //根据坦克的方向来绘制对应形状的坦克
        //direction表示方向,0:向上  1向右 2向下 3向左
        switch (direction){
            case 0://0表示向上
                g.fill3DRect(x,y,10,60,false);//画出坦克左边的轮子
                g.fill3DRect(x+10,y+10,20,40,false);//画出坦克中间
                g.fill3DRect(x+30,y,10,60,false);//画出坦克右边的轮子
                g.drawOval(x+10,y+20,20,20);//画出坦克中间的盖子
                g.drawLine(x+20,y+30,x+20,y);//画出炮筒
                break;
            case 1://1表示向右
                g.fill3DRect(x,y,60,10,false);//画出坦克上边的轮子
                g.fill3DRect(x+10,y+10,40,20,false);//画出坦克中间
                g.fill3DRect(x,y+30,60,10,false);//画出坦克下边的轮子
                g.drawOval(x+20,y+10,20,20);//画出坦克中间的盖子
                g.drawLine(x+30,y+20,x+60,y+20);//画出炮筒
                break;
            case 2://2表示向下
                g.fill3DRect(x,y,10,60,false);//画出坦克左边的轮子
                g.fill3DRect(x+10,y+10,20,40,false);//画出坦克中间
                g.fill3DRect(x+30,y,10,60,false);//画出坦克右边的轮子
                g.drawOval(x+10,y+20,20,20);//画出坦克中间的盖子
                g.drawLine(x+20,y+30,x+20,y+60);//画出炮筒
                break;
            case 3://3表示向左
                g.fill3DRect(x,y,60,10,false);//画出坦克上边的轮子
                g.fill3DRect(x+10,y+10,40,20,false);//画出坦克中间
                g.fill3DRect(x,y+30,60,10,false);//画出坦克下边的轮子
                g.drawOval(x+20,y+10,20,20);//画出坦克中间的盖子
                g.drawLine(x+30,y+20,x,y+20);//画出炮筒
                break;
            default:
                System.out.println("暂时没有处理");
        }
    }

    //编写方法,判断敌人坦克是否击中我的坦克
    public void hitHero(){
        //遍历所有敌人的坦克
        for (int i = 0; i < enemyTanks.size(); i++) {
            //取出敌人坦克,
            EnemyTank enemyTank = enemyTanks.get(i);
            //遍历enemyTank 对象的所有子弹
            for (int j = 0; j < enemyTank.shots.size(); j++) {
                //取出一颗子弹
                Shot shot = enemyTank.shots.get(j);
                //判断shot 是否击中我的坦克
                if (hero.isLive && shot.isLive){
                    hitTank(shot,hero);
                }
            }
        }
    }

    //如果我们的坦克可以发射多个子弹
    //在判断我方子弹是否击中敌人坦克时,就需要把我们的子弹集合中所有的子弹都取出
    //和敌人的所有坦克进行判断
    public void hitEnemyTank(){
        //遍历我们的子弹
        for (int j = 0; j < hero.shots.size(); j++) {
            Shot shot = hero.shots.get(j);
            //判断是否击中了敌人坦克
            if (shot != null && hero.shot.isLive) {//当我的子弹还存活
                //遍历敌人所有的坦克
                for (int i = 0; i < enemyTanks.size(); i++) {
                    EnemyTank enemyTank = enemyTanks.get(i);
                    hitTank(hero.shot, enemyTank);
                }
            }
        }
    }


    //编写方法,判断我反子弹是否击中敌人坦克
    //什么时候判断击中坦克呢?run方法
    public void hitTank(Shot s, Tank Tank){
       //判断s击中坦克
       switch (Tank.getDirect()){
           case 0://坦克向上
           case 2://坦克向下
               if (s.x >Tank.getX() && s.x < Tank.getX()+40
                    && s.y > Tank.getY() && s.y < Tank.getY()+60){
                   s.isLive = false;
                   Tank.isLive = false;
                   //当我的子弹击中敌人坦克之后,将enemyTank重Vector中去掉
                   enemyTanks.remove(Tank);
                   //创建Bomb对象,加入到bombs集合中
                   Bomb bomb = new Bomb(Tank.getX(), Tank.getY());
                   bombs.add(bomb);
               }
               break;
           case 1://坦克向右
           case 3://坦克向左
               if (s.x > Tank.getX() && s.x<Tank.getX()+60 &&
                    s.y> Tank.getY() && s.y< Tank.getY()+40){
                   s.isLive = false;
                   Tank.isLive = false;
                   //当我的子弹击中敌人坦克之后,将enemyTank重Vector中去掉
                   enemyTanks.remove(Tank);
                   //创建Bomb对象,加入到bombs集合中
                   Bomb bomb = new Bomb(Tank.getX(), Tank.getY());
                   bombs.add(bomb);
               }
               break;

       }
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    //处理w s a d 键的按下情况
    @Override
    public void keyPressed(KeyEvent e) {
        if(e.getKeyCode() == KeyEvent.VK_W){
            //按下w键,改变坦克方向
            hero.setDirect(0);
            if (hero.getY() > 0) {
                hero.moveUp();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_D){
            hero.setDirect(1);
            if (hero.getX()+60 < 1000){
                hero.moveRight();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_S){
            hero.setDirect(2);
            if (hero.getY()+60 < 750) {
                hero.moveDown();
            }
        } else if (e.getKeyCode() == KeyEvent.VK_A){
            hero.setDirect(3);
            if (hero.getX() > 0){
                hero.moveLeft();
            }
        }
        //如果用户按下j,就表示发射子弹
        if (e.getKeyCode() == KeyEvent.VK_J){
//            System.out.println("用户按下了J,开始射击。");

            //判断hero子弹是否已经销毁,shot != null 不意味着 shot对象被销毁了
            //if (hero.shot == null || !hero.shot.isLive) {
            //    hero.shotEnemyTank();
            //}
            //发射多颗子弹
            hero.shotEnemyTank();
        }

        //务必要重绘,才能显示出变化
        this.repaint();
    }

    @Override
    public void keyReleased(KeyEvent e) {

    }

    @Override
    public void run() {//每隔100ms,重绘绘图区域,子弹就移动起来了
        while (true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //是否击中敌人坦克
            hitEnemyTank();
            //敌人坦克是否击中我们
            hitHero();
            this.repaint();
        }
    }
}

Shot类

package com.yt.tankgame04;

/**
 * 射击子弹
 */
public class Shot implements Runnable{
    int x;//子弹的x坐标
    int y;//子弹的y坐标
    int direct;//子弹的方向
    int speed = 2;//子弹的速度
    boolean isLive = true;//子弹是否存活

    //构造器
    public Shot(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://表示方向向zuo
                    x -= speed;
                    break;

            }
            //测试下,输出x,y的坐标
            System.out.println("子弹 x=" + x + " y=" + y);
            //子弹出边界了
            //当子弹碰到敌人坦克是也应该结束线程
            if (!(x>= 0 && x <=1000 && y>=0 && y<=750 && isLive)){
                isLive = false;
                break;
            }
        }

    }
}

Tank类

package com.yt.tankgame04;

public class Tank {
    private int x;//指定坦克的横坐标
    private int y;//指定坦克的纵坐标
    private int direct;//表示坦克方向 0上 1右 2下 3左
    private int speed = 1;
    boolean isLive = true;

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

    //上右下左移动方法
    public void moveUp(){
        y -= speed;
    }

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

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

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

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

    public int getDirect() {
        return direct;
    }

    public void setDirect(int direct) {
        this.direct = direct;
    }

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

YtTankGame04类

package com.yt.tankgame04;

import javax.swing.*;

public class YtTankGame04 extends JFrame {
    //定义MyPanel
    MyPanel mp = null;

    public static void main(String[] args) {
        new YtTankGame04();
    }

    public YtTankGame04(){
        mp = new MyPanel();
        //将mp放入到Thread,并启动
        Thread thread = new Thread(mp);
        thread.start();
        this.add(mp);//将面板(就是绘制游戏的区域)添加到窗口中
        this.addKeyListener(mp);//让JFrame监听mp的键盘事件
        this.setSize(1000,750);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

}
posted @ 2023-01-03 17:27  半路_出家ren  阅读(289)  评论(0编辑  收藏  举报
返回顶端