Java粒子系统 - 多发礼花

为了增进线程理解,写下了如下礼花代码。讲解代码之前先看一下效果,两发礼花同时发射绽放,最后在代码中添加了音效效果。

在这里插入图片描述
首先讲解一下本代码核心部分。首先是对粒子这个类的属性和方法的定义,代码如下:

public class Particle 
{
    public VecT position, velocity, acceleration;
    public Color color;
    public double life, age;
    public int size;

    public int x, y;

    public int getX()
    {
        return (int) position.x;
    }

    public int getY() 
    {
        return (int) position.y;
    }
    
}

在粒子类中会发现有一个 VecT 类, 此类用来控制粒子的位置,速度,加速度等属性变化

public class VecT 
{
    public double x, y;

    public VecT(double x, double y)
    {
        this.x = x;
        this.y = y;
    }


    public VecT add(VecT p) // 计算下一个时刻位置
    {
        return new VecT(this.x + p.x, this.y + p.y);
    }


    public VecT multiply(double f) // 计算下一个时刻速度
    {
        return new VecT(this.x * f, this.y * f);
    }

}

然后建立一个Bomb 类用来控制多组粒子爆炸效果

public class Bomb
{
    public VecT position, velocity, acceleration;
    public double life, age;
    public int size;
    public int x, y;
    public Color Bombcolor;
    private ArrayList <Particle> pr = new ArrayList<>();

    public int getX()
    {
        return (int) position.x;
    }

    public int getY()
    {
        return (int) position.y;
    }

    Color color= new Color(255, 0, 255);

    public void drawscatter(Graphics g, double dt, Bomb b1, Bomb b2, JFrame jFrame,Graphics bg)
    {
        b1.age += dt;
        b2.age += dt;

        while(pr.size()<500) {
            for (int i = 0; i < 300; ++i) { //本代码中设计了两发礼花同时绽放 也可以添加多发礼花
            // 添加第一个爆炸粒子
                Particle tp = new Particle();
                tp.position = new VecT(b1.getX(), b1.getY());
                tp.velocity = sampleDirectionv2().multiply(1 - (double) (i / 30) / (double) 10);
                tp.acceleration = tp.velocity.multiply(0.1);
                tp.life = 10;
                tp.age = 1;

                int r = color.getRGB();
                r -= 3000;
                color = new Color(r);
                tp.color = color;
                tp.size = 5;
                pr.add(tp);

           // 添加第二个爆炸粒子
                Particle tp1 = new Particle();
                tp1.position = new VecT(b2.getX(), b2.getY());
                tp1.velocity = sampleDirectionv2().multiply(1 - (double) (i / 30) / (double) 10);
                tp1.acceleration = tp1.velocity.multiply(0.1);
                tp1.life = 10;
                tp1.age = 1;

                int r1 = color.getRGB();
                r1 -= 3000;
                color = new Color(r1);
                tp1.color = color;
                tp1.size = 5;
                pr.add(tp1);
            }

        }
        
        bg.setColor(Color.black);
        bg.fillRect(0, 0, jFrame.getWidth(), jFrame.getHeight());
        
    // 粒子挨个从动态数组中拿出 判断是否在屏幕中出现的时间超过定义的时间
        for (int i = 0; i < pr.size(); ++i)
        {
            Particle p = pr.get(i);
            p.age += dt;
            if (p.age >= p.life) 
            {
                pr.remove(i);
            }
            
        // 计算粒子下一个时间点位置和速度
            p.position = p.position.add(p.velocity.multiply(dt));
            p.velocity = p.velocity.add(p.acceleration.multiply(dt));
            bg.setColor(p.color);
            bg.fillOval(p.getX(), p.getY(), p.size, p.size);

        }
    }

// 随机速度定义
    public static VecT sampleDirectionv2() {
        double theta = Math.random() * 2 * Math.PI;
        return new VecT(5*(-6.56*Math.sin(1.4*theta)-Math.sin(1.56*theta)), 5*(1.4*Math.cos(1.4*theta)+Math.cos(1.56*theta)));
    }

}

接下来对线程的定义:

public class FireWorksThread extends Thread 
{
    private JFrame jFrame;
    Graphics g;
    private int t=0;

