簡體   English   中英

為什么我繪制的圖像在JPanel上閃爍?

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

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