Java-坦克爆炸效果以及敌方坦克随机移动的实现(Toolkit实现读取图片资源)

本文介绍了在Java项目中使用Toolkit读取图片资源的方法,包括在普通项目和Maven项目中的不同策略,并展示了如何在JPanel上绘制坦克爆炸效果和实现敌方坦克的随机移动,通过多线程和Runnable接口确保并发控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
        }
    }
}

实现效果

在这里插入图片描述

待改进/完善

  • 坦克活动范围限制;
  • 坦克与坦克之间不能重叠;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SEA-365

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值