    private ArrayList<Bomb> bo = new ArrayList<>();
    private int startX, startY = 600;
    private boolean isadd = true;
    private Sound sound=new Sound();
    private Thread thread;

// 添加音效特效
    public FireWorksThread(JFrame jframe)
    {
        this.jFrame = jframe;
        thread=new Thread(sound);
        thread.start();
    }

    public void run()
    {
        double dt = 0.1d;
        g = jFrame.getGraphics();
        Image image = jFrame.createImage(jFrame.getWidth(), jFrame.getHeight());
        Graphics bg = image.getGraphics();

        while(true) {

            if (isadd == true)
            {
              	for(int i=0;i<2;i++)  //添加两个爆炸粒子
              	{
                  Bomb bb = new Bomb();
                  startX = (int) (Math.random() * (jFrame.getWidth() - 200) + 80);

                  bb.position = new VecT(startX, startY);
                  bb.velocity = new VecT(0, -35);
                  bb.acceleration = new VecT(0, 1);
                  bb.life = 16+ Math.random()*10;
                  bb.age = 1;
                  bb.Bombcolor = new Color(255, 255, 255);
                  bb.size = 2;
                  bo.add(bb);
              	}
            }
            
            for (int i = 0; i < bo.size()-1; i++) //从动态数组中拿出每个爆炸粒子 根据条件绘制
            {
                isadd = false;
                Bomb b1 = bo.get(i);
                Bomb b2= bo.get(i+1);

                if (b1.age < b1.life && b2.age< b2.life)
                {
                    this.drawrise(g, dt,b1,b2,bg);
                }
                else 
                {
                    b1.drawscatter(g, dt, b1, b2, jFrame,bg);
                }

                if (b1.age > b1.life +20 || b2.age >b2.age+20)
                {
                    bo.remove(b1);
                    bo.remove(b2);
                    isadd = true;
                }

                g.drawImage(image,0,0,null);

                try 
                {
                    Thread.sleep(10);
                } 
                catch (Exception ef) 
                {}

            }
        }
    }

//对爆炸粒子上升阶段定义的方法
    public void drawrise(Graphics g, double dt, Bomb b1,Bomb b2,Graphics bg)
    {
        bg.setColor(Color.black);
        bg.fillRect(0, 0, jFrame.getWidth(), jFrame.getHeight());
        
     //爆炸粒子下一时间点的位置和速度计算 然后爆炸粒子绘制
        b1.position = b1.position.add(b1.velocity.multiply(dt));
        b1.velocity = b1.velocity.add(b1.acceleration.multiply(dt));
        b1.age += dt;
        b1.Bombcolor = new Color(255, 255, 255);
        bg.setColor(b1.Bombcolor);
        bg.fillOval(b1.getX(), b1.getY(), b1.size, b1.size);

        b2.position = b2.position.add(b2.velocity.multiply(dt));
        b2.velocity = b2.velocity.add(b2.acceleration.multiply(dt));
        b2.age += dt;
        b2.Bombcolor = new Color(255, 255, 255);
        bg.setColor(b2.Bombcolor);
        bg.fillOval(b2.getX(), b2.getY(), b2.size, b2.size);
    }
}

最后定义礼花音效线程

import javazoom.jl.player.Player;

import java.io.BufferedInputStream;
import java.io.FileInputStream;


public class Sound extends Thread
{
    private Boolean isstop=false;
    private Player player;

    public void setIsstop(Boolean isstop) 
    {
        this.isstop = isstop;
    }

    public Player getPlayer() 
    {
        return player;
    }

    @Override
    public void run()
    {
        while(!isstop)
        {
            try 
            {
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream("fireWork.mp3"));
                player = new Player(bis);
                player.play();
            }
            catch (Exception e)
            {
                System.out.println("进入异常");
            }
        }
    }
}

礼花界面定义

public class FireWorksMain  
{
    public static void main(String[] args) 
    {
        FireWorksMain fw = new FireWorksMain();
        fw.initUI();
    }

    public void initUI()
    {
        JFrame f = new JFrame();
        f.setTitle("烟花");
        f.setSize(800, 700);
        f.setDefaultCloseOperation(3);
        f.setLocationRelativeTo(null);
        f.setVisible(true);

        FireWorksThread ft = new FireWorksThread(f);
        ft.start();
    }
}

到此结束所有代码,该代码还有其他改变的可能,欢迎交流与分享!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值