新增功能:
-
解决穿过边界问题
-
实现敌方坦克自由移动
-
我方坦克实现连续发射,且同一时间最多存在五发子弹
-
敌方坦克发射的子弹在销毁后,再次发射子弹
-
实现我方坦克被击中时爆炸
功能实现 :
一、解决穿过边界问题
思路:
在超类Tank类的移动方法中添加限制条件,即在坦克到达边界坐标后,无法再次调用移动方法进行移动
实现代码:
//上下左右移动方法
public void moveUp() {
if (y - speed >= 0 && y <= 690) {
y -= speed;
}
// System.out.println("x=" + x + " y=" + y);//测试到达边界时坐标
}
public void moveRight() {
if (x >= 0 && x + speed <= 940) {
x += speed;
}
// System.out.println("x=" + x + " y=" + y);
}
public void moveDown() {
if (y >= 0 && y + speed <= 690) {
y += speed;
}
// System.out.println("x=" + x + " y=" + y);
}
public void moveLeft() {
if (x - speed >= 0 && x <= 940) {
x -= speed;
}
// System.out.println("x=" + x + " y=" + y);
}
二、实现敌方坦克自由移动
思路:
在enemyTank类中的run方法里,不停的循环获取坦克当前方向,然后根据方向调用对应的移动方法,可以利用(int)(Math.random()*500)得到随机数,根据随机数确定这个方向移动几次,以防止移动过快,延时50毫秒移动一次。这个方向移动结束后,同样使用(int)(Math.random()*4)得到0~3的随机整数,通过setDirect()方法即可随机更改移动方向
代码实现:
@Override
public void run() {
while (true) {
//根据方向继续向前走
switch (getDirect()) {
case 0:
//在一个方向上随机多走几步
for (int i = 0; i < (int) (Math.random() * 500); i++) {
moveUp();
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 1:
for (int i = 0; i < (int) (Math.random() * 500); i++) {
moveRight();
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 2:
for (int i = 0; i < (int) (Math.random() * 500); i++) {
moveDown();
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
case 3:
for (int i = 0; i < (int) (Math.random() * 500); i++) {
moveLeft();
//休眠50毫秒
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
...
//改变行动方向 0~3
setDirect((int) (Math.random() * 4));
//该线程结束条件
if (!isLive) {
break;
}
}
}
三、我方坦克实现连续发射,且同一时间最多存在五发子弹
思路:
① 在Tank超类中定义用来存储Shot子弹类的集合Vector<Shot> shots,并且定义一个根据Tank位置和方向创建Shot对象的方法public void shotEnemyTank(),在方法中每调用一次即可创建一个Shot对象,且会添加到shots集合中和启动线程。
② 在MyPanel类的绘制我方子弹的方法,更改为先取出shots中的shot对象,然后判断shot对象是否存活,若存活则进行绘制,否则就从shots中删除该shot对象。
③ 实现同一时间存在五发子弹,则需要在监听按键J处添加判断条件——当myTank的shots.size()<5时,才可调用shotEnemyTank()方法创建新的shot对象。
代码实现:
//定义一个Shot对象,表示一个射击行为
Shot shot = null;
//存储shot对象
Vector<Shot> shots = new Vector<>();
//射击
public void shotEnemyTank() {
//创建Shot对象,根据Tank对象位置和方向来创建Shot
// 0 向上 1 向右 2 向下 3 向左
switch (getDirect()) {//得到Tank方向
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;
}
shots.add(shot);
//启动Shot线程
new Thread(shot).start();
}
//画MyTank发射的子弹
for (int i = 0; i < myTank.shots.size(); i++) {
Shot shot = myTank.shots.get(i);
if (shot != null && shot.isLive) {
g.setColor(Color.cyan);//青色
g.fill3DRect(shot.x, shot.y, shot.width, shot.height, false);
} else {
myTank.shots.remove(shot);
}
}
//如果用户按下键就会发射子弹
if (e.getKeyCode() == KeyEvent.VK_J) {//J键
//一次发射一颗子弹
// if (myTank.shotVector.size() == 0) {
// myTank.shotEnemyTank();
// }
//连续发射
// myTank.shotEnemyTank();
//同一时间最多存在五个子弹
if (myTank.shots.size() < 5) {
myTank.shotEnemyTank();
}
}
四、敌方坦克发射的子弹在销毁后,再次发射子弹
思路:
① 在MyPanel类对敌方坦克进行初始化的时候,调用enemyTank.shotEnemyTank()方法创建Shot对象,并启动,可以实现坦克在刚出现时发射一发子弹
② 在enemyTank敌方坦克类的run方法中添加判断条件——当shot.isLive为false时即子弹不存活时,调用shotEnemyTank()方法用于创建新的Shot对象并启动,即可实现当敌方发射的子弹销毁时,再次发射子弹。
代码实现:
//初始化敌人坦克
for (int i = 0; i < enemySize; i++) {
EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 0, 1);
//启动坦克run方法,让其可以随机移动
new Thread(enemyTank).start();
enemyTank.shotEnemyTank();//创建新的Shot对象,并启动
//加入用于存放敌人坦克的Vector中
enemyTanks.add(enemyTank);
}
if (!shot.isLive) {
shotEnemyTank();//创建新的Shot对象,并启动
}
五、实现我方坦克被击中时爆炸实现我方坦克被击中时爆炸
思路:
编写public void hitTankMy()方法用于判断我方坦克是否被敌方击中。首先,遍历enemyTanks集合取出敌方坦克对象,再次遍历敌方坦克对象的shots集合取出shot对象。随后,判断敌方坦克的shot对象是否存活,若存活则传入hitTank(enemyTank.shot, myTank)方法进行击中判断。最后,将hitTankMy()方法放入MyPanel的run方法中循环判断。
代码实现:
//发射多颗子弹时,判断我方坦克是否被子弹击中
public void hitTankMy() {
for (int i = 0; i < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);//
for (int j = 0; j < enemyTank.shots.size(); j++) {
//判断我方坦克是否被子弹击中
if (enemyTank.shot != null && enemyTank.shot.isLive) {//当子弹还存活时
hitTank(enemyTank.shot, myTank);
}
}
}
}
//编写方法,判断子弹是否击中坦克 将其放入run方法循环判断
public void hitTank(Shot s, Tank tank) {
//判断 s 击中坦克
// 0 向上 1 向右 2 向下 3 向左
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;
//创建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;
//创建Bomb对象,加入到bombs中
Bomb bomb = new Bomb(tank.getX(), tank.getY());
bombs.add(bomb);
}
break;
}
}
@Override
public void run() {//每隔100毫秒,重绘区域,刷新绘图区域,子弹就移动 在创建MyPanel的地方启动线程
while (true) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
hitTankEnemy();//判断敌人坦克是否被击中
hitTankMy();//判断我方坦克是否被击中
this.repaint();//重绘
}
}
在学习中不断改进、不断进步,加油!!!
未完待续~