6

我写了一个粒子系统小程序;目前我正在创建并分别绘制每个粒子。(这里是代码)

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());


            }
    }

}

它的性能还不错,我可以用 20,000 个粒子获得约 40FPS(尽管我有一台不错的笔记本电脑)。但是在我添加了碰撞检测(见下文)之后,这个数字骤降到不到 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);


    }

我已经搜索了很多将粒子绘制到屏幕上的更好方法,但这些示例要么让我感到困惑,要么不适用。

我正在做大量的计算(太多)。但是我想不出另一种方法,欢迎提出建议。

4

3 回答 3

6

首先,添加诸如碰撞检测之类的东西总是会占用大量内存。但是,让我们看看您的碰撞检测算法

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. 检查粒子 i 是否还活着,甚至在 i 进入视野后计算任何其他内容之前。
  2. 快速通过对,如果它们接近,甚至会费心仔细检查。一种简单的方法是先检测它们是否在 x 和 y 维度内,然后再进行 sqrt 操作。总是先做最便宜的测试,然后再做复杂的测试。
  3. 查看您的代码,看看您是否真的使用了所有计算出的值,或者您是否能够以某种方式减少操作次数。
  4. 也许您可以使用粗笔划定期对图像进行聚类,然后仅细化通过初始聚类一段时间的对象,然后执行广泛的聚类算法。
  5. 你可以线程碰撞检测。但是,如果您要这样做,您应该只执行线程检查以查看是否有碰撞,并且在所有这些线程完成后,更新视图上的对象。
  6. 研究替代架构,这可能会加快一些速度。
于 2012-11-22T23:44:09.163 回答
4

绘画是一个复杂的过程,绘画请求可能由于多种原因被触发,操作系统可能想要更新窗口,重绘管理器可能想要重绘,程序员可能想要重绘。

在过程中更新粒子paint是个坏主意。您要做的是在单独缓冲区的单独线程中更新粒子。准备好后,请求负责绘制缓冲区的组件执行重新绘制,将缓冲区的新副本传递给重新绘制(您不想在开始更新到屏幕的缓冲区上进行绘制,您最终会弄脏油漆)。

很难从您的代码中分辨出来,但看起来您正在使用java.awt.Applet,就个人而言,我会升级到javax.swing.JApplet.

我会把这幅画移到java.swing.JPanel. Swing 组件提供双重缓冲(以及其他缓冲策略)。这个面板的唯一工作是在粒子引擎有一个新帧时在屏幕上绘制一个缓冲区。

粒子引擎负责更新所有粒子并将这些结果绘制到后备缓冲区(BufferedImage),然后将其交给面板,面板将制作副本并安排更新。

Swing 不是线程安全的。也就是说,您不应该从除 Event Dispatching Thread 之外的任何线程对 UI 进行更改。为此,您可能希望通读Swing中的并发,以了解将屏幕外缓冲区重新同步到客户端的解决方案。

于 2012-11-22T23:44:12.767 回答
0

您正在检查与所有粒子碰撞的所有粒子,这是一个相当大的要求,大约为 n^2(2,000 个粒子意味着每帧有 4,000,000 个组合)。

问题不是java,而是算法。必须有更好的选择,但首先可以减少比较;如果您有最大速度 S 并且您的世界中的时间以 T 递增,则使用 T*S 您可以获得可以与您正在考虑的粒子碰撞的粒子的最大距离 D。将搜索减少到距离等于或小于该距离的那些粒子。也许将搜索限制在以粒子为中心且高度/宽度为 D 的正方形中会更容易(这将包括一些太远的粒子,但会使检查更容易)。

此外,在您的代码中,您正在检查 P1 与 P2 和 P2 与 P1 的冲突,这是相同的,这是您可以轻松避免的冗余。

于 2012-11-22T23:48:21.417 回答