在不冻结UI线程的情况下实现游戏循环的最佳方法

我正在尝试用Java制作一个简单的2D游戏。

到目前为止,我有一个带有菜单栏的JFrame ,以及一个扩展JPanel并覆盖它的paint方法的类。 现在,我需要进行游戏循环,在那里我将更新图像的位置等等。 但是,我坚持如何最好地实现这一目标。 我应该使用multithreading,因为当然,如果你在主线程上放置一个无限循环,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课程?

你需要一个线程为你的游戏循环和一个线程来处理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 } } 

您的游戏循环(模型)不应该在任何GUI类(视图)附近。 它使用您的GUI类 – 但即使您可能想要通过中介(控制器)。 确保您正确执行的一个好方法是检查您的模型没有单个“include javax.swing。???”。

你能做的最好的事情就是让游戏循环保持在自己的线程中。 无论何时想要在GUI中进行更改,都可以使用SwingWorker或者小孩现在使用的任何内容来强制它进入GUI线程以进行一次操作。

这实际上很棒,因为它让你考虑GUI操作(构成你的控制器)。 例如,您可能有一个名为“Move”的类,它具有移动后面的GUI逻辑。 您的游戏循环可能会使用正确的值(要移动的项目,最终位置)实例化“移动”,并将其传递给GUI循环进行处理。

一旦达到这一点,您就会意识到只需为每个GUI操作添加一个简单的“撤消”,就可以轻松撤消任意数量的操作。 您还会发现使用Web GUI替换Swing GUI更容易……

Java非常适合事件驱动编程 。 基本上,设置一个计时器事件并监听。 在每个’tick-tock’,你更新你的游戏逻辑。 在每个GUI事件中,您都会更新游戏逻辑方法将读取的数据结构。