Tank类

package com.learn.exercise.tanke;

import com.learn.exercise.tanke.Direction;

import java.awt.*;
import java.util.ArrayList;

/**
 * 坦克类
 * 要记得继承的父类是抽象的所以继承类也一定是抽象的
 */
public abstract class Tank extends GameObject {
    private boolean left;
    private boolean right;
    private boolean up;
    private boolean down;
    //尺寸
    public int width = 40;
    public int height = 50;
    //速度
    private int speed = 3;
    //方向;设置坦克的初始方向向上;
    public com.learn.exercise.tanke.Direction direction = com.learn.exercise.tanke.Direction.UP;
    //玩家生命元素
    public boolean alive = false;
    //定义不同方向的图片(上下左右方向)
    private String upImg;
    private String leftImg;
    private String rightImg;
    private String downImg;
    //攻击冷却状态
    private boolean attackCoolDown = true;
    //攻击冷却时间毫秒间隔100ms
    private int attackCoolDownTime = 100;
    //构造函数;里面的函数有继承父类的有当前这个类的
    public Tank(String img,int x,int y,Panel panel,
                String upImg,String leftImg,String rightImg,String downImg) {
        //调用父类的初始化方法,其实就是调用父类中的public xxx()方法
        super(img,x,y,panel);
        //this调用当前属性:
        /* 其主要作用是当需要给类中的数据进行初始化时,
        可以通过this来进行赋值
        */
        this.upImg = upImg;
        this.leftImg = leftImg;
        this.rightImg = rightImg;
        this.downImg = downImg;
    }

    //坦克图片左右移动的值
    private void setImg(String img){
        this.img = Toolkit.getDefaultToolkit().getImage(img);
    }
    public void leftward(){
        direction = Direction.LEFT;
        setImg(leftImg);
        //给坦克方向添加条件:括号里是坦克下一步的坐标不是当前坐标
        if (!moveToBorder(x-speed,y) && !hitWall(x-speed,y)){//!moveToBorder(x-speed,y)&&
            this.x -= speed;
        }
    }
    public void rightward(){
        direction = Direction.RIGHT;
        setImg(rightImg);
        if (!moveToBorder(x+speed,y) && !hitWall(x+speed,y)){//!moveToBorder(x+speed,y)&&
            this.x += speed;
        }

    }
    public void upward(){
        direction = Direction.UP;
        setImg(upImg);
        if (!moveToBorder(x,y-speed) && !hitWall(x,y-speed)){//!moveToBorder(x,y-speed)&&
            this.y -= speed;
        }

    }
    public void downward(){
        direction = Direction.DOWN;
        setImg(downImg);
        if (!moveToBorder(x,y+speed) && !hitWall(x,y+speed)){//!moveToBorder(x,y+speed)&&
            this.y += speed;
        }

    }

    //初始化子弹
    public void attack(){
        if (attackCoolDown){
            Point p = this.getHeadPoint();
            Bullet bullet = new Bullet("E:\\Program Files\\GYJ\\IntelliJ IDEA 2020.1.2\\classOne\\src\\com\\learn\\exercise\\tanke\\img\\enemymissile.gif",
                    p.x, p.y, this.panel, direction);
            //初始化的新子弹添加到列表里
            this.panel.bulletList.add(bullet);
            //子弹线程开始
            new AttackCD().start();
        }

    }
    //添加一个新线程
    class AttackCD extends Thread{
        public void run(){
            //将攻击功能设置为冷却状态
            attackCoolDown = false;
            //休眠1秒
            try {
                Thread.sleep(attackCoolDownTime);
            }catch (Exception e){
                e.printStackTrace();
            }
            //将攻击功能解除冷却状态
            attackCoolDown = true;
            //线程终止
            this.stop();
        }
    }



    //子弹的方向与坦克方向有关
    public Point getHeadPoint(){
        switch (direction){
            case LEFT:
                return new Point(x,y = height/2);
            case RIGHT:
                return new Point(x+width,y+height/2);
            case UP:
                return new Point(x+width/2,y);
            case DOWN:
                return new Point(x+width/2,y+height);
            default:
                return null;
        }
    }
    /**坦克与墙壁碰撞检测:坦克坐标不变出现遇到墙不能动的状态*/
    public boolean hitWall(int x,int y){
        //围墙列表
        ArrayList<Wall>walls = this.panel.wallList;
        //下一步矩形
        Rectangle next = new Rectangle(x,y,width,height);
        //遍历列表
        for (Wall wall:walls){
            //让子弹与每一个围墙进行检测
            if (next.intersects(wall.gerRec())){
                //发生碰撞返回ture
                return true;
            }
        }
        return false;
    }
    //坦克与边界碰撞
    public boolean moveToBorder(int x,int y){
        if (x<0){
            return true;
        }else if (x+width>this.panel.getWidth()){
            return true;
        }else if (y<0){
            return true;
        }else if (y+height>this.panel.getHeight()){
            return true;
        }
        return false;
    }

