Java-敌方坦克攻击(Vector实现发射多颗子弹并绘制)
实现过程
- 子弹类(Bullet)
- 新增属性:
- (1)isLive:子弹是否有效
- 当子弹超出边界后,子弹失效,不应该继续占用资源;
- (1)isLive:子弹是否有效
- 新增属性:
/**
* 子弹类
* @author: SEA
* @date: 2023/3/18
*/
public class Bullet {
//子弹坐标和发射方向
int x;
int y;
int direction;
int speed = 5;//子弹初始速度为 5
boolean isLive = false;//子弹是否有效
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;
}
public boolean isLive() {
return isLive;
}
public void setLive(boolean live) {
isLive = live;
}
}
- 普通坦克类(Tank)
- 新增属性:
- (1)子弹对象集合bullets;
- 新增方法:
- (1)setBullets() 和 getBullets();
- 修改:
- (1)坦克攻击方法attack()中,每调用一次attack()方法:
- 1)生成一个子弹对象bullet,并设置isLive为true;
- 2)将新生成的子弹对象bullet加入子弹对象集合bullets中;
- (1)坦克攻击方法attack()中,每调用一次attack()方法:
- 新增属性:
import java.util.Vector;
/**
* 坦克
* @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;//坦克子弹
private Vector<Bullet> bullets = new Vector<Bullet>();//子弹集合,使坦克可以发射多颗子弹并在panel绘制
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 Vector<Bullet> getBullets() {
return bullets;
}
public void setBullets(Vector<Bullet> bullets) {
this.bullets = bullets;
}
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;
}
//这样写只能发射一颗子弹,因为每次攻击都会重新给子弹分配新的对象,repaint()后旧的对象将不会被绘制,但实际上仍然存在;
//1.我一开始想的是,怎么让本次攻击不影响上一次攻击,没想出来怎么实现;
//2.韩老师的思路:每次攻击创建一颗子弹,用vector保存,绘制的时候再取出来,到边界后再移除;
this.bullet = new Bullet(bullet_x, bullet_y, this.direction);
this.bullet.isLive = true;
this.bullets.add(this.bullet);
TankAttackThread tankAttackThread = new TankAttackThread(bullet);
Thread thread = new Thread(tankAttackThread);
thread.start();
}
}
-
子弹发射:
- 修改:
- (1)当子弹超出边界时,设置子弹的isLive属性为false,该子弹应释放资源;
/** * 子弹发射(多线程) * @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 ){ this.bullet.setLive(false); break; } } } }
- 修改:
-
坦克大战绘图区域:
-
修改:
-
(1)使用for循环,绘制玩家坦克子弹;
-
(2)使用for循环,绘制敌方坦克子弹;
-
(3)需要注意的点:
- 遍历时,如果使用增强for遍历,会爆ConcurrentModificationException错误;
- 绘制时,当子弹的isLive属性为false时,及时移除子弹,释放资源;
-
/**
* 坦克大战绘图区域
* @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(heroTank.getX(), heroTank.getY(), g, heroTank.getDirection(), 0);
//绘制玩家坦克的子弹
for (int i = 0; i < heroTank.getBullets().size(); i++) {
Bullet bullet = heroTank.getBullets().get(i);//取出子弹
if(bullet!=null && bullet.isLive)
drawBullet(bullet.x, bullet.y, g, 0);//绘制
else
heroTank.getBullets().remove(bullet);//子弹超出边界,移除
}
//绘制敌方坦克子弹
for (int i = 0; i < enemyTankSize; i++) {
EnemyTank enemyTank = enemyTanks.get(i);
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 1);
System.out.println("====================================【" + enemyTank.getBullets().size() + "】==========");
for (int j = 0; j < enemyTank.getBullets().size(); j++) {//这里应使用for遍历,不然会爆并发错误
Bullet bullet = enemyTank.getBullets().get(j);//取出子弹
if(bullet != null && bullet.isLive)
drawBullet(bullet.x, bullet.y, g, 1);//绘制
else
enemyTank.getBullets().remove(bullet);//子弹超出边界,移除
}
}
}
......
//绘制子弹具体实现
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);//绘制矩形子弹
}
......
}
//最后,在GameFrame中启动GamePanel线程
实现效果
待优化
-
子弹击中敌方坦克后:
- 1.爆炸效果;
- 2.敌方坦克消失;
-
敌方坦克需要能够随机移动;
-
坦克移动范围未限制,可能移动至边界外;