簡體   English   中英

如何(正確)在純Java中以高幀速率渲染

[英]How do I (properly) render at a high frame rate in pure Java

我一直在嘗試用純java制作一個簡單的游戲,我在繪圖時遇到了問題。 我試圖保持相對較高的幀速率,但是JFrame.repaint()不能被“強制”這一事實存在問題,而且僅僅是在下一個可用機會重繪幀的請求。 結果,下面代碼的幀速率很糟糕。 然而,(這是一個奇怪的部分)當我的鼠標不動時似乎只是可怕。 如果我的鼠標移動並且在窗口頂部,則幀速率快速且清晰。

我已經嘗試了各種在線建議甚至編譯了如何做到這一點的例子,當鼠標沒有在窗口上移動時,它們似乎都有同樣的問題,幀速率急劇下降。

(我正在使用Linux,如果這很重要)

任何和所有的幫助非常感謝!

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


public class Test extends JPanel {


  public static void main(String[] args) {

    JFrame frame = new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setSize(new Dimension(300, 300));
    frame.setVisible(true);
    frame.getContentPane().add(new Test());


    for (int k = 0; k < 1_000_000; k++) {
      frame.repaint();
      try {
        Thread.sleep(10);
      } catch (InterruptedException e) {
        e.printStackTrace();
        System.exit(1);
      }
    }

    frame.dispose();
    System.exit(0);
  }



  private int k = 0;

  public Test() {
    super();
  }

  @Override public void paintComponent(Graphics g) {
    super.paintComponent(g);

    g.setColor(Color.WHITE);
    g.fillRect(0, 0, getWidth(), getHeight());

    g.setColor(Color.BLACK);
    int height = (int) (((k * 0.01) % 1) * getHeight());
    g.drawLine(
      0, height,
      getWidth(), height
      );

    k++;

  }

}

經過太多的研究,結果證明java在許多Linux系統上不會自動同步/刷新顯示緩沖區。 所有的重繪都是正確的,但顯示緩沖區沒有沖洗,因此產生了奇怪的滯后效應。

解決方案:

Toolkit toolkit = Toolkit.getDefaultToolkit(); /* get AWT toolkit */

/* initialize things */
...

while (your_loop) {

  /* run your logic */
  ...

  /* paint everything */
  ...

  toolkit.sync(); /* force display buffer to flush */
}

謝謝大家的意見

問題並不簡單。 下面的代碼尚未經過測試,只是為了給你一個想法......在下一行中,AWT是Swing的基礎。

首先,你必須保持你的paintComponent()非常快(確實!)。 這是第一個要求。 基本上,對於60 fps,您必須在不到15毫秒的時間內繪制。 忘記transaparency和其他東西(在Windows上工作很糟糕,我不知道Linux)。 盡可能嘗試保存計算。

其次,在不同的線程中執行其他所有操作。 這是我用於自己程序的方式。 注意每次調用AWT(包括Swing,當然)都必須封裝在對EventQueue.invokeLater()的調用中,以確保你在AWT線程中運行東西,因為設置標簽絕不能在AWT線程之外完成。

當您從AWT收到需要時間的輸入時,不要忘記創建一個線程!

第三,用像這樣的計時器替換你的循環

new Timer("Drawer", true).scheduleAtFixedRate( new TimerTak(){
    public void run(){
       frame.repaint();
    }
}, 
100, // Start in 100 ms
(int)(1000 / 60)); // 60 is the frame rate.

一切都應該順利進行。 對於幀計數k ,請使用以下內容:

// You should initialize just before you create the timer...!
static private long startedAt = System.currentTimeMillis();

@Override public void paintComponent(Graphics g) {
    super.paintComponent(g);

    // Microseconds since the game started.
    long k = (System.currentTimeMillis() - startedAt);

    // Increment only one by frame (60 fps)
    k = (int)((double)k * 60 / 1000.0)

    // Draw the game...!      
}

就這樣。 請注意,如果計算機功能不夠強大(或者需要CPU密集型,或垃圾收集器......),則可以刪除某些幀。 但是,如果可能,您的游戲將以最高60 fps的速度運行。

額外獎勵:如果每次通過paintComponent()時增加一個值,就可以找到自游戲開始以來真正顯示的丟幀數或平均每秒幀數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM