2015-02-08 62 views
0

我做了一個基於Java的遊戲,利用包含JApplet的JFrame,該JApplet又包含一個JPanel,在該JPanel上繪製圖形,但由於某種原因,圖形的左側偶爾會閃爍,弄清楚爲什麼。我想這可能是因爲我實際上並沒有使用EDT,我之後才意識到這一點,所以有人能夠告訴我如何正確地將EDT的使用整合到程序中?在JApplet中正確使用Event Dispatch Thread?

我的主類的樣子:

public class Main extends JFrame{ 

public static final int HEIGHT = 600; 
public static final int WIDTH = 800; 
public static RApplet app; 

public Main(){ 
    setTitle("RCrawl"); 
    Container c = getContentPane(); 
    c.setPreferredSize(new Dimension(WIDTH, HEIGHT)); 
    pack(); 
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
    setLocationRelativeTo(null); 
    setResizable(false); 
    final Player p = new Player(50, 70, true); 
    final Room r = new Room(0xff222244); 
    r.addEntity(new EntityRock(200, 150)); 
    app = new RApplet(WIDTH, HEIGHT); 
    app.setRoom(r); 
    app.setPlayer(p); 
    app.setFps(25); 
    add(app); 
    setVisible(true); 
    new UpdateThread().start(); 
    System.out.println("thread run"); 
} 

public static void main(String[] args){ 
    Main m = new Main(); 
} 

class UpdateThread extends Thread{ 
    public void run(){ 
     while(true)update(); 
    } 

    public void update(){ 
     app.refresh(); 
    } 
} 
} 

雖然RApplet類的樣子:

public class RApplet extends JApplet{ 
public int width, height, fps; 
public long curTime, delta; 
public RenderPanel panel; 
public Room curRoom; 
public Player player; 
public boolean isSingle; 
public InputHandler input; 

public RApplet(int x, int y){ 
    width = x; 
    height = y; 
    panel = new RenderPanel(width, height); 
    fps = 30; 
    isSingle = true; 
    input = new InputHandler(); 
    addKeyListener(input); 
    setFocusable(true); 
    add(panel); 
} 

public void setPlayer(Player p){ 
    player = p; 
    curRoom.addPlayer(p); 
} 

public void setFps(int f){ 
    fps = f; 
} 

public void initialize(){ 
    curTime = System.currentTimeMillis(); 
    delta = curTime; 
} 

public void refresh(){ 
    delta = (int)(System.currentTimeMillis() - curTime); 
    if(delta > 1000/fps){ 
     curTime = System.currentTimeMillis(); 
     render(); 
     tick(); 
    } 
} 

public void setRoom(Room r){ 
    curRoom = r; 
} 

public void render(){ 
    panel.renderStart(curRoom); 
    panel.renderRoom(curRoom); 
    panel.renderEnd(); 
    panel.repaint(); 
} 

public void tick(){ 
    curRoom.tick(this); 
} 
} 

所以我什麼希望,才能正常使用EDT辦?我已經嘗試了幾種使用不同組合的invokeLater()invokeAndWait(),但無法讓他們工作。如果你能幫助,任何幫助表示讚賞。

編輯:下面是來自RenderPanel渲染方法:

public void paintComponent(Graphics g){ 
    super.paintComponent(g); 
    Graphics2D g2 = (Graphics2D) g; 
    BufferedImage drawer = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 
    Graphics g1 = drawer.getGraphics(); 
    g1.drawImage(canvas, 0, 0, this); 
    g2.drawImage(drawer, 0, 0, this); 
    g1.dispose(); 
    g2.dispose(); 
} 

那不是雙緩衝?

+0

'',它利用了包含JApplet的JFrame「' - 爲什麼這個*非常*奇怪而脆弱的設計?這不應該是一個擁有JPanel的JFrame嗎? – 2015-02-08 15:12:31

+0

