2009-12-26 92 views
15

下學期我們有一個團隊製作的Java應用程序的模塊。模塊的要求是製作遊戲。在聖誕假期,我一直在做一些練習,但我無法弄清楚繪製圖形的最佳方式。的Java 2D遊戲的圖形

我使用Java Graphics2D對象在屏幕上繪製形狀,並每秒調用repaint() 30次,但是這種閃爍非常厲害。在Java中繪製高性能2D圖形有更好的方法嗎?

回答

16

你想要做的是創建一個帶有BufferStrategy的畫布組件,並渲染到下面的代碼應該告訴你它是如何工作的,我已經從我自己寫的引擎中提取了代碼,覆蓋了here

表演完全取決於你想畫的東西,我的遊戲大多使用圖片。其中約1500人,我仍然在480x480以上200 FPS以上。在禁用幀限制的情況下,只有100張圖像,我擊中6k FPS。

一個小遊戲(這個屏幕有一次約120圖像)我已經創建,可以發現here(下面是也是辦法正常工作作爲applet)

import java.awt.Canvas; 
import java.awt.Color; 
import java.awt.Graphics2D; 
import java.awt.GraphicsConfiguration; 
import java.awt.GraphicsEnvironment; 
import java.awt.Toolkit; 
import java.awt.Transparency; 
import java.awt.event.WindowAdapter; 
import java.awt.event.WindowEvent; 
import java.awt.image.BufferStrategy; 
import java.awt.image.BufferedImage; 

import javax.swing.JFrame; 
import javax.swing.WindowConstants; 

public class Test extends Thread { 
    private boolean isRunning = true; 
    private Canvas canvas; 
    private BufferStrategy strategy; 
    private BufferedImage background; 
    private Graphics2D backgroundGraphics; 
    private Graphics2D graphics; 
    private JFrame frame; 
    private int width = 320; 
    private int height = 240; 
    private int scale = 1; 
    private GraphicsConfiguration config = 
      GraphicsEnvironment.getLocalGraphicsEnvironment() 
       .getDefaultScreenDevice() 
       .getDefaultConfiguration(); 

    // create a hardware accelerated image 
    public final BufferedImage create(final int width, final int height, 
      final boolean alpha) { 
     return config.createCompatibleImage(width, height, alpha 
       ? Transparency.TRANSLUCENT : Transparency.OPAQUE); 
    } 

    // Setup 
    public Test() { 
     // JFrame 
     frame = new JFrame(); 
     frame.addWindowListener(new FrameClose()); 
     frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 
     frame.setSize(width * scale, height * scale); 
     frame.setVisible(true); 

     // Canvas 
     canvas = new Canvas(config); 
     canvas.setSize(width * scale, height * scale); 
     frame.add(canvas, 0); 

     // Background & Buffer 
     background = create(width, height, false); 
     canvas.createBufferStrategy(2); 
     do { 
      strategy = canvas.getBufferStrategy(); 
     } while (strategy == null); 
     start(); 
    } 

    private class FrameClose extends WindowAdapter { 
     @Override 
     public void windowClosing(final WindowEvent e) { 
      isRunning = false; 
     } 
    } 

    // Screen and buffer stuff 
    private Graphics2D getBuffer() { 
     if (graphics == null) { 
      try { 
       graphics = (Graphics2D) strategy.getDrawGraphics(); 
      } catch (IllegalStateException e) { 
       return null; 
      } 
     } 
     return graphics; 
    } 

    private boolean updateScreen() { 
     graphics.dispose(); 
     graphics = null; 
     try { 
      strategy.show(); 
      Toolkit.getDefaultToolkit().sync(); 
      return (!strategy.contentsLost()); 

     } catch (NullPointerException e) { 
      return true; 

     } catch (IllegalStateException e) { 
      return true; 
     } 
    } 

