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 個のパーティクルで最大 40 FPS を得ることができました (ただし、まともなラップトップを持っています)。しかし、衝突検出 (以下を参照) を追加した後、その数は 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);
                }
            }
  }

1と2の2つのパーティクルしかなかったとしましょう。1,11,22,12,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操作をしているということですが、それは静的な数のように見えるものと比較するためだけです。それを削除し、それを2乗数と比較すると、特に多くのことを行うと、大幅な改善が得られます。

    double distance_squared = ( (dx*dx) + (dy*dy) );
    return (distance <= edges*edges);

このような改善を探し続けてください。次に、別のクラスの使用やスレッド化など、システムを改善する可能性のある他のオプションを注意深く検討する場合があります。ただし、最初にできる場所でコードを最適化するようにしてください。これが私が試みる他のことのリストです、大まかに順番に。

  1. iが表示された後、他の何かを計算する前に、パーティクルiが生きているかどうかを確認してください。
  2. ペアが近いかどうかを詳細に確認するだけでも、ペアをすばやく通過します。簡単な方法は、sqrt演算を実行する前に、最初にx次元とy次元内にあるかどうかを検出することです。複雑なテストを行う前に、常に最も安価なテストを最初に行ってください。
  3. コードを見て、計算されたすべての値を実際に使用しているかどうか、または何らかの方法で操作の数を減らすことができるかどうかを確認してください。
  4. おそらく、広いストロークで定期的に画像をクラスター化し、最初のクラスターを一定期間通過したオブジェクトのみを絞り込んでから、広いクラスターアルゴリズムを実行することができます。
  5. 衝突検出をスレッド化できます。ただし、これを行う場合は、何かが衝突したかどうかを確認するためのスレッドのみをスレッド化する必要があります。これらのスレッドがすべて完了したら、ビューのオブジェクトを更新します。
  6. いくつかの速度を上げる可能性のある代替アーキテクチャを調べてください。
于 2012-11-22T23:44:09.163 に答える
4

ペイントは複雑なプロセスであり、ペイント要求はさまざまな理由でトリガーされる可能性があります。OSはウィンドウを更新する必要があり、再ペイントマネージャーは再ペイントする必要があり、プログラマーは再ペイントする必要があります。

プロセス内でパーティクルを更新するpaintことは悪い考えです。あなたがしたいのは、別のバッファ上の別のスレッドでパーティクルを更新することです。準備ができたら、バッファのペイントを担当するコンポーネントに再ペイントを実行するように要求し、バッファの新しいコピーを再ペイントに渡します(画面に更新され始めたバッファにペイントしたくない場合は、次のようにします。汚れた絵の具で終わるでしょう)。

あなたのコードから見分けるのは難しいですが、あなたが使用しているように見えますがjava.awt.Applet、個人的には、にアップグレードしjavax.swing.JAppletます。

絵をに移動しjava.swing.JPanelます。Swingコンポーネントは、ダブルバッファリング(および他のバッファリング戦略)を提供します。このパネルの唯一の仕事は、パーティクルエンジンに新しいフレームがあるときに、画面にバッファをペイントすることです。

パーティクルエンジンは、すべてのパーティクルを更新し、これらの結果をバッキングバッファ(BufferedImage)にペイントする役割を果たします。これはパネルに渡され、パネルはコピーを作成して更新をスケジュールします。

スイングはスレッドセーフではありません。つまり、イベントディスパッチスレッド以外のスレッドから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 に答える