2015-12-02 151 views
0

對於我的遊戲,我畫了一個java.awt.Image然後將圖像繪製到JPanel上。我這樣做的原因有兩個,主要是因爲我不希望遊戲渲染在EDT上佔用CPU週期,並且爲了便攜性。JPanel閃爍問題

的問題arised造成使用

圖形#的drawImage(圖像IMG,INT的x,INT Y,INT寬度,INT高度, ImageObserver的觀察者)

當在java.awt.JPanel閃爍。

然而,

圖形#的drawImage(Image img時,INT X,INT Y,ImageObserver的觀察者)

並未導致此問題。

這裏是我的代碼:

Sandbox.java

public class Sandbox implements Paintable { 

public static void main(String[] args) throws Exception { 
    GameWindow window = new GameWindow("Test", 800, 600); 
    GameScreen screen = new GameScreen(800, 600); 
    Sandbox sandbox = new Sandbox(); 

    window.add(screen); 
    window.setVisible(true); 

    boolean running = true; 
    while(running) { 
     sandbox.update(); 
     screen.getPaintBuffer().clear(); 
     screen.getPaintBuffer().paint(sandbox); 
     screen.repaint(); 
     Thread.sleep(1000/60); 
    } 

} 

private int x = 0, y = 0; 

public void update() { 
    x++; 
    y++; 
} 

public void paint(Graphics g) { 
    g.drawRect(x, y, 50, 50); 
} 

} 

GameWindow.java

public class GameWindow extends JFrame { 

public GameWindow(String title, int width, int height) { 
    setTitle(title); 
    setSize(width, height); 
    setResizable(false); 
    setLocationByPlatform(true); 
    setDefaultCloseOperation(3); 
} 

} 

GameScreen.java

public class GameScreen extends JPanel { 

private ImageBuffer buffer; 

public GameScreen(int width, int height) { 
    buffer = new ImageBuffer(width, height); 
} 

public void paint(Graphics g) { 
    g.drawImage(getPaintBuffer().getBuffer(), 0, 0, getWidth(), getHeight(), null); 
} 

public ImageBuffer getPaintBuffer() { 
    return buffer; 
} 

} 

Paintable.java

public interface Paintable { 

public void paint(Graphics g); 

} 

ImageBuffer.java

public class ImageBuffer { 

private final Image buffer; 
private int width, height; 

public ImageBuffer(int width, int height) { 
    buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 
    this.width = width; 
    this.height = height; 
} 

public void paint(Paintable paintable) { 
    paintable.paint(buffer.getGraphics()); 
} 

public void clear() { 
    buffer.getGraphics().clearRect(0, 0, width, height); 
} 

public Image getBuffer() { 
    return buffer; 
} 

} 
+0

我想說的僥倖,一個工作,另一個沒有 – MadProgrammer

+0

Flukes發生一次,他們不容易重現,這個錯誤是。 –

+0

伴侶,如果你對此有信心,那就提交一個bug報告 – MadProgrammer

回答

4

更改GameScreenpaint方法...

  1. 覆蓋paintComponent代替
  2. 呼叫super.paintComponent爲了維持塗料鏈的合同
  3. 通行證thisImageObsever參數drawImage

例如...

public class GameScreen extends JPanel { 

    private ImageBuffer buffer; 

    public GameScreen(int width, int height) { 
     buffer = new ImageBuffer(width, height); 
    } 

    @Override 
    protected void paintComponent(Graphics g) { 
     super.paintComponent(g); 
     g.drawImage(getPaintBuffer().getBuffer(), 0, 0, getWidth(), getHeight(), this); 
    } 

    public ImageBuffer getPaintBuffer() { 
     return buffer; 
    } 

} 

看一看Painting in AWT and SwingPerforming Custom Painting瞭解更多詳細的繪畫是如何工作的

更新

基本問題是縮放問題...

您使用的圖像是800x600,但GameScreen實際上是794x572(在我的PC上),這會導致圖像縮放。現在,Swing的默認縮放並不漂亮,並且基於速度而不是質量。

現在,有許多可以改進這種方法,但一個快速的方法是採用一些更高呈現提示,例如

@Override 
protected void paintComponent(Graphics g) { 
    super.paintComponent(g); 
    Graphics2D g2d = (Graphics2D) g.create(); 
    g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); 
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
    g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); 
    g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); 
    g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); 
    g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 
    g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); 
    g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); 
    g2d.drawImage(getPaintBuffer().getBuffer(), 0, 0, getWidth(), getHeight(), this); 
    g2d.dispose(); 
} 

現在,我已經與這個過火,所以您可能要刪除一些,看看有什麼變化

更新

呈現提示可以減慢渲染過程,所以更好的解決辦法可能覆蓋getPreferredSize方法的GameScreen並返回預期大小

public static class GameScreen extends JPanel { 

    private ImageBuffer buffer; 
    private Dimension size; 

    public GameScreen(int width, int height) { 
     buffer = new ImageBuffer(width, height); 
     this.size = new Dimension(width, height); 
    } 

    @Override 
    public Dimension getPreferredSize() { 
     return new Dimension(800, 600); 
    } 

然後,而不是通過大小GameWindow,只需撥打pack

GameWindow window = new GameWindow("Test"); 
GameScreen screen = new GameScreen(800, 600); 
Sandbox sandbox = new Sandbox(); 

window.add(screen); 
window.pack(); 
window.setVisible(true); 

這樣一來,每個人都在相同的尺寸

+0

我試過用paintComponent來代替,它不會改變任何東西。我不想使用ImageObserver,反正這也不相關。另外我也不會調用super.paint(g),因爲我不打算在這個類中添加任何組件。 無論哪種方式,這些更改是不相關的,並且無法解決我的問題。 –

+0

@ChrisGray繪畫更復雜,然後只是「繪畫」組件。你有責任保持油漆連鎖店,否則奇怪和美妙的事情可能會出錯。 'ImageObsever'提供了一些重新獲得的信息給API,所以組件可以在'Image'改變時自行更新。這兩種做法都是「好做法」 – MadProgrammer

+0

我明白這一點。我一直在揮杆和AWT畫了很長時間。現在,這個圖像被用作緩衝區,我不希望它被顯示或重繪到屏幕上,直到它完成。每次更改圖像時更新JPanel都是浪費處理能力,不需要它。有些時候,「良好做法」的事情應該被忽視。 –