2012-03-25 24 views
7

Bir dalga formunu ekran üzerinde düzgün bir şekilde kaydırması gereken bir java uygulaması yazıyorum. Bu animasyonu mümkün olduğunca düzgün hale getirmek için çeşitli öğreticilerden geçerek yaşıyorum. Görebildiğim kadarıyla, titremeyi ortadan kaldırmak için tüm normal şeyleri yaptım (ekran koruyucusundan çizim yapma ve tek bir adımda render yapma, ayrıca ekran boş bırakılmadan güncelleme geçersiz kılma), ama animasyonum hala titriyor ve ekran Her güncellemeden önce boş bırakılıyor gibi görünüyor.Java animasyonunun titremesi nasıl engellenir

Eminim temel (ve muhtemelen basit) bir şey var eminim, ama fikirlerin dışındayım. Sorunu gösteren bir sınıf yayınlayacağım. Herhangi bir yardım çok takdir edilecektir.

import java.awt.*; 
import javax.swing.*; 

public class FlickerPanel extends JPanel implements Runnable { 

    private float [] pixelMap = new float[0]; 

    /** Cached graphics objects so we can control our animation to reduce flicker **/ 
    private Image screenBuffer; 
    private Graphics bufferGraphics; 

    public FlickerPanel() { 
     Thread t = new Thread(this); 
     t.start(); 
    } 

    private float addNoise() { 
     return (float)((Math.random()*2)-1); 
    } 

    private synchronized void advance() { 
     if (pixelMap == null || pixelMap.length == 0) return; 
     float [] newPixelMap = new float[pixelMap.length]; 
     for (int i=1;i<pixelMap.length;i++) { 
      newPixelMap[i-1] = pixelMap[i]; 
     } 

     newPixelMap[newPixelMap.length-1] = addNoise();  

     pixelMap = newPixelMap; 
    } 

    public void run() { 
     while (true) { 
      advance(); 
      repaint(); 

      try { 
       Thread.sleep(25); 
      } catch (InterruptedException e) {} 

     } 
    } 

    private int getY (float height) { 
     double proportion = (1-height)/2; 
     return (int)(getHeight()*proportion); 
    } 

    public void paint (Graphics g) { 

     if (screenBuffer == null || screenBuffer.getWidth(this) != getWidth() || screenBuffer.getHeight(this) != getHeight()) { 
      screenBuffer = createImage(getWidth(), getHeight()); 
      bufferGraphics = screenBuffer.getGraphics(); 
     } 

     if (pixelMap == null || getWidth() != pixelMap.length) { 
      pixelMap = new float[getWidth()]; 
     } 

     bufferGraphics.setColor(Color.BLACK); 

     bufferGraphics.fillRect(0, 0, getWidth(), getHeight()); 

     bufferGraphics.setColor(Color.GREEN); 

     int lastX = 0; 
     int lastY = getHeight()/2; 

     for (int x=0;x<pixelMap.length;x++) { 
      int y = getY(pixelMap[x]); 
      bufferGraphics.drawLine(lastX, lastY, x, y); 
      lastX = x; 
      lastY = y; 
     } 

     g.drawImage(screenBuffer, 0, 0, this); 
    } 

    public void update (Graphics g) { 
     paint(g); 
    } 

    public static void main (String [] args) { 
     JFrame frame = new JFrame("Flicker test"); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setContentPane(new FlickerPanel()); 
     frame.setSize(500,300); 
     frame.setVisible(true); 
    } 


} 
+0

Kodunuzla ilgili hiçbir sorun bulamıyorum. Masaüstümde oldukça düzgün görünüyor. ps. Yalnızca bir pixelMap sahip olan ve bir siklik tampon olarak kullanarak ve senkronize bir blok ile ona erişim koruyarak çok daha verimli bir şekilde yapabilir. Yeni bir yapı oluşturulduğu için her güncelleme tamam olmalıdır. için – Adam

+0

+ 1 [sscce] (http://sscce.org/). Platformumda oldukça kötü bir şekilde parlıyor. – trashgod

+0

Biraz daha fazla çalınan bir problem, bir boya sırasında değiştirilmekte olan pixelMap veri yapısından kaynaklanıyor olabilir, bu yüzden ekranın farklı bölümleri arasında yırtılmaya başlıyorum. Boya yönteminin başlangıcında pixelMap'in bir klonunu() yapmak bir şeyleri iyileştirir gibi gözükür, ancak titreme hala oradadır, bu yüzden bunun tam bir cevap olduğunu düşünmüyorum. –

cevap

6

JPanel varsayılan olarak çift tamponlu edilir ve "Salıncak programları yerine paint() geçersizleştirebilir paintComponent() geçersiz kılar." - Painting in AWT and Swing: The Paint Methods. Bu 'u geçersiz kılan bu example ile paintComponent()'u geçersiz kılan bu example numaralı kontrastı karşılaştırın.

Ek: Kesme, her bir güncellemede tüm bir görüntünün oluşturulması ve kopyalanmasıyla oluşur. Bunun yerine, here gösterildiği gibi her yinelemede GeneralPath ve draw()Shape numaralı noktalarda puan toplayın. Bir JPanel yılında

+0

paintComponent'i geçersiz kılmayı denedim ve panelin çift tamponlu olmamasına da karar verdim, ancak burada hiçbir fark yaratmıyor gibi görünüyor, titreme hala gerçekleşiyor. –

+0

İlgili [örnek] (http://stackoverflow.com/a/5048863/230513) titreşmiyor. Düzenleme: EDT'de güncellemek için javax.swing.Timer'a güvenir. – trashgod

+0

Yukarıda detaylandırdım. Ayrıca gürültü için ['nextGaussian()'] 'ı (http://docs.oracle.com/javase/7/docs/api/java/util/Random.html#nextGaussian%28%29) ele alalım. – trashgod

7
  1. , geçersiz paintComponent(Graphics) yerine
  2. paint(Graphics) daha yerine Thread.sleep(n) görevleri yinelenen bir salıncak Timer uygulamak veya uzun süren görevler için bir SwingWorker çağırmak. Daha fazla ayrıntı için Concurrency in Swing bakınız.
+1

Evet; 'Uyku()' farklı bir iş parçacığı ve 'Rötuş() üzerinde meydana geldiği halde' evreli olduğunu 'avans()' senkronize edilmesi gerekir. – trashgod

+0

Tamam, bu iyi bir nokta, ancak titremede doğrudan bir etkisi olduğunu sanmıyorum - geçersiz kıldığım aynı yöntem görünüyor. –

+1

@trashgod 2 numaralı cümlenin 1. maddesini kaldırmaya karar verdim * "EDT'yi (Olay Sevk İpliği) engellemeyin - GUI bu olduğunda 'donacaktır'." * Doğru olduğu sürece, burada geçerli görünmüyor. . –

İlgili konular