2009-12-26 27 views
15

El próximo semestre tenemos un módulo para hacer aplicaciones Java en un equipo. El requisito del módulo es hacer un juego. Durante las vacaciones de Navidad he estado practicando un poco, pero no puedo encontrar la mejor manera de dibujar gráficos.Gráficos 2D de juegos Java

Estoy usando el objeto Java Graphics2D para pintar formas en la pantalla, y llamando al repaint() 30 veces por segundo, pero esto parpadea terriblemente. ¿Hay una mejor manera de pintar gráficos 2D de alto rendimiento en Java?

Respuesta

16

Lo que quiere hacer es crear un componente de lienzo con una BufferStrategy y representarlo, el siguiente código debe mostrarle cómo funciona, he extraído el código de mi motor auto escrito sobre here.

El rendimiento depende únicamente de las cosas que desea dibujar, mis juegos utilizan principalmente imágenes. Con alrededor de 1500 de ellos, aún estoy por encima de 200 FPS a 480x480. Y con solo 100 imágenes, estoy alcanzando 6k FPS al deshabilitar la limitación de fotogramas.

Un pequeño juego (éste tiene alrededor de 120 imágenes a la vez en la pantalla) que he creado puede ser encontrado here (sí el enfoque de abajo también funciona bien como un applet.)

import java.awt.Canvas; 
import java.awt.Color; 
import java.awt.Graphics2D; 
import java.awt.GraphicsConfiguration; 
import java.awt.GraphicsEnvironment; 
import java.awt.Toolkit; 
import java.awt.Transparency; 
import java.awt.event.WindowAdapter; 
import java.awt.event.WindowEvent; 
import java.awt.image.BufferStrategy; 
import java.awt.image.BufferedImage; 

import javax.swing.JFrame; 
import javax.swing.WindowConstants; 

public class Test extends Thread { 
    private boolean isRunning = true; 
    private Canvas canvas; 
    private BufferStrategy strategy; 
    private BufferedImage background; 
    private Graphics2D backgroundGraphics; 
    private Graphics2D graphics; 
    private JFrame frame; 
    private int width = 320; 
    private int height = 240; 
    private int scale = 1; 
    private GraphicsConfiguration config = 
      GraphicsEnvironment.getLocalGraphicsEnvironment() 
       .getDefaultScreenDevice() 
       .getDefaultConfiguration(); 

    // create a hardware accelerated image 
    public final BufferedImage create(final int width, final int height, 
      final boolean alpha) { 
     return config.createCompatibleImage(width, height, alpha 
       ? Transparency.TRANSLUCENT : Transparency.OPAQUE); 
    } 

    // Setup 
    public Test() { 
     // JFrame 
     frame = new JFrame(); 
     frame.addWindowListener(new FrameClose()); 
     frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); 
     frame.setSize(width * scale, height * scale); 
     frame.setVisible(true); 

     // Canvas 
     canvas = new Canvas(config); 
     canvas.setSize(width * scale, height * scale); 
     frame.add(canvas, 0); 

