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

发布于 2024-09-02 12:38:41 字数 1583 浏览 2 评论 0原文

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

到目前为止,我有一个带有菜单栏的 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 类中?

I'm trying to make a simple 2D game in Java.

So far I have a JFrame, with a menubar, and a class which extends JPanel and overrides it's paint method. Now, I need to get a game loop going, where I will update the position of images and so on. However, I'm stuck at how best to achieve this. Should I use multi-threading, because surely, if you put an infinite loop on the main thread, the UI (and thus my menu bar) will freeze up?

Here's my code so far:

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);
    }
}

Any pointers or tips? Where should I put my loop? In my main class, or my GameCanvas class?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

み零 2024-09-09 12:38:41

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

你能做的最好的事情就是让游戏循环保持在它自己的线程中。每当您想在 GUI 中进行更改时,请使用 SwingWorker 或小孩子现在使用的任何东西将其强制到 GUI 线程上以执行该操作。

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

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

Your game loop (model) should not be anywhere near any of your GUI classes (view). It uses your GUI classes--but even that you probably want to do through an intermediary (controller). A good way to ensure that you are doing it right is to check that your model doesn't have a single "include javax.swing.???".

The best thing you could do is to keep the game loop in it's own thread. Whenever you want to make a change in the GUI, use SwingWorker or whatever the young kids use now to force it onto the GUI thread for just that one operation.

This is actually awesome because it makes you think in terms of GUI Operations (which would constitute your controller). For instance, you might have a class called "Move" that would have the GUI logic behind a move. Your game loop might instantiate a "Move" with the right values (item to move, final location) and pass it to a GUI loop for processing.

Once you get to that point, you realize that simply adding a trivial "undo" for each GUI operation allows you to easily undo any number of operations. You will also find it easier to replace your Swing GUI with a web GUI...

々眼睛长脚气 2024-09-09 12:38:41

您需要一个用于游戏循环的线程和一个用于处理 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    
    }
}

You need one thread for you game loop and one thread to handle Swing UI events like mouse clicks and keypresses.

When you use the Swing API, you automatically get an additional thread for your UI called the event dispatch thread. Your callbacks are executed on this thread, not the main thread.

Your main thread is fine for your game loop if you want the game to start automatically when the programs runs. If you want to start and stop the game with a Swing GUI, then have then main thread start a GUI, then the GUI can create a new thread for the game loop when the user wants to start the game.

No, your menu bar will not freeze up if you put your game loop in the main thread. Your menu bar will freeze up if your Swing callbacks take a long time to finish.

Data that is shared between the threads will need to be protected with locks.

I suggest you factor your Swing code into its own class and only put your game loop inside your main class. If you're using the main thread for your game loop, this is a rough idea of how you could design it.

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    
    }
}
空‖城人不在 2024-09-09 12:38:41

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

Java is really suited for event-driven programming. Basically, set up a timer event and listen. At each 'tick-tock' you update your game logic. At each GUI-event you update your data structures that the game logic method will read.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文