[英]Why is my drawn image flickering on a JPanel?
完整的源代碼在這個地址的 Github上。
我有一個JPanel,我在其中繪制一個背景和玩家精靈。 邏輯和重新繪制在不同的時間尺度上。 這是相關代碼:
static long logicSleep = (long) (1e9 / 120);
static long drawSleep = (long) (1e9 / 60);
static long startTime;
static long logicTime, drawTime;
public static void main(String[] args) {
startTime = System.nanoTime();
while (keys[KeyEvent.VK_ESCAPE] == false) {
if (System.nanoTime() > logicTime + startTime) {
player.logic();
logicTime += logicSleep;
}
if (System.nanoTime() > drawTime + startTime) {
sheet.repaint();
drawTime += drawSleep;
}
}
System.exit(0);
}
因此,邏輯每1/120秒運行一次,繪圖每1/60秒運行一次。 這按預期工作。 但是,圖像經常閃爍。 這不是屏幕撕裂,因為我有Java 8並且我的顯示器以60 Hz刷新(更不用說默認情況下如何在jpanel上啟用雙緩沖)。 閃爍似乎是位置的突然變化,盡管屏幕上角色的實際位置僅受到大約一個幀的影響。
這是我的自定義JPanel類:
static class Sheet extends JPanel {
@Override
public void paintComponent(Graphics g1) {
super.paintComponent(g1);
Graphics2D g = (Graphics2D) g1;
g.translate((int) -(player.xPos + player.sprites[0].getWidth() / 2 - getWidth() / 2),
(int) -(player.yPos + player.sprites[0].getHeight() / 2 - getHeight() / 2));
g.drawImage(map, 0, 0, null);
g.drawImage(player.sprites[player.spriteNum], (int) player.xPos, (int) player.yPos, null);
g.drawImage(foreground, 0, 0, null);
}
}
通過將邏輯睡眠改為每1/180秒運行一次,我設法解決了這個問題,但我很好奇為什么在特定時間尺度下運行邏輯會發生這種情況。 每1/240秒邏輯更新也會發生這種情況。
TL; DR:我的精靈因為我的邏輯滴答而閃爍,我不知道為什么。
你的主循環是一個忙等待循環。 它使CPU核心完全忙於進行比較( while
條件和時序條件)。 這會浪費CPU,使您的系統響應性降低,通常不建議使用。 這本身可能會對其他線程的調度產生影響並導致閃爍。
此外,您選擇了具有公分母(即60和180,60和120等)的更新時間。 這意味着存在應該觸發兩個條件的循環。 如果您的邏輯計算很長,則意味着重繪將延遲這么長時間。 這可能是閃爍的另一個原因。
執行定期任務的正確方法是使用Timer
對象。 由於您有兩個不同的時序循環,並且您需要它們是獨立的,因此您應該使用兩個單獨的Timer
實例。
對於Timer
類,通常有兩種選擇:
javax.swing.Timer
- 在Event Dispatch Thread中運行計划的操作。 這對於像repaint()
這樣的操作很有用,並且允許進行短邏輯操作,並且由於所有操作都在同一個線程中運行,因此不需要揮發性或其他方法來確保內存可見性。 java.util.Timer
(或者, java.util.concurrent.ScheduledThreadPoolExecutor
) - 適合安排非GUI,更復雜的操作,你不想在EDT上運行。 如果使用此方法,則應注意使用SwingUtility
方法更新GUI組件,並使用volatile
或其他方法對EDT可見的數據進行更改,因為它們將在不同的線程中運行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.