10

我正在尝试用 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 课上?

4

3 回答 3

13

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

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

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

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

于 2010-05-18T23:46:57.520 回答
6

您需要一个线程用于游戏循环和一个线程来处理 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    
    }
}
于 2010-05-21T00:02:54.037 回答
4

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

于 2010-05-18T23:51:37.623 回答