我尝试在java中为2D游戏编写一个简单的结构,使用主动渲染和fps独立游戏更新,我遇到了run()方法的问题。
正如您在下面的代码中看到的,我创建了一个BufferedImage frameBuff,在其上绘制一堆矩形并将其渲染到嵌套Window类中的JPanel面板。 我的问题是,当我设置我的fps上限非常低时,我可以看到第一个BufferedImage frameBuff并没有真正被绘制到面板上,尽管调用了renderImage()方法。 这似乎是因为在我将frameBuff渲染到面板之前,Graphics2D对象没有完成绘图。
在渲染frameBuff之前,我确实可以在绘制后暂停一段时间,但是当场景变得更复杂时,完成绘制到frameBuff所需的时间可能会有所不同。 那么有没有办法在g2d完成绘制之前等待/准确睡眠?
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class Game implements Runnable
{
private Window gui;
private int frameCap;
private double delta, fixedSpeed, frameTimeCap;
private long lastFrame, currentFrame;
private BufferedImage frameBuff;
private Thread mainThread;
private boolean running;
private static final long oneSecondInNano = 1000000000;
private static final long oneMilliInNano = 1000000;
public static void main(String[] args)
{
new Game(60, 0);
}
/**
* Sets up the Game, setting its fixed speed (time relative) and the
* framecap.
*
* @param fixedSpeed
* - the speed at which the game should run in units/second
* <p>
* <strong>Example:</strong> If a rectangle should move 60
* pixels/second, the speed would be set to 60 and the rectangle
* would be moved by 1*delta every frame)
* @param frameCap
* - the amount of frames/second the game must not exceed
*/
public Game(double fixedSpeed, int frameCap)
{
this.frameCap = frameCap;
if(this.frameCap == 0)
this.frameTimeCap = 0;
else
this.frameTimeCap = oneSecondInNano/frameCap;
this.fixedSpeed = oneSecondInNano/fixedSpeed;
gui = new Window();
running = true;
lastFrame = System.nanoTime();
mainThread = new Thread(this);
mainThread.start();
}
/**
* Mainloop
*/
@Override
public void run()
{
double x = 0;
while (running)
{
//Calculating delta
currentFrame = System.nanoTime();
long diff = currentFrame - lastFrame;
System.out.println("last Frametime:\t" + diff + "ns");
delta = diff / fixedSpeed;
//Update Game
if(frameBuff == null)
frameBuff = new BufferedImage(640, 480, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D)frameBuff.getGraphics();
g2d.setColor(Color.red);
g2d.fillRect(0, 0, frameBuff.getWidth(), frameBuff.getHeight());
g2d.setColor(Color.black);
System.out.println("current Delta:\t" + delta);
x+= 3*delta;
g2d.fillRect((int)x%640, (int)x%480, 20, 20);
if(x%640 > 620 && x%480 > 460)
g2d.fillRect(0, 0, (int)x%640 - 620, (int)x%480 - 460);
else if(x%640 > 620)
g2d.fillRect(0, (int)x%480, (int)x%640 - 620, 20);
else if(x%480 > 460)
g2d.fillRect((int)x%640, 0, 20, (int)x%480 - 460);
g2d.dispose();
//Render image
gui.renderImage(frameBuff);
//Wait a bit
Thread.yield();
//Stay at/under the frameCap
diff = System.nanoTime() - currentFrame;
if(diff < frameTimeCap)
{
try
{
Thread.sleep((long)(frameTimeCap - diff)/oneMilliInNano);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
lastFrame = currentFrame;
}
}
/**
* GUI for the Game.
*
*/
public class Window extends JFrame
{
private static final long serialVersionUID = 1L;
JPanel contentPane, panel;
/**
* Sets up the Window. Creates the JPanel.
*/
public Window()
{
// Sets up the contentPane with Border and LayoutManager
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.X_AXIS));
this.setContentPane(contentPane);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setResizable(false);
// Sets up the panel and adds it to the contentPane
panel = new JPanel();
//panel.setBackground(Color.cyan);
panel.setPreferredSize(new Dimension(640, 480));
contentPane.add(panel);
this.pack();
this.setVisible(true);
this.setIgnoreRepaint(true);
panel.setIgnoreRepaint(true);
}
/**
* Renders a BufferedImage using the JPanel's Graphics Object.
*
* @param im
* Buffered Image that is drawn.
*/
public void renderImage(BufferedImage im)
{
Graphics2D g2d = (Graphics2D) panel.getGraphics();
g2d.drawImage(im, 0, 0, null);
}
}
}