当前位置: 首页 > 面试题库 >

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

郎星汉
2023-03-14
问题内容

我试图用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班级?


问题答案:

您需要一个线程进行游戏循环,并需要一个线程来处理诸如鼠标单击和按键之类的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    
    }
}


 类似资料:
  • 问题内容: 我正在用cardLayout更改“视图”(此类有一个变量)。当用户单击新游戏按钮时,会发生以下情况: 游戏的循环方法和类标题: 我在用画 实际上,它没有画任何东西。当我不调用方法(因此它仅绘制一次)时,所有图像均正确绘制。但是当我调用方法时,窗口中什么也没有发生。(即使关闭按钮也不起作用。) 如何解决?(当我在游戏课程中创建游戏时,一切正常,但是现在我想拥有更多视图,因此我需要在其他课

  • 问题内容: 我正在做一个游戏,我需要每3秒更新一次JProgressBar。为此,我使用while循环。问题是我的程序由于while循环而冻结(我在其他问题中读过它,它们没有帮助我解决此问题)。我不知道该怎么解决。这是我的代码: 你能帮我吗? 问题答案: 您应该在自己的线程中运行循环: 顺便说一句:如果您没有在方法中使用“ String [] args”,则没有理由在方法中声明它。

  • 我的大学教授给我布置了一个练习,内容如下: “Geofence对象是一个对象,它有一个对象集合,可以在这些对象发出信号时等待。有一个add(object)方法,它将对象添加到集合中。还有一个await()方法:它允许等待集合中的任何对象发出信号。每当调用add(object)方法时,await()方法处于活动状态时,add的参数将放入队列中。使用以下接口编写源代码:“。 因此,只有当调用相同数量的

  • 本食谱演示了使用组合流来创建游戏循环的一种方式。本食谱旨在突出如何用响应式的方式来重新思考现有问题。在这个示例中,我们将提供整体循环以及自上帧以来的增量时间。与此相结合的是用户输入流,以及当前的游戏状态,我们可以用它来更新我们的对象,并根据每帧的发出来将其渲染到屏幕上。 示例代码 ( StackBlitz ) import { BehaviorSubject, Observable, of, fr

  • 问题内容: 在编写动画和小游戏时,我已经知道我依靠这种方法告诉操作系统什么时候我的应用程序不需要任何CPU,并以此来使程序以可预测的速度前进,这具有不可思议的重要性。 我的问题是JRE在不同的操作系统上使用了不同的方法来实现此功能。在基于UNIX(或受影响的)的OS:例如Ubuntu和OS X上,底层的JRE实现使用功能良好且精确的系统将CPU时间分配给不同的应用程序,从而使我的2D游戏流畅无延迟

  • 我有一个用例,在这个用例中,我有3个Kafka消费者向一个主题写作,每个消费者中的消息都需要按顺序处理。在这种情况下,如果某个消费者中存在延迟,则需要更早处理的消息将被丢弃(写入条件)。那么,有没有一种方法可以维持这些消息的顺序呢。