2009-12-26 71 views
15

Học kỳ tiếp theo, chúng tôi có một mô-đun để tạo các ứng dụng Java trong một nhóm. Yêu cầu của module là tạo ra một trò chơi. Trong những ngày lễ Giáng sinh, tôi đã thực hành một chút, nhưng tôi không thể tìm ra cách tốt nhất để vẽ đồ họa.Đồ họa trò chơi Java 2D

Tôi đang sử dụng đối tượng Java Graphics2D để vẽ hình dạng trên màn hình và gọi số repaint() 30 lần trong một giây, nhưng điều này thật đáng kinh ngạc. Có cách nào tốt hơn để vẽ đồ họa 2D hiệu năng cao trong Java không?

Trả lời

16

Điều bạn muốn làm là tạo thành phần canvas với BufferStrategy và hiển thị, mã bên dưới sẽ cho bạn biết cách hoạt động, tôi đã trích xuất mã từ Engine tự viết của mình qua here.

Hiệu suất chỉ phụ thuộc vào nội dung bạn muốn vẽ, trò chơi của tôi chủ yếu sử dụng hình ảnh. Với khoảng 1500 người trong số họ tôi vẫn còn trên 200 FPS ở 480x480. Và chỉ với 100 hình ảnh tôi đang nhấn 6k FPS khi vô hiệu hóa khung giới hạn.

Một trò chơi nhỏ (cái này có khoảng 120 hình ảnh cùng một lúc vào màn hình) Tôi đã tạo có thể được tìm thấy here (có phương pháp dưới đây cũng hoạt động tốt như một 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

Cảm ơn !!! Điều này là rất intresting. FPS cũng hạn chế. Trò chơi bạn đã thực hiện là VERY NICE! –

+0

Thú vị, là strategy.show() an toàn để gọi từ bên ngoài EDT? – Pool

+0

Thử nghiệm ngắn với chuỗi thứ hai cho biết có, nó an toàn. Đối với try/catch, đó là chỉ có bởi vì Toolkit.getDefaultToolkit(). Sync() CÓ THỂ ném một ngoại lệ trong trường hợp hiếm hoi. –

3

Java OpenGL (JOGL) là một cách.

+0

JOGL là tốt, nhưng tôi nghi ngờ tôi có thể thuyết phục các thành viên khác của nhóm sử dụng nó. Các đội được xếp vào tất cả các cấp độ kỹ năng và trong khi tôi là người tạo trò chơi trong thời gian rảnh rỗi và viết mã đồng thời để giải trí, những người khác trong nhóm sẽ muốn giữ mọi thứ càng đơn giản càng tốt (Thật không may) – Martin

2

Tôi nghĩ bạn đã ghi đè từ paint(Graphics g)? Đây không phải là cách tốt. Sử dụng cùng một mã nhưng trong paintComponent(Graphics g) thay vì paint(Graphics g).

Nhãn bạn có thể tìm kiếm là doublebuffer. Đó là những gì sẽ được thực hiện tự động bằng cách ghi đè paintComponent.

+0

vì vậy tôi có thể chỉ cần sao chép mã từ sơn để sơn thành phần và tất cả mọi thứ sẽ làm việc giống nhau, ngoại trừ nó sẽ được đệm đôi? – Martin

+0

vâng, đó là điều tôi muốn nói. Câu trả lời của Feast mô tả những gì đang xảy ra.Nhưng Java đã có một giải pháp tích hợp sẵn. Những gì Lễ làm là chỉ cần tránh sử dụng 'paintComponent' và làm cho giải pháp của riêng mình. –

8

Nhấp nháy là do bạn viết trực tiếp lên màn hình. Sử dụng bộ đệm để vẽ và sau đó viết toàn bộ màn hình trong 1 lần. Đây là Double Buffering mà bạn có thể đã nghe nói về trước đây. Here là dạng đơn giản nhất có thể.

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 

Xem việc sử dụng đồ họa trên màn hình ngoài offG. Nó là tốn kém để tạo ra hình ảnh màn hình tắt vì vậy tôi sẽ đề nghị tạo nó chỉ trên cuộc gọi đầu tiên.

Có các khu vực khác bạn có thể cải thiện thêm ví dụ này: creating a compatible image, sử dụng clipping v.v. Để có thêm điều khiển hoạt ảnh được điều chỉnh tốt, bạn nên xem active rendering.

Có một trang phong nha mà tôi đã đánh dấu trang thảo luận về các hướng dẫn trò chơi here.

Chúc may mắn!

0

Có một cách đơn giản để tối ưu hóa chương trình của bạn. Loại bỏ bất kỳ mã phức tạp nào và chỉ sử dụng JComponent thay vì Canvas và vẽ các đối tượng của bạn trên đó. Đó là tất cả. Hãy tận hưởng nó ...

Các vấn đề liên quan