    //方法重写
    @Override
    public abstract void paintSelf(Graphics g);
    //检测方法(检测坦克和子弹有没有发生碰撞;返回坦克和子弹的矩形,判断这两个矩形是否相交)
    @Override
    public abstract Rectangle gerRec();
}





/*@Override是伪代码,表示重写(当然不写也可以),不过写上有如下好处:
1、可以当注释用,方便阅读;
2、编译器可以给你验证@Override下面的方法名是否是你父类中所有的,如果没有则报错。
比如你如果没写@Override而你下面的方法名又写错了,这时你的编译器是可以通过的
(它以为这个方法是你的子类中自己增加的方法)。
*/
/*1、super.变量/对象名;
使用这种方法可以直接访问父类中的变量或对象,进行修改赋值等操作

2、super.方法名();
直接访问并调用父类中的方法

3、super();常用
调用父类的初始化方法,其实就是调用父类中的public xxx()方法,通常第三种指代的是super()的省略写法,系统会默认添加此句。
特殊情况:如果父类没有无参的构造函数,所以子类需要在自己的构造函数中显示的调用父类的构造函数,即不能使用系统默认的“super()”,而需要显性写出super(xxx)
*/

/*//没有优化前的初始化子弹
    public void attack(){
        Point p = this.getHeadPoint();
        Bullet bullet = new Bullet("E:\\Program Files\\GYJ\\IntelliJ IDEA 2020.1.2\\classOne\\src\\com\\learn\\exercise\\tanke\\img\\enemymissile.gif",
                p.x, p.y, this.panel, direction);
        //初始化的新子弹添加到列表里
        this.panel.bulletList.add(bullet);
    }*/

Bot类

package com.learn.exercise.tanke;

import java.awt.*;
import java.util.Random;

/**
 * 敌方坦克
 */
public class Bot extends Tank{
    //移动的时间
    int moveTime = 0;
    public Bot(String img, int x, int y, Panel panel, String upImg, String leftImg, String rightImg, String downImg) {
        super(img, x, y, panel, upImg, leftImg, rightImg, downImg);
    }
    //敌方坦克随机移动方法
    public Direction getRandmoDirection(){
        Random random = new Random();
        int rnum = random.nextInt(4);
        switch (rnum){
            case 0:
                return Direction.LEFT;
            case 1:
                return Direction.RIGHT;
            case 2:
                return Direction.UP;
            case 3:
                return Direction.DOWN;
            default:
                return null;
        }
    }

    //定义一个go方法
    public void go(){
        //射击方法
        attack();
        if (moveTime>=20){
            //生成一个随机的方向
            direction = getRandmoDirection();
            moveTime = 0;
        }else {
            moveTime++;
        }
        switch (direction){
            case LEFT:
                leftward();
                break;
            case RIGHT:
                rightward();
                break;
            case UP:
                upward();
                break;
            case DOWN:
                downward();
                break;
        }
    }
    //新建一个方法:敌方坦克的随机子弹
    public void attack(){
        Point p =getHeadPoint();
        Random random = new Random();
        int rnum = random.nextInt(100);
        if (rnum<4){
            this.panel.bulletList.add(new EnemyBullet("E:\\Program Files\\GYJ\\IntelliJ IDEA 2020.1.2\\classOne\\src\\com\\learn\\exercise\\tanke\\img\\ybuttet.png",
                    p.x, p.y, this.panel, direction));
        }
    }


    @Override
    public void paintSelf(Graphics g) {
        g.drawImage(img,x,y,null);
        go();
    }

    @Override
    public Rectangle gerRec() {
        return new Rectangle(x,y,width,height);
    }
}

Blast类

 

package com.learn.exercise.tanke;

import java.awt.*;

/**
 * 子弹爆炸类
 */
public class Blast extends GameObject{
    //爆炸图集
    static Image[] imgs = new Image[8];
    //定义变量表示切换的图片
    int explodeCount = 0;
    //用静态方法往图集中添加图片
    static {
        for (int i = 0;i<8;i++){
            imgs[i] = Toolkit.getDefaultToolkit().getImage("E:\\Program Files\\GYJ\\IntelliJ IDEA 2020.1.2\\classOne\\src\\com\\learn\\exercise\\tanke\\img\\blast"+(i+1)+".gif");
        }
    }


    /**
     * @param img
     * @param x
     * @param y
     * @param panel
     */
    public Blast(String img, int x, int y, Panel panel) {
        super(img, x, y, panel);
    }

    @Override
    public void paintSelf(Graphics g) {
        //连续播放图集图片
        if (explodeCount<8){
            g.drawImage(imgs[explodeCount],x,y,null);
            explodeCount++;
        }
    }

    @Override
    public Rectangle gerRec() {
        return null;
    }
}

教学视频链接:https://www.bilibili.com/video/BV1iQ4y1C7pt?p=18&spm_id_from=pageDriver

该教学内容为b站上《清华博士用1小时讲完的Java坦克大战项目!》

本代码内容均依据该部分视频学习进行编辑;该视频中缺少部分已补全并可执行

注意:视频中的GamePanel类=该博客文章Panel类