Java-坦克爆炸效果以及敌方坦克随机移动的实现(Toolkit实现读取图片资源)
Toolkit实现读取图片资源
一、Toolkit读取图片资源
-
情况1:该Project/Module是普通项目
- 图片资源文件应放在 当前Java文件在out目录下的同一个包中;
- 存在的问题:每次rebuild项目时,该文件会被删除;
//例: Image img = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("test.jpg"));
-
情况2:该Project/Module是Maven项目
- 方式一:图片资源可以放在 当前Java文件在target目录下的同一个包中,但是也存在情况1中的问题;
- 方式二:图片资源放在resource目录下,但是需要注意的是,在读取图片时,应当使用绝对路径读取;
//例: //1.准备绝对路径 String cur_dir = "D:\\JavaCode\\projects\\JavaDemo\\TankGameV0.3\\src\\main\\resources\\"; //2.Toolkit读取图片字眼 Image img = Toolkit.getDefaultToolkit().getImage(cur_dir + "test.jpg");
二、在JPanel上绘制图片
- 有了图片资源对象img后,我们只需使用JPanel中绘制图片的方法drawImage()进行绘制即可;
//绘制图片对象
@Override
public void paint(Graphics g) {
super.paint(g);
/*
在坐标为(100, 100)的位置绘制宽高为400x400的图片对象img
*/
g.drawImage(img, 100, 100, 400, 400, this);
}
案例:绘制坦克被击中后的爆炸效果
实现过程
- 新增坦克爆炸效果类
- 注意的点:通过给坦克爆炸效果类设置生命周期属性,实现在生命周期的不同阶段显示不同的效果;
/**
* 坦克爆炸效果实现类
* @author: SEA
* @date: 2023/4/1
*/
public class TankExplosionEffect {
//爆炸效果的坐标
private int x;
private int y;
//爆炸效果的生命周期,在不同的阶段显示不同的效果
private int life = 9;
//爆炸效果是否显示
private boolean isLive = true;
public TankExplosionEffect(int x, int y) {
this.x = x;
this.y = y;
}
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 getLife() {
return life;
}
public void setLife(int life) {
this.life = life;
}
public boolean isLive() {
return isLive;
}
public void setLive(boolean live) {
isLive = live;
}
public void lifeDown(){
if(this.life > 0){
--this.life;
}
else {
this.setLive(false);
}
}
}
- 坦克大战绘图区域(GamePanel类)
- 新增属性
- 三个Image图片对象;
- Vector<TankExplosionEffect> bombs:爆炸效果集合;
- 修改:
- 敌方坦克被击中时,在移除敌方坦克的同时,在该坦克的位置添加坦克爆炸效果,并放入爆炸效果集合;
- 遍历爆炸效果集合,根据爆炸效果的生命周期绘制爆炸效果;
- 新增属性
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;
/**
* 坦克大战绘图区域
* @author: SEA
* @date: 2023/3/2
*/
class GamePanel extends JPanel implements KeyListener, Runnable{//画板
//定义玩家的坦克
HeroTank heroTank = null;
//考虑多线程问题,使用Vector来保存敌方坦克
Vector<EnemyTank> enemyTanks = null;
int enemyTankSize = 5;
//定义爆炸效果的三张图片
Image image_1 = null;
Image image_2 = null;
Image image_3 = null;
//爆炸效果集合
Vector<TankExplosionEffect> bombs = new Vector<>();
public GamePanel() {
heroTank = new HeroTank(250, 400);//初始化玩家坦克
enemyTanks = new Vector<EnemyTank>();
for (int i = 0; i < enemyTankSize; i++) {
EnemyTank enemyTank = new EnemyTank(100 * (i + 1), 100);
enemyTank.setDirection(i % 4);
//让敌方坦克也能攻击
enemyTank.attack();
enemyTanks.add(enemyTank);
}
}
@Override
public void paint(Graphics g) {
super.paint(g);
//游戏窗口背景
g.setColor(Color.BLACK);
g.fillRect(0, 0, 1000, 800);
//画出坦克-封装成方法
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 < enemyTanks.size(); i++) {
EnemyTank enemyTank = enemyTanks.get(i);
if(enemyTank.isLive())
drawTank(enemyTank.getX(), enemyTank.getY(), g, enemyTank.getDirection(), 1);
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);//子弹超出边界,移除
}
}
//绘制爆炸效果(如果爆炸效果集合中有的话)
for (int i = 0; i < bombs.size(); i++) {
TankExplosionEffect tankExplosionEffect = bombs.get(i);
if (tankExplosionEffect.getLife() > 6){
g.drawImage(image_1, tankExplosionEffect.getX(), tankExplosionEffect.getY(), 60, 60, this);
} else if (tankExplosionEffect.getLife() > 3) {
g.drawImage(image_2, tankExplosionEffect.getX(), tankExplosionEffect.getY(), 60, 60, this);
}
else {
g.drawImage(image_3, tankExplosionEffect.getX(), tankExplosionEffect.getY(), 60, 60, this);
}
tankExplosionEffect.lifeDown();
if(tankExplosionEffect.getLife() == 0){
tankExplosionEffect.setLive(false);
bombs.remove(tankExplosionEffect);
}
}
}
/**
*
* @param x 坦克左上方x轴坐标
* @param y 坦克左上方y轴坐标
* @param g 画笔
* @param direction 坦克的朝向:0 => 向上 ,1 => 向下 ,2 => 向左 , 3 => 向右
* @param type 坦克类型:0 => 玩家坦克,1 => 敌方坦克
*/
public void drawTank(int x, int y, Graphics g, int direction, int type){
......
}
public void drawBullet(int x, int y, Graphics g, int type){
......
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
......
}
@Override
public void keyReleased(KeyEvent e) {
}
//是否击中敌方坦克判断,返回被击中的敌方坦克
private EnemyTank isHitted(Bullet bullet, Vector<EnemyTank> enemyTanks) {
......
}
@Override
public void run() {
while(true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if(heroTank.getBullets() != null){
for (int i = 0; i < heroTank.getBullets().size(); i++) {
Bullet cur_bullet = heroTank.getBullets().get(i);
if (cur_bullet != null && cur_bullet.isLive()){
//是否击中敌方坦克判断
EnemyTank hittedTank = isHitted(cur_bullet, enemyTanks);
if (cur_bullet.isLive() && hittedTank != null) {
System.out.println("======击中了["+ hittedTank.getX() + ", " + hittedTank.getY() +"]=======");
//销毁被击中的坦克以及该子弹
hittedTank.setLive(false);
cur_bullet.setLive(false);
enemyTanks.remove(hittedTank);
heroTank.getBullets().remove(cur_bullet);
//坦克移除时增加爆炸效果
TankExplosionEffect tankExplosionEffect = new TankExplosionEffect(hittedTank.getX(), hittedTank.getY());
bombs.add(tankExplosionEffect);
}
}
}
}
this.repaint();
}
}
}
案例:敌方坦克随机移动
实现过程
- 敌方坦克类
- 修改:
- 1.为了使每一个敌方坦克随机移动,需要使敌方坦克类继承Runnable接口,并实现run()方法【变成一个线程类】;
- 2.run()方法中实现坦克随机移动,每个坦克每隔【500,1300】ms(此处随机产生一个时间)都会有一个随机的移动方向并移动一定的距离;
- 需要注意的点:
- 1.在编写多线程并发程序时,一定要写线程终止逻辑;
- 修改:
import java.util.Random;
/**敌方坦克类
* @author: SEA
* @date: 2023/3/5
*/
public class EnemyTank extends Tank implements Runnable{
public EnemyTank(int x, int y) {
super(x, y);
Thread thread = new Thread(this);
thread.start();
}
@Override
public void run() {
while(true) {
int millis =(int) (Math.random() * 800) + 500;
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
int direction = new Random().nextInt(4);
this.setDirection(direction);
this.setSpeed(2);
switch (direction) {
case 0:
//让坦克保持一个方向走10次
for (int i = 0; i < 20; i++) {
this.moveUp();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
break;
case 1:
//让坦克保持一个方向走10次
for (int i = 0; i < 20; i++) {
this.moveDown();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
break;
case 2:
//让坦克保持一个方向走10次
for (int i = 0; i < 20; i++) {
this.moveLeft();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
break;
case 3:
//让坦克保持一个方向走10次
for (int i = 0; i < 20; i++) {
this.moveRight();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
break;
}
//注意:写多线程并发程序时,一定要写线程终止逻辑;
if(!this.isLive())
break;
}
}
}
实现效果
待改进/完善
- 坦克活动范围限制;
- 坦克与坦克之间不能重叠;