[英]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.