我正在嘗試開發使用Java的2D遊戲。到目前爲止,我已經成功設置了遊戲以使用全屏獨佔模式並在自定義線程中執行主動渲染。我決定使用的遊戲循環是類型固定的時間步變量渲染。這種類型的遊戲循環應該儘可能快地呈現設備可以處理的情況,但我並不完全滿意。所以我試圖用Thread.sleep()
限制幀率。Java thread.sleep(1)睡眠時間超過1毫秒
如果我關閉所有渲染,並簡單地更新遊戲循環中的遊戲,Thread.sleep(1)
成功睡眠約1 ms
。但是,如果我打開渲染,有時Thread.sleep(1)
睡眠方式比1 ms
更長,如15 ms
。 我加入開啓/關閉渲染/刪除行:
BufferedImage drawImage = render(Math.min(1d, lag/TIME_PER_UPDATE));
drawToScreen(drawImage);
是什麼原因造成的線程睡眠時間過長?
這是我第一次在這些論壇發帖,所以請告訴我,如果我在我的文章中做了錯誤的事,或者這是重複的(我還沒有設法找到類似的帖子)。
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;
}
}
}
它可能發生,軟件通常容易出現這樣的錯誤。 http://stackoverflow.com/questions/8240876/thread-sleep-sleeps-for-longer?rq=1。我認爲任何時候承諾只適用於理想的情況。但是,如果我們用一百萬個proc等等阻塞系統,一切都可能磨損 – Coffee
'Thread.Sleep(n)意味着至少在n毫秒內可以發生的次數(或線程量)的數量阻塞當前線程。時間片的長度在不同的版本......和不同的處理器上是不同的,一般範圍從15到30毫秒。這意味着線程幾乎可以保證阻塞超過n毫秒。你的線程在n毫秒後重新喚醒的可能性幾乎是不可能的。因此,Thread.Sleep對於時間安排毫無意義。' - http://stackoverflow.com/a/8241018/763029 – Coffee