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类