2010-05-18 47 views
9

我想在Java中製作一個簡單的2D遊戲。實現遊戲循環而不會凍結UI線程的最佳方式

到目前爲止,我有一個JFrame,有一個菜單欄和一個延伸JPanel並覆蓋它的paint方法的類。現在,我需要獲得一個遊戲循環,在那裏我會更新圖像的位置等等。但是,我堅持如何最好地實現這一目標。我是否應該使用多線程,因爲當然,如果您在主線程上放置一個無限循環,UI(以及我的菜單欄)會凍結?

這裏是我到目前爲止的代碼:

import java.awt.Color; 
import java.awt.Graphics; 

import javax.swing.JPanel; 

@SuppressWarnings("serial") 
public class GameCanvas extends JPanel { 

    public void paint(Graphics g) { 
     while (true) { 
      g.setColor(Color.DARK_GRAY); 
      try { 
       Thread.sleep(100); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

import javax.swing.JFrame; 
import javax.swing.JMenu; 
import javax.swing.JMenuBar; 
import javax.swing.JMenuItem; 

@SuppressWarnings("serial") 
public class Main extends JFrame { 

    GameCanvas canvas = new GameCanvas(); 
    final int FRAME_HEIGHT = 400; 
    final int FRAME_WIDTH = 400; 

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

    public Main() { 
     super("Game"); 

     JMenuBar menuBar = new JMenuBar(); 
     JMenu fileMenu = new JMenu("File"); 
     JMenuItem startMenuItem = new JMenuItem("Pause"); 
     menuBar.add(fileMenu); 
     fileMenu.add(startMenuItem); 

     super.add(canvas); 
     super.setVisible(true); 
     super.setSize(FRAME_WIDTH, FRAME_WIDTH); 
     super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     super.setJMenuBar(menuBar); 
    } 
} 

任何指針或建議嗎?我應該把我的循環放在哪裏?在我的主類或我的GameCanvas類中?

回答

5

你需要一個線程爲你的遊戲循環和一個線程來處理像鼠標點擊和按鍵的Swing UI事件。

當您使用Swing API時,會自動爲您的UI獲取一個稱爲事件分派線程的附加線程。你的回調在這個線程上執行,而不是在主線程上執行。

如果您希望遊戲在程序運行時自動啓動,您的主線程適用於您的遊戲循環。如果你想用Swing GUI啓動和停止遊戲,那麼當主線程啓動一個GUI時,GUI可以在用戶想要啓動遊戲時爲遊戲循環創建一個新線程。

不,你的菜單欄不會凍結,如果你把你的遊戲循環放在主線程中。如果您的Swing回調需要很長時間才能完成,您的菜單欄將凍結。

線程之間共享的數據需要用鎖保護。

我建議你把你的Swing代碼放到它自己的類中,只把你的遊戲循環放到你的主類中。如果你正在使用遊戲循環的主線程,這是你如何設計它的粗略想法。

import javax.swing.JFrame; 
import javax.swing.JMenu; 
import javax.swing.JMenuBar; 
import javax.swing.JMenuItem; 

class GUI extends JFrame { 
    GameCanvas canvas = new GameCanvas(); 
    final int FRAME_HEIGHT = 400; 
    final int FRAME_WIDTH = 400; 


    public GUI() { 
     // build and display your GUI 
     super("Game"); 

     JMenuBar menuBar = new JMenuBar(); 
     JMenu fileMenu = new JMenu("File"); 
     JMenuItem startMenuItem = new JMenuItem("Pause"); 
     menuBar.add(fileMenu); 
     fileMenu.add(startMenuItem); 

     super.add(canvas); 
     super.setVisible(true); 
     super.setSize(FRAME_WIDTH, FRAME_WIDTH); 
     super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     super.setJMenuBar(menuBar); 
    } 
} 

public class Main { 

    public static void main(String args[]) { 
     GUI ui = new GUI(); // create and display GUI   

     gameLoop(); // start the game loop 
    } 

    static void gameLoop() { 
     // game loop  
    } 
} 
11

您的遊戲循環(模型)不應該在您的任何GUI類(視圖)附近的任何地方。它使用你的GUI類 - 但即使你想通過中介(控制器)來做。確保你做得對的一個好方法是檢查你的模型沒有一個「include javax.swing。???」。

你可以做的最好的事情就是讓遊戲循環保持在它自己的線程中。無論何時您想在GUI中進行更改,都可以使用SwingWorker或任何年輕孩子現在使用它將其強制加載到GUI線程上以進行這一操作。

這實際上很棒,因爲它讓你用GUI操作(這將構成你的控制器)來思考。例如,您可能有一個名爲「移動」的類,該移動將具有GUI邏輯。您的遊戲循環可能會以正確的值(移動的項目,最終位置)實例化一個「移動」,並將其傳遞給GUI循環進行處理。

一旦你明白了這一點,你意識到只需爲每個GUI操作添加一個簡單的「撤消」就可以輕鬆地撤銷任何操作次數。您還會發現用Web GUI更換Swing GUI更容易...

4

Java非常適合event-driven programming。基本上,設置一個計時器事件並收聽。在每個'嘀嗒'中,你都會更新你的遊戲邏輯。在每次GUI事件中,您都會更新遊戲邏輯方法將讀取的數據結構。