下學期我們有一個團隊製作的Java應用程序的模塊。模塊的要求是製作遊戲。在聖誕假期,我一直在做一些練習,但我無法弄清楚繪製圖形的最佳方式。的Java 2D遊戲的圖形
我使用Java Graphics2D對象在屏幕上繪製形狀,並每秒調用repaint()
30次,但是這種閃爍非常厲害。在Java中繪製高性能2D圖形有更好的方法嗎?
下學期我們有一個團隊製作的Java應用程序的模塊。模塊的要求是製作遊戲。在聖誕假期,我一直在做一些練習,但我無法弄清楚繪製圖形的最佳方式。的Java 2D遊戲的圖形
我使用Java Graphics2D對象在屏幕上繪製形狀,並每秒調用repaint()
30次,但是這種閃爍非常厲害。在Java中繪製高性能2D圖形有更好的方法嗎?
你想要做的是創建一個帶有BufferStrategy的畫布組件,並渲染到下面的代碼應該告訴你它是如何工作的,我已經從我自己寫的引擎中提取了代碼,覆蓋了here。
表演完全取決於你想畫的東西,我的遊戲大多使用圖片。其中約1500人,我仍然在480x480以上200 FPS以上。在禁用幀限制的情況下,只有100張圖像,我擊中6k FPS。
一個小遊戲(這個屏幕有一次約120圖像)我已經創建,可以發現here(下面是也是辦法正常工作作爲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();
}
}
Java OpenGL(JOGL)是一種方式。
JOGL很好,但我懷疑我可以說服團隊的其他成員使用它。該團隊在所有技能水平種子,而我'#是那種人誰使他們的遊戲打發時間和寫入樂趣併發代碼,其他人組會希望讓事情儘可能簡單(不幸) – Martin 2009-12-26 14:48:03
我認爲你從paint(Graphics g)
做了一個覆蓋?這不是好的方法。使用相同的代碼,但在paintComponent(Graphics g)
而不是paint(Graphics g)
。
您可以搜索的標籤是doublebuffer
。這是通過覆蓋paintComponent
自動完成的。
所以我可以從字面上只是將代碼從油漆複製到塗料成份,一切都將工作一樣,除了它會被加倍緩衝? – Martin 2009-12-26 14:48:55
是的,這就是我的意思。盛宴的答案描述了發生的事情。但Java已經有了一個解決方案內建。盛宴所做的只是避免使用'paintComponent'並製作他自己的解決方案。 – 2009-12-26 15:49:08
的閃爍是因爲你直接寫在屏幕上。使用一個緩衝區來繪製,然後寫入整個屏幕。這是你以前可能聽說過的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。
祝你好運!
有一個簡單的方法來優化你的程序。擺脫任何複雜的代碼,只需使用JComponent
代替Canvas
並在其上繪製的對象。就這樣。享受它...
謝謝!這非常有趣。 FPS限制也。你做的遊戲非常好! – 2009-12-26 17:18:15
有趣的是,strategy.show()可以安全地從美國東部時間之外打電話嗎? – Pool 2009-12-26 17:29:18
第二個線程短測驗說是的,這是安全的。對於try/catch,這只是因爲Toolkit.getDefaultToolkit()。sync()可能會在極少數情況下拋出異常。 – 2009-12-26 17:55:01