为了增进线程理解,写下了如下礼花代码。讲解代码之前先看一下效果,两发礼花同时发射绽放,最后在代码中添加了音效效果。
首先讲解一下本代码核心部分。首先是对粒子这个类的属性和方法的定义,代码如下:
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();
}
}
到此结束所有代码,该代码还有其他改变的可能,欢迎交流与分享!