     // Background & Buffer 
     background = create(width, height, false); 
     canvas.createBufferStrategy(2); 
     do { 
      strategy = canvas.getBufferStrategy(); 
     } while (strategy == null); 
     start(); 
    } 

    private class FrameClose extends WindowAdapter { 
     @Override 
     public void windowClosing(final WindowEvent e) { 
      isRunning = false; 
     } 
    } 

    // Screen and buffer stuff 
    private Graphics2D getBuffer() { 
     if (graphics == null) { 
      try { 
       graphics = (Graphics2D) strategy.getDrawGraphics(); 
      } catch (IllegalStateException e) { 
       return null; 
      } 
     } 
     return graphics; 
    } 

    private boolean updateScreen() { 
     graphics.dispose(); 
     graphics = null; 
     try { 
      strategy.show(); 
      Toolkit.getDefaultToolkit().sync(); 
      return (!strategy.contentsLost()); 

     } catch (NullPointerException e) { 
      return true; 

     } catch (IllegalStateException e) { 
      return true; 
     } 
    } 

    public void run() { 
     backgroundGraphics = (Graphics2D) background.getGraphics(); 
     long fpsWait = (long) (1.0/30 * 1000); 
     main: while (isRunning) { 
      long renderStart = System.nanoTime(); 
      updateGame(); 

      // Update Graphics 
      do { 
       Graphics2D bg = getBuffer(); 
       if (!isRunning) { 
        break main; 
       } 
       renderGame(backgroundGraphics); // this calls your draw method 
       // thingy 
       if (scale != 1) { 
        bg.drawImage(background, 0, 0, width * scale, height 
          * scale, 0, 0, width, height, null); 
       } else { 
        bg.drawImage(background, 0, 0, null); 
       } 
       bg.dispose(); 
      } while (!updateScreen()); 

      // Better do some FPS limiting here 
      long renderTime = (System.nanoTime() - renderStart)/1000000; 
      try { 
       Thread.sleep(Math.max(0, fpsWait - renderTime)); 
      } catch (InterruptedException e) { 
       Thread.interrupted(); 
       break; 
      } 
      renderTime = (System.nanoTime() - renderStart)/1000000; 

     } 
     frame.dispose(); 
    } 

    public void updateGame() { 
     // update game logic here 
    } 

    public void renderGame(Graphics2D g) { 
     g.setColor(Color.BLACK); 
     g.fillRect(0, 0, width, height); 
    } 

    public static void main(final String args[]) { 
     new Test(); 
    } 
} 
+0

Gracias! Esto es muy interesante. El FPS también limita. ¡El juego que hiciste es MUY BONITO! –

+0

Interesante, ¿es seguro llamar a strategy.show() desde fuera del EDT? – Pool

+0

La prueba corta con un segundo hilo dice sí, es seguro. Para el try/catch, eso solo está allí porque Toolkit.getDefaultToolkit(). Sync() PUEDE lanzar una excepción en casos excepcionales. –

3

Java OpenGL (JOGL) es de una sola manera.

+0

JOGL es bueno, pero dudo que pueda persuadir a otros miembros del equipo para que lo usen. Los equipos están sembrados en todos los niveles de habilidad, y aunque yo soy el tipo de persona que hace juegos en su tiempo libre y escribe códigos simultáneos para divertirse, otras personas en el grupo querrán mantener las cosas lo más simples posible (por desgracia) – Martin

2

Creo que hizo una anulación de paint(Graphics g)? Esta no es la buena manera. Utilice el mismo código pero en paintComponent(Graphics g) en lugar de paint(Graphics g).

Una etiqueta que puede buscar es doublebuffer. Eso es lo que se hará automáticamente anulando paintComponent.

+0

así que, literalmente, puedo copiar el código de pintura para pintar componentes y todo funcionará igual, excepto que tendrá doble buffer. – Martin

+0

sí, eso es lo que quiero decir. La respuesta de la Fiesta describe lo que está sucediendo.Pero Java ya tenía una solución incorporada. Lo que The Feast hace es simplemente evitar usar 'paintComponent' y hacer su propia solución. –

8

El parpadeo se debe a que escribe directamente en la pantalla. Use un buffer para dibujar y luego escriba toda la pantalla en 1 paso. Esto es Double Buffering del que posiblemente haya oído hablar anteriormente. Here es la forma más simple posible.

public void paint(Graphics g) 
{ 

    Image image = createImage(size + 1, size + 1); 
    Graphics offG = image.getGraphics(); 
    offG.setColor(Color.BLACK); 
    offG.fillRect(0, 0, getWidth(), getHeight()); 
    // etc 

Ver el uso de los gráficos de la pantalla fuera offG. Es costoso crear la imagen fuera de pantalla, por lo que sugeriría crearla solo en la primera llamada.

Hay otras áreas que se pueden mejorar aún más este ejemplo creating a compatible image, utilizando clipping etc. Para más afinado de control de la animación usted debe buscar en active rendering.

Hay una página decente que he marcado marcar sobre tutoriales de juegos here.

¡Buena suerte!

0

Hay una forma simple de optimizar su programa. Deshágase de cualquier código complejo y simplemente use JComponent en lugar de Canvas y pinte sus objetos en él. Eso es todo. Disfrútalo ...

Cuestiones relacionadas