2010-05-18 2 views
9

Ich versuche, ein einfaches 2D-Spiel in Java zu machen.Der beste Weg, um Game-Loop ohne Einfrieren von UI-Thread zu implementieren

Bisher habe ich eine JFrame, mit einer Menüleiste, und eine Klasse, die JPanel erweitert und überschreibt es ist paint Methode. Jetzt muss ich eine Spielschleife machen, wo ich die Position der Bilder aktualisieren werde und so weiter. Ich bin jedoch fest, wie das am besten zu erreichen ist. Soll ich Multithreading verwenden, weil die UI (und damit meine Menüleiste) sicher einfriert, wenn Sie eine Endlosschleife auf den Hauptthread setzen?

Hier ist mein Code so weit:

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

Alle Hinweise oder Tipps? Wo sollte ich meine Schleife setzen? In meiner Hauptklasse oder in meiner GameCanvas-Klasse?

Antwort

5

Sie benötigen einen Thread für Ihre Spielschleife und einen Thread, um Swing UI-Ereignisse wie Mausklicks und Tastendruck zu verarbeiten.

Wenn Sie die Swing-API verwenden, erhalten Sie automatisch einen zusätzlichen Thread für Ihre Benutzeroberfläche, den so genannten Ereignisversand-Thread. Ihre Rückrufe werden für diesen Thread ausgeführt, nicht für den Hauptthread.

Ihr Hauptthread ist gut für Ihre Spielschleife, wenn Sie möchten, dass das Spiel automatisch startet, wenn die Programme ausgeführt werden. Wenn Sie das Spiel mit einer Swing-GUI starten und stoppen wollen, dann muss der Haupt-Thread eine GUI starten, dann kann die GUI einen neuen Thread für die Spiel-Schleife erstellen, wenn der Benutzer das Spiel starten möchte.

Nein, Ihre Menüleiste wird nicht einfrieren, wenn Sie Ihre Spielschleife in den Hauptthread einfügen. Ihre Menüleiste wird einfrieren, wenn Ihre Swing-Rückrufe lange dauern.

Daten, die zwischen den Threads geteilt werden, müssen mit Sperren geschützt werden.

Ich schlage vor, dass Sie Ihren Swing-Code in eine eigene Klasse einteilen und Ihre Spielschleife nur in Ihre Hauptklasse einfügen. Wenn Sie den Hauptthread für Ihre Spielschleife verwenden, ist dies eine grobe Vorstellung davon, wie Sie sie entwerfen könnten.

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  
    } 
} 
11

Ihre Spielschleife (Modell) sollte sich nicht in der Nähe einer Ihrer GUI-Klassen (Ansicht) befinden. Es verwendet Ihre GUI-Klassen - aber auch das möchten Sie wahrscheinlich über einen Vermittler (Controller) tun. Ein guter Weg, um sicherzustellen, dass Sie es richtig machen, ist zu überprüfen, dass Ihr Modell kein einziges "include javax.swing. ???" hat.

Das Beste, was Sie tun können, ist, die Spielschleife in einem eigenen Thread zu behalten. Wann immer Sie eine Änderung in der GUI vornehmen möchten, verwenden Sie SwingWorker oder was auch immer die kleinen Kinder jetzt verwenden, um es für nur diese eine Operation in den GUI-Thread zu zwingen.

Das ist wirklich genial, weil es Sie in Bezug auf GUI-Operationen denken lässt (was Ihren Controller ausmachen würde). Zum Beispiel könnten Sie eine Klasse namens "Move" haben, die die GUI-Logik hinter einer Bewegung hat. Ihre Spielschleife kann ein "Move" mit den richtigen Werten (zu bewegendes Element, endgültiger Ort) instanziieren und zur Verarbeitung an eine GUI-Schleife übergeben.

Sobald Sie diesen Punkt erreicht haben, stellen Sie fest, dass Sie einfach durch Hinzufügen eines trivialen "Undo" für jede GUI-Operation eine beliebige Anzahl von Operationen rückgängig machen können. Sie werden es auch einfacher finden, Ihre Swing-GUI durch eine Web-GUI zu ersetzen ...

4

Java ist wirklich geeignet für event-driven programming. Stellen Sie im Grunde ein Timer-Ereignis auf und hören Sie zu. Bei jedem Tick-Tack aktualisieren Sie Ihre Spiellogik. Bei jedem GUI-Event aktualisieren Sie Ihre Datenstrukturen, die die Spiellogik-Methode lesen wird.

Verwandte Themen