2009-12-26 5 views
15

다음 학기에는 팀에서 Java 응용 프로그램을 만드는 모듈이 있습니다. 모듈의 요구 사항은 게임을 만드는 것입니다. 크리스마스 휴일 동안 나는 약간의 연습을 해왔지만 그래픽을 그릴 수있는 최선의 방법을 찾지 못했습니다.자바 2D 게임 그래픽

자바 Graphics2D 객체를 사용하여 화면에 도형을 그리고 repaint()을 초당 30 번 호출하지만 이것은 깜박 거립니다. Java에서 고성능 2D 그래픽을 그리는 더 좋은 방법이 있습니까?

답변

16

원하는 것은 BufferStrategy로 캔버스 구성 요소를 만들어 렌더링하는 것입니다. 아래 코드는 어떻게 동작하는지 보여 주어야하며, 자체 작성 엔진에서 코드를 추출했습니다 (here 이상).

퍼포먼스는 당신이 그리고 싶은 것에 달려 있습니다. 게임은 주로 이미지를 사용합니다. 약 1500 개를 가지고 480x480에서 여전히 200FPS를 넘었습니다. 그리고 단지 100 장의 이미지로 프레임 제한을 해제 할 때 6kFPS를 때리고 있습니다.

작은 게임은 내가 here을 찾을 수 있습니다 만든 (이 화면에서 한 번에 120 개의 이미지가) (예 또한 아래의 방법은 애플릿으로 작동합니다.)

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

감사합니다 !!! 이것은 매우 intresting입니다. FPS도 제한적입니다. 당신이 만든 게임은 아주 멋지 네요! –

+0

흥미 롭습니다, 동방 박사회 밖에서 전화를 걸려면 strategy.show()가 안전합니까? – Pool

+0

두 번째 스레드로 테스트를 마치면 yes입니다. 안전합니다. try/catch의 경우 Toolkit.getDefaultToolkit(). sync()가 드문 경우에 예외를 throw 할 수 있기 때문에 그곳에 만 존재합니다. –

3

Java OpenGL (JOGL)은 편도입니다.

+0

JOGL은 좋지만 팀의 다른 구성원에게 사용하도록 설득 할 수 있을지는 의문입니다. 팀은 모든 기술 수준에 걸쳐 파종되며, 여유 시간에 게임을 만들고 재미있는 코드를 작성하는 사람이지만 그룹의 다른 사람들은 가능한 한 단순하게 유지하려고합니다. 불행히도) – Martin

2

paint(Graphics g)에서 우선 적용하셨습니까? 이것은 좋은 방법이 아닙니다. 같은 코드를 사용하지만 paint(Graphics g) 대신 paintComponent(Graphics g)에 사용하십시오.

검색 할 수있는 라벨은 doublebuffer입니다. 이는 paintComponent을 재정 의하여 자동으로 수행됩니다.

+0

그래서 필자는 문자 그대로 페인트에서 코드를 페인트 컴포넌트로 복사 할 수 있으며 이중 버퍼링을 제외하고는 모든 것이 동일하게 작동합니까? – Martin

+0

예, 그게 무슨 뜻입니까. 명절의 응답은 무슨 일이 일어 났는지 설명합니다.그러나 Java는 이미 솔루션 구축 기능을 갖추고있었습니다. Feast는'paintComponent'를 사용하지 않고 자신 만의 솔루션을 만드는 것을 피합니다. –

8

깜박임은 화면에 직접 쓰는 것이 원인입니다. 버퍼를 사용하여 그림을 그린 다음 1 단계로 전체 화면을 씁니다. 이전에 들어 보셨을 수도있는 Double Buffering입니다. Here은 가능한 가장 간단한 형식입니다.

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 

오프 스크린 그래픽 offG 사용을 참조하십시오. 화면 이미지를 만드는 것은 비용이 많이 들기 때문에 첫 번째 호출에서만 화면 이미지를 만드는 것이 좋습니다.

이 부분을 개선 할 수있는 다른 영역이 있습니다 (예 : creating a compatible image). clipping 등을 사용합니다. 더 세밀하게 조정 된 애니메이션을 보려면 active rendering을 조사해야합니다.

게임 자습서에 대해 토론하기 위해 북마크 한 괜찮은 페이지가 있습니다. here.

행운을 빈다.

0

가있다 프로그램을 최적화하는 간단한 방법. 복잡한 코드를 없애고 Canvas 대신 JComponent을 사용하고 오브젝트를 페인트하십시오. 그게 다야. 그것을 즐기십시오 ...