Лучший способ реализовать игровой цикл, не замораживая поток UI

Я пытаюсь сделать простую 2D игру в Java.

До сих пор у меня есть a 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?

9
задан Jonas 4 February 2012 в 23:25
поделиться

3 ответа

Вам нужен один поток для игрового цикла и один поток для обработки событий пользовательского интерфейса Swing, таких как щелчки мыши и нажатия клавиш.

Когда вы используете Swing API, вы автоматически получаете дополнительный поток для вашего пользовательского интерфейса, называемый потоком диспетчеризации событий. Ваши обратные вызовы выполняются в этом потоке, а не в основном потоке.

Ваш основной поток подходит для игрового цикла, если вы хотите, чтобы игра запускалась автоматически при запуске программ. Если вы хотите запускать и останавливать игру с помощью 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    
    }
}
5
ответ дан 4 December 2019 в 10:30
поделиться

Java действительно подходит для программирования, управляемого событиями . По сути, установите событие таймера и слушайте. С каждым тактом вы обновляете свою игровую логику. При каждом GUI-событии вы обновляете свои структуры данных, которые будет читать метод игровой логики.

4
ответ дан 4 December 2019 в 10:30
поделиться

Ваш игровой цикл (модель)не должно находиться рядом ни с каким из классов графического интерфейса пользователя (вид). Он использует ваши классы GUI, но даже это вы, вероятно, хотите сделать через посредника (контроллера). Хороший способ убедиться, что вы делаете это правильно, — это проверить, что в вашей модели нет ни одного «include javax.swing.???».

Лучшее, что вы можете сделать, это сохранить цикл игры в своем собственном потоке. Всякий раз, когда вы хотите внести изменения в графический интерфейс, используйте SwingWorker или что-то еще, что маленькие дети используют сейчас, чтобы принудительно вставить его в поток графического интерфейса только для этой одной операции.

Это на самом деле потрясающе, потому что это заставляет вас думать с точки зрения операций с графическим интерфейсом (который будет составлять ваш контроллер). Например, у вас может быть класс с именем «Move», который будет иметь логику графического интерфейса пользователя за перемещением. Ваш игровой цикл может создать экземпляр «Move» с правильными значениями (предмет для перемещения, окончательное местоположение) и передать его в цикл графического интерфейса для обработки.

Как только вы дойете до этой точки,Вы понимаете, что простое добавление тривиальной «отмены» для каждой операции графического интерфейса пользователя позволяет легко отменить любое количество операций. Вам также будет проще заменить свой графический интерфейс Swing на веб-графический интерфейс ...

11
ответ дан 4 December 2019 в 10:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: