我正在尝试为大学的一个项目创建一个小游戏。因为我需要永久更新屏幕,我正在寻找一个好的渲染循环实现。应该是全屏的。我在gamedev.net上发现了这个- Java游戏:主动渲染:
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JFrame;
public class SimpleFullScreenGame {
static boolean running;
public static void main( String[] args ) {
// Create game window...
JFrame app = new JFrame();
app.setIgnoreRepaint( true );
app.setUndecorated( true );
// Add ESC listener to quit...
app.addKeyListener( new KeyAdapter() {
public void keyPressed( KeyEvent e ) {
if( e.getKeyCode() == KeyEvent.VK_ESCAPE )
running = false;
}
});
// Get graphics configuration...
GraphicsEnvironment ge =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gd.getDefaultConfiguration();
// Change to full screen
gd.setFullScreenWindow( app );
if( gd.isDisplayChangeSupported() ) {
gd.setDisplayMode(
new DisplayMode( 640, 480, 32, DisplayMode.REFRESH_RATE_UNKNOWN )
);
}
// Create BackBuffer...
app.createBufferStrategy( 2 );
BufferStrategy buffer = app.getBufferStrategy();
// Create off-screen drawing surface
BufferedImage bi = gc.createCompatibleImage( 640, 480 );
// Objects needed for rendering...
Graphics graphics = null;
Graphics2D g2d = null;
Color background = Color.BLACK;
Random rand = new Random();
// Variables for counting frames per seconds
int fps = 0;
int frames = 0;
long totalTime = 0;
long curTime = System.currentTimeMillis();
long lastTime = curTime;
running = true;
while( running ) {
try {
// count Frames per second...
lastTime = curTime;
curTime = System.currentTimeMillis();
totalTime += curTime - lastTime;
if( totalTime > 1000 ) {
totalTime -= 1000;
fps = frames;
frames = 0;
}
++frames;
// clear back buffer...
g2d = bi.createGraphics();
g2d.setColor( background );
g2d.fillRect( 0, 0, 639, 479 );
// draw some rectangles...
for( int i = 0; i < 20; ++i ) {
int r = rand.nextInt(256);
int g = rand.nextInt(256);
int b = rand.nextInt(256);
g2d.setColor( new Color(r,g,b) );
int x = rand.nextInt( 640/2 );
int y = rand.nextInt( 480/2 );
int w = rand.nextInt( 640/2 );
int h = rand.nextInt( 480/2 );
g2d.fillRect( x, y, w, h );
}
// display frames per second...
g2d.setFont( new Font( "Courier New", Font.PLAIN, 12 ) );
g2d.setColor( Color.GREEN );
g2d.drawString( String.format( "FPS: %s", fps ), 20, 20 );
// Blit image and flip...
graphics = buffer.getDrawGraphics();
graphics.drawImage( bi, 0, 0, null );
if( !buffer.contentsLost() )
buffer.show();
} finally {
// release resources
if( graphics != null )
graphics.dispose();
if( g2d != null )
g2d.dispose();
}
}
gd.setFullScreenWindow( null );
System.exit(0);
}
}
我执行了代码,有两件事我想知道:
我的显示器分辨率是1920x1080。代码使用640x480。因此,渲染的矩形看起来就像一张640x480的图片升级到1920x1080,这意味着你有巨大的像素,代码没有利用显示器可能的更高分辨率。如何调整代码以实际使用用户的本机显示分辨率?其次,当你查看游戏代码时,你通常会看到一个存在于自己线程中的渲染循环,而不会阻塞主线程。然而,这里的情况并非如此。为什么?我想是因为JFrame已经存在于swing创建的自己的线程中?
谢谢你的帮助。
这段代码有很多问题。
首先,当您检查(轮询)主线程中的标志变量时,它将由一个键侦听器更新,该侦听器将在事件调度线程中调用,您必须做的最少的事情是声明该变量volatile
。
那么,当您根本不使用Swing框架时,使用JFrame
是没有意义的。此外,从AWT请求双重缓冲,然后在已经缓冲的操作之上使用BufferedImage
进行另一个缓冲,这是毫无意义的。
使用本机分辨率就像删除setDisplayMode(…)
调用一样简单。在将窗口变为全屏后,您可以简单地使用getWidth()
和getHeight()
来获取操作的实际尺寸(对于缓冲的图像不需要它,因为它已经过时了)。
public class SimpleFullScreenGame {
static volatile boolean running = true;
public static void main(String[] args) {
// Create game window...
Frame app = new Frame();
app.setIgnoreRepaint(true);
app.setUndecorated(true);
// Add ESC listener to quit...
app.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_ESCAPE) running = false;
}
});
GraphicsDevice dev = app.getGraphicsConfiguration().getDevice();
// Change to full screen
dev.setFullScreenWindow(app);
int width = app.getWidth(), height = app.getHeight();
// Create BackBuffer...
app.createBufferStrategy(2);
BufferStrategy buffer = app.getBufferStrategy();
// Objects needed for rendering...
Color background = Color.BLACK, textColor = Color.GREEN;
Font font = new Font("Courier New", Font.PLAIN, 12);
ThreadLocalRandom rand = ThreadLocalRandom.current();
// Variables for counting frames per seconds
int fps = 0, frames = 0, totalTime = 0;
long currTime = System.nanoTime(), lastTime;
while(running) {
// count Frames per second...
lastTime = currTime;
currTime = System.nanoTime();
totalTime += currTime - lastTime;
if(totalTime > 1_000_000_000) {
totalTime -= 1_000_000_000;
fps = frames;
frames = 0;
}
frames++;
Graphics gfx = buffer.getDrawGraphics();
gfx.setColor(background);
gfx.fillRect(0, 0, width, height);
// draw some rectangles...
for(int i = 0; i < 20; ++i) {
gfx.setColor(new Color(rand.nextInt(0x1000000)));
int x = rand.nextInt(width/2), y = rand.nextInt(height/2);
int w = rand.nextInt(width/2), h = rand.nextInt(height/2);
gfx.fillRect(x, y, w, h);
}
// display frames per second...
gfx.setFont(font);
gfx.setColor(textColor);
gfx.drawString("FPS: " + fps, 20, 20);
gfx.dispose();
if(!buffer.contentsLost()) buffer.show();
}
dev.setFullScreenWindow(null);
System.exit(0);
}
}
我做了一些其他的小改进。