我写了一个粒子系统小程序;目前我正在创建,并分别绘制每个粒子。 (这是代码)
BufferedImage backbuffer;
Graphics2D g2d;
public void init(){
backbuffer = new BufferedImage(WIDTH,HEIGHT,BufferedImage.TYPE_INT_RGB);
g2d = backbuffer.createGraphics();
setSize(WIDTH, HEIGHT);
//creates the particles
for (int i = 0; i < AMOUNTPARTICLES; i++) {
prtl[i] = new particleO();
prtl[i].setX(rand.nextInt(STARTX));
prtl[i].setY(rand.nextInt(STARTY));
prtl[i].setVel(rand.nextInt(MAXSPEED)+1);
prtl[i].setFAngle(Math.toRadians(rand.nextInt(ANGLESPREAD)));
}
//other code
}
public void update(Graphics g) {
g2d.setTransform(identity);
//set background
g2d.setPaint(BGCOLOUR);
g2d.fillRect(0,0,getSize().width,getSize().height);
drawp();
paint(g);
}
public void drawp() {
for (int n = 0; n < AMOUNTPARTICLES; n++) {
if (prtl[n].getAlive()==true){
g2d.setTransform(identity);
g2d.translate(prtl[n].getX(), prtl[n].getY());
g2d.setColor(prtl[n].getColor());
g2d.fill(prtl[n].getShape());
}
}
}
它的表现还不错,我可以得到~40FPS的20,000粒子(虽然,我有一台不错的笔记本电脑)。但是在我添加了碰撞检测(见下文)后,这个数字急剧下降到2000以下,
public void particleUpdate(){
for (int i = 0; i < AMOUNTPARTICLES; i++) {
//other update code (posx, posy, angle etc etc)
for (int j = 0; j < AMOUNTPARTICLES; j++) {
if (i!=j && prtl[j].getAlive()==true){
if(hasCollided(i, j)){
prtl[i].setcolor(Color.BLACK);
prtl[j].setcolor(Color.BLACK);
}
}
}
public boolean hasCollided(int prt1, int prt2){
double dx = prtl[prt1].getX() - prtl[prt2].getX();
double dy = prtl[prt1].getY() - prtl[prt2].getY();
int edges = prtl[prt1].getRadius() + prtl[prt2].getRadius();
double distance = Math.sqrt( (dx*dx) + (dy*dy) );
return (distance <= edges);
}
我已经搜索了一些更好的方法来将粒子绘制到屏幕上,但这些例子要么让我困惑,要么不适用。
我正在做一大堆计算(太多)。但我想不出另一种做法,欢迎提出建议。
答案 0 :(得分:5)
首先,添加类似碰撞检测的东西总会占用大量内存。但是,让我们来看看你的碰撞检测算法
public void particleUpdate(){
for (int i = 0; i < AMOUNTPARTICLES; i++) {
//other update code (posx, posy, angle etc etc)
for (int j = 0; j < AMOUNTPARTICLES; j++) {
if (i!=j && prtl[j].getAlive()==true){
if(hasCollided(i, j)){
prtl[i].setcolor(Color.BLACK);
prtl[j].setcolor(Color.BLACK);
}
}
}
让我们假装只有2个粒子,1和2.您将按顺序检查 1,1 1,2 2,1 2,2
事实是,在这种情况下,你只需要检查1对2。如果1次点击2,则2也会达到1.因此,请更改先前测试的for循环跳过,以及相同的数字。
public void particleUpdate(){
for (int i = 0; i < AMOUNTPARTICLES; i++) {
//other update code (posx, posy, angle etc etc)
for (int j = i+1; j < AMOUNTPARTICLES; j++) {
我注意到的另一件事是你做了sqrt
操作,但只是比较看起来像静态数字。如果你删除它,并将其与平方数进行比较,你将获得很大的改进,尤其是你做了很多的事情。
double distance_squared = ( (dx*dx) + (dy*dy) );
return (distance <= edges*edges);
继续寻找这样的改进。然后你可能会仔细查看其他选项,比如使用不同的类,线程等,这些都可以改善系统。但请确保您可以首先优化代码。这是我将尝试的其他事项的列表,大致按顺序。
答案 1 :(得分:4)
绘制是一个复杂的过程,并且可以由于多种原因触发绘制请求,操作系统可能希望窗口更新,重绘管理器可能想要重绘,程序员可能想要重新绘制。
更新paint
进程中的粒子是个坏主意。您要做的是在单独的缓冲区中的单独线程中更新粒子。当你准备好了,请求负责绘制缓冲区的组件执行重绘,传递缓冲区的新副本以重新绘制(你不想在开始更新到屏幕的缓冲区上绘画,你'最终会弄脏油漆。)
很难从您的代码中看出来,但看起来您正在使用java.awt.Applet
,个人而言,我会升级到javax.swing.JApplet
。
我将这幅画移到java.swing.JPanel
。 Swing组件提供双缓冲(以及其他缓冲策略)。此面板的唯一工作是在粒子引擎具有新帧时为屏幕绘制缓冲区。
粒子引擎负责更新所有粒子并将这些结果绘制到后备缓冲区(BufferedImage
),然后将其传递给面板,面板将制作副本并安排更新。 / p>
摆动不是安全的。也就是说,您不应该从事件调度线程以外的任何线程更改UI。为此,您可能希望通过Concurrency in Swing读取解决方案,以将屏幕外缓冲区重新同步到客户端。
答案 2 :(得分:0)
你正在检查所有粒子碰撞的所有粒子,这是一个非常复杂的,n ^ 2的顺序(2,000粒子意味着4,000,000种组合,每帧)。
问题不是java而是算法。必须有更好的选择,但首先你可以减少比较;如果您有最大速度S并且世界中的时间增加T,则使用T * S获得可能与您正在考虑的粒子碰撞的粒子的最大距离D.将搜索范围减少到距离等于或小于该距离的粒子。也许更容易将搜索限制在以粒子为中心的正方形中的高度/宽度D(这将包括一些太远的粒子,但会使检查更容易)。
此外,在您的代码中,您正在检查P1与P2以及P2与P1的冲突是否相同,这是一种可以轻松避免的冗余。