    public void run() { 
     backgroundGraphics = (Graphics2D) background.getGraphics(); 
     long fpsWait = (long) (1.0/30 * 1000); 
     main: while (isRunning) { 
      long renderStart = System.nanoTime(); 
      updateGame(); 

      // Update Graphics 
      do { 
       Graphics2D bg = getBuffer(); 
       if (!isRunning) { 
        break main; 
       } 
       renderGame(backgroundGraphics); // this calls your draw method 
       // thingy 
       if (scale != 1) { 
        bg.drawImage(background, 0, 0, width * scale, height 
          * scale, 0, 0, width, height, null); 
       } else { 
        bg.drawImage(background, 0, 0, null); 
       } 
       bg.dispose(); 
      } while (!updateScreen()); 

      // Better do some FPS limiting here 
      long renderTime = (System.nanoTime() - renderStart)/1000000; 
      try { 
       Thread.sleep(Math.max(0, fpsWait - renderTime)); 
      } catch (InterruptedException e) { 
       Thread.interrupted(); 
       break; 
      } 
      renderTime = (System.nanoTime() - renderStart)/1000000; 

     } 
     frame.dispose(); 
    } 

    public void updateGame() { 
     // update game logic here 
    } 

    public void renderGame(Graphics2D g) { 
     g.setColor(Color.BLACK); 
     g.fillRect(0, 0, width, height); 
    } 

    public static void main(final String args[]) { 
     new Test(); 
    } 
} 
+0

謝謝!這非常有趣。 FPS限制也。你做的遊戲非常好! – 2009-12-26 17:18:15

+0

有趣的是,strategy.show()可以安全地從美國東部時間之外打電話嗎? – Pool 2009-12-26 17:29:18

+0

第二個線程短測驗說是的,這是安全的。對於try/catch,這只是因爲Toolkit.getDefaultToolkit()。sync()可能會在極少數情況下拋出異常。 – 2009-12-26 17:55:01

3

Java OpenGLJOGL)是一種方式。

+0

JOGL很好,但我懷疑我可以說服團隊的其他成員使用它。該團隊在所有技能水平種子,而我'#是那種人誰使他們的遊戲打發時間和寫入樂趣併發代碼,其他人組會希望讓事情儘可能簡單(不幸) – Martin 2009-12-26 14:48:03

2

我認爲你從paint(Graphics g)做了一個覆蓋?這不是好的方法。使用相同的代碼,但在paintComponent(Graphics g)而不是paint(Graphics g)

您可以搜索的標籤是doublebuffer。這是通過覆蓋paintComponent自動完成的。

+0

所以我可以從字面上只是將代碼從油漆複製到塗料成份,一切都將工作一樣,除了它會被加倍緩衝? – Martin 2009-12-26 14:48:55

+0

是的,這就是我的意思。盛宴的答案描述了發生的事情。但Java已經有了一個解決方案內建。盛宴所做的只是避免使用'paintComponent'並製作他自己的解決方案。 – 2009-12-26 15:49:08

8

的閃爍是因爲你直接寫在屏幕上。使用一個緩衝區來繪製,然後寫入整個屏幕。這是你以前可能聽說過的Double BufferingHere是最簡單的形式。

public void paint(Graphics g) 
{ 

    Image image = createImage(size + 1, size + 1); 
    Graphics offG = image.getGraphics(); 
    offG.setColor(Color.BLACK); 
    offG.fillRect(0, 0, getWidth(), getHeight()); 
    // etc 

看到使用戲外圖形offG的。創建屏幕外圖片的成本很高,所以我建議只在第一次通話時創建它。

還有其他方面可以進一步提高,例如creating a compatible image,使用clipping等。對於動畫更精細的調整控制,你應該看看active rendering

有一個體面的頁面我已經書籤討論遊戲教程here

祝你好運!

0

有一個簡單的方法來優化你的程序。擺脫任何複雜的代碼,只需使用JComponent代替Canvas並在其上繪製的對象。就這樣。享受它...