2012-11-22 100 views
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()); 


      } 
    } 

} 

它的性能是好的,我能得到〜40FPS有20000個(雖然,我有一個像樣的筆記本電腦)。但之後,我加入衝突檢測(見下文),這一數字驟降至不足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

http://stackoverflow.com/questions/13046033/an-efficient-way-to-simulate-many-particle-collisions –

+0

不要忘了接受一個答案,一旦你決定哪一個最能幫助你。 – PearsonArtPhoto

回答

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. 在我進入視野之前,請檢查是否存在粒子我還沒有計算任何其他東西。
  2. 快速通過對,只是甚至麻煩仔細檢查他們是否接近。一個簡單的方法是在進行sqrt操作之前先檢測它們是否在x和y維度內。在做複雜的測試之前,總是先做最便宜的測試。
  3. 看看你的代碼,看看你是否真的使用了所有計算的值,或者你是否能夠以某種方式減少操作次數。
  4. 也許你可以用廣泛的筆劃定期對圖像進行聚類,然後只對通過初始聚類一段時間的對象進行優化,然後進行廣泛的聚類算法。
  5. 你可以線程碰撞檢測。但是,如果要這樣做,則只應通過檢查來檢查是否發生了相互衝突,並且在所有這些線程完成後,更新視圖上的對象。
  6. 研究其他架構,這可能會加速一些東西。
+0

非常感謝,我正在考慮這樣的事情(嵌套的),但它不會從我的腦袋裏出來(+ 1'ed) – user1159424

+0

沒問題。優化這樣的東西可能是我最喜歡的編程部分:-) – PearsonArtPhoto

+0

另外,如何檢查粒子已經移動?只繪製自上一幀以來移動的粒子。碰撞也是如此; 2個未移動的粒子不會相互碰撞。 –

4

繪畫是一個複雜的過程,可能由於多種原因觸發繪畫請求,操作系統可能希望窗口更新,重繪管理器可能需要重繪,程序員可能需要重畫。

更新paint過程中的粒子是個壞主意。你想要做的是在單獨的緩衝區上的單獨線程中更新粒子。準備好後,請求負責繪製緩衝區的組件執行重新繪製,傳遞緩衝區的新副本以重新繪製(您不希望在開始更新到屏幕的緩衝區上繪畫,最後會得到骯髒的塗料)。

很難從你的代碼中知道,但它會顯示你正在使用java.awt.Applet,我個人會將它升級到javax.swing.JApplet

我會把這幅畫移動到java.swing.JPanel。 Swing組件提供雙緩衝(以及其他緩衝策略)。這個面板的唯一工作就是當粒子引擎有一個新的幀時,在屏幕上畫一個緩衝區。

粒子引擎負責更新所有粒子並將這些結果繪製到後臺緩衝區(BufferedImage),然後將其交給面板,面板將製作副本並安排更新。

Swing不是線程安全。也就是說,您不應該從任何線程(除了事件派發線程)對UI進行更改。爲此,您可能希望通過Concurrency in Swing的解讀來將屏幕外緩衝區重新同步到客戶端。

+0

+1不錯的地方:P – MadProgrammer

+0

非常豐富, – user1159424

0

您正在檢查與所有粒子碰撞的所有粒子,這是一個非常重要的問題,對於每幀n^2(2,000個粒子意味着4,000,000個組合)。

這個問題不是java,而是算法。必須有更好的選擇,但首先你可以減少比較;如果你有一個最大速度S,並且你的世界中的時間增加了T,那麼T * S可以得到一個粒子的最大距離D,它可以與你正在考慮的粒子發生碰撞。將搜索減少到距離等於或小於該距離的那些粒子。也許將搜索限制在一個以高度/寬度D爲中心的方塊中會更容易搜索(這將包括一些粒子太遠,但會使檢查更容易)。

此外,在您的代碼中,您正在檢查P1與P2和P2與P1的衝突,它們是相同的,這是您可以輕鬆避免的冗餘。