2010-05-18 14 views
9

Estoy intentando hacer un simple juego 2D en Java.La mejor forma de implementar un bucle de juego sin congelar el hilo de la interfaz de usuario

Hasta ahora tengo un JFrame, con una barra de menú, y una clase que se extiende JPanel y anula el método paint. Ahora necesito un ciclo de juego, donde actualizaré la posición de las imágenes, etc. Sin embargo, estoy estancado en la mejor manera de lograr esto. ¿Debería usar multi-threading, porque seguramente, si pones un ciclo infinito en el hilo principal, la IU (y por lo tanto mi barra de menú) se congelará?

Aquí está mi código hasta ahora:

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

Cualquier indicador o consejos? ¿Dónde debería poner mi lazo? En mi clase principal, o mi clase GameCanvas?

Respuesta

5

Necesita un hilo para su juego lazo y un hilo para manejar eventos Swing UI como clics del mouse y presionar teclas.

Cuando utiliza la API Swing, automáticamente obtiene un hilo adicional para su interfaz de usuario llamado el hilo de envío del evento. Sus devoluciones de llamada se ejecutan en este hilo, no en el hilo principal.

Su hilo principal está bien para su bucle de juego si desea que el juego se inicie automáticamente cuando se ejecuta el programa. Si desea iniciar y detener el juego con una GUI de Swing, entonces haga que el hilo principal inicie una GUI, luego la GUI puede crear un nuevo hilo para el bucle del juego cuando el usuario quiera comenzar el juego.

No, su barra de menú no se congelará si coloca el lazo del juego en el hilo principal. La barra de menú se congelará si las devoluciones de llamada de Swing tardan mucho en finalizar.

Los datos que se comparten entre los hilos deberán protegerse con bloqueos.

Te sugiero que factorices tu código Swing en su propia clase y solo pongas tu bucle de juego dentro de tu clase principal. Si está utilizando el hilo principal para su bucle de juego, esta es una idea aproximada de cómo podría diseñarlo.

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

Su bucle de juego (modelo) no debe estar cerca de ninguna de sus clases de GUI (vista). Utiliza tus clases de GUI, pero incluso eso es probable que quieras hacerlo a través de un intermediario (controlador). Una buena forma de asegurarse de que lo está haciendo bien es verificar que su modelo no tenga un solo "include javax.swing. ???".

Lo mejor que puedes hacer es mantener el bucle del juego en su propio hilo. Siempre que desee hacer un cambio en la GUI, use SwingWorker o lo que sea que los niños pequeños usen ahora para forzarlo en el hilo de la GUI para solo esa operación.

Esto es realmente asombroso porque te hace pensar en términos de Operaciones GUI (que constituirían tu controlador). Por ejemplo, podría tener una clase llamada "Mover" que tendría la lógica de la GUI detrás de un movimiento. El ciclo del juego podría instanciar un "Movimiento" con los valores correctos (elemento para mover, ubicación final) y pasarlo a un bucle GUI para su procesamiento.

Una vez que llegue a ese punto, se dará cuenta de que simplemente agregando un "deshacer" trivial para cada operación de la GUI le permite deshacer fácilmente cualquier cantidad de operaciones. También le resultará más fácil reemplazar su GUI de Swing con una GUI web ...

4

Java es realmente adecuado para event-driven programming. Básicamente, configure un evento de temporizador y escuche. En cada 'tic-tac' actualizas la lógica de tu juego. En cada evento GUI usted actualiza sus estructuras de datos que leerá el método lógico del juego.

Cuestiones relacionadas