2013-09-22 52 views
13

Khi thực hiện phát triển trò chơi 2D trong Java, hầu hết các hướng dẫn tạo ra một vùng đệm để hiển thị. Điều này làm cho cảm giác hoàn hảo. Tuy nhiên, nơi mọi người dường như nghiêng là phương pháp vẽ đồ họa thực tế vào bộ đệm.Đồ họa bộ đệm Java hoặc mảng số

Một số hướng dẫn tạo hình ảnh đệm, sau đó tạo một mảng số nguyên để biểu thị các màu pixel riêng lẻ.

private BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 
private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); 

Graphics g = bs.getDrawGraphics(); 
g.setColor(new Color(0x556B2F)); 
g.fillRect(0, 0, getWidth(), getHeight()); 
g.drawImage(image, 0, 0, getWidth(), getHeight(), null); 

Tuy nhiên một số hướng dẫn khác không tạo hình ảnh đệm, vẽ thành phần đồ họa của BufferStrategy để vẽ hình ảnh trực tiếp vào bộ đệm.

Graphics g = bs.getDrawGraphics(); 
g.setColor(new Color(0x556B2F)); 
g.fillRect(0, 0, getWidth(), getHeight()); 

g.drawImage(testImage.image, x*128, y*128, 128, 128, null); 

Tôi đã tự hỏi, tại sao tạo toàn bộ mảng int, sau đó vẽ nó. Điều này đòi hỏi rất nhiều công việc trong việc thực hiện hình chữ nhật, kéo dài, minh bạch, vv Các thành phần đồ họa của chiến lược đệm đã có phương pháp có thể dễ dàng được gọi. Có một số tăng hiệu năng lớn khi sử dụng mảng int không?

Tôi đã tra cứu điều này hàng giờ và tất cả các trang web tôi đã xem chỉ giải thích những gì họ đang làm và không phải lý do họ chọn làm theo cách đó.

+1

Tôi không chắc chắn 100%, nhưng tôi sẽ xuất hiện một số người tin rằng sẽ nhanh hơn để cập nhật mảng int/pixel sau đó sử dụng API đồ họa. Điều này có thể đúng, cũng có khả năng mọi người không hiểu cách tạo các đối tượng đồ họa tương thích. Sử dụng một chiến lược đệm nên cung cấp hầu như truy cập trực tiếp đến lớp phần cứng (nếu có), vì vậy tôi không thực sự hiểu tại sao bạn cần xem tại sao bạn cần sử dụng mảng int, đó là tôi, và tôi lười biếng như vậy;) – MadProgrammer

+0

Vâng, tôi đã thử nghiệm với cả hai phương pháp. Với mảng int, tôi có thể thay đổi khoảng 27648000 pixel trước khi khung hình/giây bắt đầu giảm xuống dưới 120. với đối tượng đồ họa, tôi có thể hiển thị hình ảnh trong suốt, lên hình chữ nhật có kích thước gấp vài nghìn lần tương đương với mảng int . Sử dụng đối tượng đồ họa có vẻ hữu ích hơn. – Kabistarz

+0

Tôi đã làm một ví dụ tương đối đơn giản một số thời gian trước đây, sử dụng không có gì nhiều hơn sau đó một JPanel với một số bức tranh tùy chỉnh cho các đối tượng chính. Sau đó tôi nhận được 4500 của những vật thể này, tất cả đều di chuyển theo các hướng khác nhau, bao gồm cả vòng quay của vật thể chính để nó chỉ theo hướng chuyển động của nó. Không chắc chắn tỷ lệ khung hình thực sự là gì, nhưng tôi đã làm cho nó làm mới khoảng 25 khung hình/giây và nó hoạt động khá tốt. Tôi nghĩ rằng có rất nhiều tối ưu hóa trong đường dẫn dựng hình, với khả năng sử dụng DirectX hoặc OpenGL nếu có, nhưng bạn sẽ cần thử nghiệm;) – MadProgrammer

Trả lời

5

Cho phép rõ ràng về một điều: cả hai đoạn mã đều làm chính xác điều tương tự - vẽ hình ảnh. Tuy nhiên, các đoạn trích này khá không hoàn chỉnh - đoạn thứ hai không hiển thị 'testImage.image' thực sự là gì hoặc cách nó được tạo ra. Nhưng cả hai đều gọi Graphics.drawImage() và tất cả các biến thể của drawImage() trong Graphics hoặc Graphics2D đều vẽ một Image, đơn giản và đơn giản. Trong trường hợp thứ hai, chúng tôi chỉ đơn giản là không biết nếu nó là một BufferedImage, một VolatileImage hoặc thậm chí là một Toolkit Image.

Vì vậy, không có sự khác biệt trong bản vẽ thực sự được minh họa ở đây!

Chỉ có một sự khác biệt giữa hai đoạn trích - người đầu tiên một cũng có được một tài liệu tham khảo trực tiếp đến các mảng số nguyên đó là cuối cùng trong nội bộ ủng hộ các trường hợp ảnh. Điều này cho phép truy cập trực tiếp vào dữ liệu pixel thay vì phải đi qua API hình ảnh (Buffered) của việc sử dụng ví dụ các phương thức getRGB() và setRGB() tương đối chậm. Lý do tại sao để làm điều đó không thể được thực hiện cụ thể trong bối cảnh là trong câu hỏi này, mảng thu được nhưng không bao giờ được sử dụng trong đoạn mã. Vì vậy, để giải thích lý do sau đây, chúng ta phải giả định rằng ai đó muốn trực tiếp đọc hoặc chỉnh sửa pixel của hình ảnh, có thể vì lý do tối ưu hóa cho "chậm trễ" của API hình ảnh (Buffered) Thao tác dữ liệu.

