我正在尝试使用 Java 开发 2D 游戏。到目前为止,我已经设法将游戏设置为使用全屏独占模式并在自定义线程中进行主动渲染。我决定使用的游戏循环属于固定时间步变量渲染类型。这种类型的游戏循环应该在设备可以处理的情况下尽可能快地呈现,我对此并不完全满意。所以我尝试使用 Thread.sleep()
来限制帧速率。
如果我关闭所有渲染,并简单地在游戏循环中更新游戏,Thread.sleep(1)
会成功 hibernate 大约 1 毫秒
。但是,如果我打开渲染,有时 Thread.sleep(1)
hibernate 的时间会超过 1 毫秒
,比如 15 毫秒
。
我通过添加/删除线条来打开/关闭渲染:
BufferedImage drawImage = render(Math.min(1d, lag / TIME_PER_UPDATE));
drawToScreen(drawImage);
是什么导致线程 hibernate 时间过长?
这是我第一次在这些论坛上发帖,所以请告诉我我的帖子是否做错了什么,或者这是重复的(我没有找到类似的帖子)。
import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
public class Main implements KeyListener
{
private static final long serialVersionUID = 1L;
private boolean gameRunning = false;
private final double UPDATE_RATE = 60;
private final double TIME_PER_UPDATE = 1000000000 / UPDATE_RATE;
private final int MAX_UPDATES_BEFORE_RENDERING = 5;
private final int TARGET_FPS = 60;
private int windowWidth;
private int windowHeight;
private GraphicsDevice graphicsDevice;
private DisplayMode defaultDisplayMode;
private Frame frame;
private BufferStrategy bufferStrategy;
private Player player;
public Main()
{
GraphicsDevice[] screenDevices = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
this.graphicsDevice = screenDevices[0];
// This is later used to restore the original display mode when closing
// the game
defaultDisplayMode = this.graphicsDevice.getDisplayMode();
frame = new Frame("GameTest");
frame.setIgnoreRepaint(true);
frame.setResizable(false);
frame.setUndecorated(true);
// Ensure that the user device supports full screen exclusive mode
if (this.graphicsDevice.isFullScreenSupported())
{
graphicsDevice.setFullScreenWindow(frame);
}
windowWidth = frame.getWidth();
windowHeight = frame.getHeight();
frame.createBufferStrategy(2);
bufferStrategy = frame.getBufferStrategy();
// The frame receives keyboard event dispatched on the EDT-thread.
frame.addKeyListener(this);
initGame();
// Starts the gameThread. The updating of the game state and rendering
GameThread gameThread = new GameThread();
gameThread.start();
}
private void initGame()
{
player = new Player(300, 300);
}
private class GameThread extends Thread
{
@Override
public void run()
{
gameLoop();
}
}
public static void main(String[] Args)
{
new Main();
}
private void gameLoop()
{
gameRunning = true;
double lastStartTime = System.nanoTime();
double startTime;
double elapsedTime = 0;
double lag = 0;
double lastRenderTime;
int updateCount = 0;
while (gameRunning)
{
System.out.println("");
System.out.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
System.out.println("New Gameloop");
System.out.println("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
startTime = System.nanoTime();
elapsedTime = startTime - lastStartTime;
lag += elapsedTime;
updateCount = 0;
while (lag >= TIME_PER_UPDATE && updateCount < MAX_UPDATES_BEFORE_RENDERING)
{
updateGameState();
lag -= TIME_PER_UPDATE;
updateCount++;
}
if (startTime - lastStartTime > TIME_PER_UPDATE)
{
lastStartTime = startTime - TIME_PER_UPDATE;
}
BufferedImage drawImage = render(Math.min(1d, lag / TIME_PER_UPDATE));
drawToScreen(drawImage);
lastRenderTime = System.nanoTime();
double currentFPS = 1000000000d / (lastRenderTime - startTime);
//Sleeps until target FPS is reached
System.out.println("");
System.out.println("Before sleeping");
System.out.println("");
System.out.println("Current FPS:");
System.out.println(currentFPS);
while (currentFPS > TARGET_FPS && (lastRenderTime - startTime) < TIME_PER_UPDATE)
{
//Lets the CPU rest
Thread.yield();
double beginSleepTime = System.nanoTime();
try
{
Thread.sleep(1);
} catch (Exception e)
{
e.printStackTrace();
}
double endSleepTime = System.nanoTime();
lastRenderTime = System.nanoTime();
currentFPS = 1000000000d / (lastRenderTime - startTime);
System.out.println("");
System.out.println("--------------------------------");
System.out.println("Sleeping");
System.out.println("");
System.out.println("Time slept in ms:");
System.out.println("");
System.out.println((endSleepTime - beginSleepTime) / 1000000d);
System.out.println("");
System.out.println("current FPS");
System.out.println("");
System.out.println(currentFPS);
}
lastStartTime = startTime;
}
}
private void updateGameState()
{
player.update();
}
private void drawToScreen(BufferedImage drawImage)
{
try
{
Graphics2D g2d = (Graphics2D) bufferStrategy.getDrawGraphics();
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.clearRect(0, 0, windowWidth, windowHeight);
g2d.setBackground(Color.BLACK);
g2d.drawImage(drawImage, 0, 0, windowWidth, windowHeight, null);
g2d.dispose();
if (!bufferStrategy.contentsLost())
{
bufferStrategy.show();
}
} catch (Exception e)
{
e.printStackTrace();
}
}
private BufferedImage render(double delta)
{
BufferedImage drawImage = new BufferedImage(windowWidth, windowHeight, BufferedImage.TYPE_INT_ARGB);
drawImage.createGraphics();
Graphics2D g = (Graphics2D) drawImage.getGraphics();
g.setBackground(Color.WHITE);
g.clearRect(0, 0, windowWidth, windowHeight);
//Render player
g.setColor(Color.BLUE);
g.fillRect((int) Math.round(player.getLocX() + delta * player.getSpeedX()), (int) Math.round(player.getLocY() + delta * player.getSpeedY()), 64, 64);
g.dispose();
return drawImage;
}
@Override
public void keyPressed(KeyEvent keyEvent)
{
switch (keyEvent.getKeyCode())
{
case KeyEvent.VK_ESCAPE:
graphicsDevice.setDisplayMode(defaultDisplayMode);
System.exit(0);
break;
case KeyEvent.VK_A:
player.setSpeedX(-player.getMoveSpeed());
break;
case KeyEvent.VK_D:
player.setSpeedX(player.getMoveSpeed());
break;
case KeyEvent.VK_W:
player.setSpeedY(-player.getMoveSpeed());
break;
case KeyEvent.VK_S:
player.setSpeedY(player.getMoveSpeed());
break;
case KeyEvent.VK_SPACE:
break;
case KeyEvent.VK_LESS:
break;
case KeyEvent.VK_I:
break;
}
}
@Override
public void keyReleased(KeyEvent keyEvent)
{
switch (keyEvent.getKeyCode())
{
case KeyEvent.VK_A:
player.setSpeedX(0);
break;
case KeyEvent.VK_D:
player.setSpeedX(0);
break;
case KeyEvent.VK_W:
player.setSpeedY(0);
break;
case KeyEvent.VK_S:
player.setSpeedY(0);
break;
case KeyEvent.VK_SPACE:
break;
case KeyEvent.VK_LESS:
break;
case KeyEvent.VK_I:
break;
}
}
@Override
public void keyTyped(KeyEvent keyEvent)
{
}
private class Player
{
protected double speedX;
protected double speedY;
protected double locX;
protected double locY;
protected double moveSpeed;
public Player(int locX, int locY)
{
speedX = 0;
speedY = 0;
this.locX = locX;
this.locY = locY;
moveSpeed = 3d;
}
public void update()
{
locY += speedY;
locX += speedX;
}
public void setSpeedX(double speedX)
{
this.speedX = speedX;
}
public void setSpeedY(double speedY)
{
this.speedY = speedY;
}
public double getSpeedX()
{
return speedX;
}
public double getSpeedY()
{
return speedY;
}
public double getLocX()
{
return locX;
}
public double getLocY()
{
return locY;
}
public double getMoveSpeed()
{
return moveSpeed;
}
}
}
最佳答案
java中的sleep()
方法让当前正在执行的线程(处于运行状态) hibernate 1ms。
1 毫秒后,线程进入可运行状态(能够运行),现在取决于调度程序何时从可运行状态获取线程并执行它(即运行状态)。
因此,您可以假设线程在再次运行之前至少 hibernate 1 毫秒。
下图描述了不同的线程状态:
关于Java thread.sleep(1) sleep 时间超过 1 毫秒,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30034457/