在將Applet移動到網頁之前,我正在使用JFrame對其進行測試。 – user2649681 2015-02-08 15:46:53

+1

1.不要在paintComponent方法中執行BufferedImage繪圖,即從'BufferedImage drawer = ....'到'g1.drawImage(...)'的所有內容。這應該在其他地方完成,可能在後臺線程中。另外,儘管可以放棄BufferedImage的Graphics對象g1,但是在完成它之後,不要處置由JVM提供給您的Graphics對象,即g2對象。這將打破Swing的繪畫樹。 – 2015-02-08 16:16:40

回答

0

調用重繪將在EDT上排隊事件。 。

如果你正在經歷閃爍,我會看看渲染方法實際需要多長時間來執行(也許渲染到BufferedImage離屏,並讓你的繪畫方法只繪製。),在你正在繪畫的bufferedimage以及在渲染完成時在屏幕上繪製的緩衝圖像。 (這被稱爲雙緩衝)。

你可以使用BufferStrategy使用(PS即時消息不是100%肯定,如果將用於小應用程序的工作,或者如果它需要一個完整的窗口。)

而這一切都是假設你沒有做任何愚蠢的事情,例如抓住圖形上下文被傳遞到paint方法中,然後在其他方法中使用它(比如你的渲染室方法),或者在這些方法的任何組件上調用「getGraphics()」。

檢查的懶惰方式你在哪個線程在您直接訪問圖形對象的方法打印出來

Thread.currentThread().getName() 

Thread.dumpStack(); 

+0

我添加了'paintComponent()'方法,是不是已經使用雙緩衝? – user2649681 2015-02-08 15:49:14

1

問題...

  • 小心更新從事件指派線程之外的比賽狀態,這可能會導致髒塗料作爲國家的一部分,是在油漆工藝更新
  • 不要處理您沒有創建的Graphics上下文,處理系統Graphics上下文將影響其他組件的繪製方式,請記住,您可能不是在繪製週期中唯一繪製的內容,上下文是共享資源
  • 因爲它的方式實施,repaint是一個線程安全的方法。繪畫事件發佈在Event Queue上,由Event Dispatching Thread處理,所以除非你做了一些可怕的錯誤(打印可能是規則的一個例外),繪畫將始終發生在Event Dispatching的上下文中線程
  • paintComponent內沒有任何關於BufferedImage的渲染,Swing組件已經是雙緩衝。如果你想實現頁面翻轉,那麼你應該畫多於一個BufferedImage,但應該更新EDT之外的屏幕外緩衝區
  • 代碼的基本結構沒有意義,你給了很多控制到JAppletJFrame,但也不應該有什麼,他們應該是沒有什麼比容器多爲RenderPanel

可能的解決方案...

開始通過簡化渲染/更新的過程。我認爲使用Swing Timer作爲更新過程的主要引擎,很簡單,它觸發了它在EDT內的更新,這意味着可以安全地更改遊戲狀態而不必擔心競爭條件和骯髒的繪畫。它可以讓你直接利用paintComponent方法。

當你得到基本的進程在運行,你可以探索更多的提前更新引擎(如使用Thread)和翻頁,technquies

改變你的程序的結構,從上面觀的要求分離的核心程序( JAppletJFrame應該不外乎容器多爲實際的程序,並且只包含足夠的邏輯來構造和顯示程序)

MVC

到它的進一步分離遊戲邏輯流沒有渲染邏輯的n個類。將控制器與渲染視圖分開。該API將信息提供給控制器(例如鼠標事件和關鍵事件),這將更新模型。控制器也可以作爲主要更新引擎,調度更新週期以便根據需要更新模型和視圖。

請參閱Model-View-Controller瞭解更多詳情。

+0

從這個角度來看,我不妨從頭開始吧,呵呵? – user2649681 2015-02-10 13:16:18

+0

至少有一點切割和粘貼;) – MadProgrammer 2015-02-10 20:09:41

相關問題