Và những lý do tối ưu hóa đó có thể là tối ưu hóa sớm có thể gây phản tác dụng cho bạn.


Tất cả, mã này chỉ hoạt động vì loại hình ảnh là INT_RGB sẽ cung cấp cho hình ảnh một IntDataBuffer. Nếu nó là một loại hình ảnh khác, cũ là 3BYTE_BGR, mã này sẽ thất bại với một ClassCastException vì bộ đệm dữ liệu sao lưu sẽ không là một IntDataBuffer. Điều này có thể không có nhiều vấn đề khi bạn chỉ tạo thủ công hình ảnh và bạn thực thi một loại cụ thể, nhưng hình ảnh có xu hướng được tải từ các tệp được tạo bởi các công cụ bên ngoài. Thứ hai, có một nhược điểm lớn hơn để truy cập trực tiếp vào bộ đệm pixel: khi bạn làm điều đó, Java2D sẽ từ chối tăng tốc của hình ảnh đó vì nó không thể biết khi nào bạn sẽ thay đổi nó ngoài tầm kiểm soát của nó.Chỉ cần cho rõ ràng: tăng tốc là quá trình giữ một hình ảnh không bị thay đổi trong bộ nhớ video thay vì sao chép nó từ bộ nhớ hệ thống mỗi khi nó được rút ra. Đây có thể là một cải tiến hiệu suất rất lớn (hoặc mất mát nếu bạn phá vỡ nó) tùy thuộc vào bao nhiêu hình ảnh bạn làm việc với.

How can I create a hardware-accelerated image with Java2D?

(Như câu hỏi có liên quan cho bạn: bạn nên sử dụng GraphicsConfiguration.createCompatibleImage() để xây dựng trường BufferedImage).

Về bản chất: hãy thử sử dụng API Java2D cho mọi thứ, không truy cập trực tiếp bộ đệm. tài nguyên off-site này đưa ra một ý tưởng tốt chỉ là những gì các tính năng của API có để hỗ trợ bạn trong việc đó mà không cần phải đi ở mức độ thấp:

http://www.pushing-pixels.org/2008/06/06/effective-java2d.html

5

Trước hết, có rất nhiều khía cạnh lịch sử. API ban đầu rất cơ bản, do đó, cách duy nhất để làm bất cứ điều gì không tầm thường là triển khai tất cả các nguyên thủy bắt buộc.

Truy cập dữ liệu thô có một chút lỗi thời và chúng tôi có thể cố gắng thực hiện một số "khảo cổ học" để tìm lý do cách tiếp cận đó được sử dụng. Tôi nghĩ rằng có hai lý do chính:

1. Kết quả bộ lọc

Đừng quên các hiệu ứng bộ lọc (loại bóng mờ, vv) rất đơn giản, rất quan trọng đối với bất kỳ nhà phát triển trò chơi và sử dụng rộng rãi.

enter image description here

Cách simples để thực hiện như một hiệu ứng với Java 1 là sử dụng int mảng và lọc được xác định như một ma trận. Herbert Schildt, ví dụ, đã từng có rất nhiều bản demo ví dụ:

public class Blur { 

    public void convolve() { 
     for (int y = 1; y < height - 1; y++) { 
      for (int x = 1; x < width - 1; x++) { 
       int rs = 0; 
       int gs = 0; 
       int bs = 0; 
       for (int k = -1; k <= 1; k++) { 
        for (int j = -1; j <= 1; j++) { 
         int rgb = imgpixels[(y + k) * width + x + j]; 
         int r = (rgb >> 16) & 0xff; 
         int g = (rgb >> 8) & 0xff; 
         int b = rgb & 0xff; 
         rs += r; 
         gs += g; 
         bs += b; 
        } 
       } 
       rs /= 9; 
       gs /= 9; 
       bs /= 9; 
       newimgpixels[y * width + x] = (0xff000000 
         | rs << 16 | gs << 8 | bs); 
      } 
     } 
    } 
} 

Đương nhiên, bạn có thể thực hiện điều đó bằng getRGB, nhưng truy cập dữ liệu thô là cách hiệu quả hơn. Sau đó, Graphics2D cung cấp tốt hơn trừu tượng lớp:

public interface BufferedImageOp

giao diện này mô tả hoạt động đơn đầu vào/đầu ra đơn thực hiện trên BufferedImage đối tượng. Nó được thực hiện bởi AffineTransformOp, ConvolveOp, ColorConvertOp, RescaleOp và LookupOp. Các đối tượng này có thể được chuyển qua vào BufferedImageFilter để hoạt động trên BufferedImage trong mô hình ImageProducer-ImageFilter-ImageConsumer.

2. đúp đệm

Một vấn đề khác có liên quan đến bập bùng và vẽ rất chậm. Double buffering loại bỏ sự nhấp nháy xấu xí và đột nhiên nó cung cấp một cách dễ dàng để thực hiện các hiệu ứng lọc, bởi vì bạn đã có bộ đệm rồi.

enter image description here

Cái gì đó như một kết luận cuối cùng :)

tôi sẽ nói những tình huống mà bạn đã mô tả là khá phổ biến đối với bất kỳ công nghệ phát triển. Có hai cách để đạt được cùng một mục tiêu: tiếp cận di sản

  • sử dụng, mã hơn, vv
  • dựa trên các lớp trừu tượng mới, các kỹ thuật được cung cấp, vv

Có cũng có một số phần mở rộng hữu ích để đơn giản hóa thậm chí không cần phải sử dụng int[] :)

+0

Cảm ơn, không cần phải làm điều đó. Đó là về niềm vui. Hơn nữa, nó có vẻ như tôi có đủ điểm;) –

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