2010-05-18 48 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事件中,您都会更新游戏逻辑方法将读取的数据结构。